1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
|
#!/usr/bin/env python3
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2023, Linaro Limited
#
import sys
def hex_parse(str):
try:
h = bytes.fromhex(str)
except ValueError as e:
try:
# Try to pad with a '0' nibble in front
h = bytes.fromhex('0' + str)
print('Odd number of nibbles in hexadecimal string',
file=sys.stderr)
raise e
except ValueError:
raise e
return h
def get_args():
import argparse
import textwrap
parser = argparse.ArgumentParser(
allow_abbrev=False,
description='''Derive an RPMB key from the Hardware Unique Key used
by OP-TEE and the CID of the RPMB.''',
epilog='''Note that the derived key matches what the
__huk_subkey_derive() would produce. If huk_subkey_derive()
is overridden to call another function, please don't use
this script''')
parser.add_argument('--quiet', action='store_true', default=False,
help='''Gives only the hexstring of the RPMB key as
output, intended for scripting''')
parser.add_argument('--testkey', action='store_true', default=False,
help='''Outputs the hardcoded test key''')
parser.add_argument('--huk', type=hex_parse,
help='''Hardware Unique Key (16 bytes), as returned
by the platform specific function
tee_otp_get_hw_unique_key() in OP-TEE''')
parser.add_argument('--cid', type=hex_parse, help='CID (16 bytes)')
parser.add_argument('--compat', action='store_true', default=False,
help='''Generates a backwards compatible key,
only to be used if OP-TEE is build with
CFG_CORE_HUK_SUBKEY_COMPAT=y''')
return parser.parse_args()
def derive_key(huk, cid, compat):
import struct
from cryptography.hazmat.primitives import hashes, hmac
# Prepare the CID and Clear the PRV (Product revision) and CRC (CRC7
# checksum) fields as OP-TEE does.
data = bytearray(cid)
data[9] = 0
data[15] = 0
# This is how __huk_subkey_derive() is implemented, if huk_subkey_derive()
# is overridden the key derived here may not match what OP-TEE is using
#
# HUK is as tee_otp_get_hw_unique_key() in OP-TEE returns it
h = hmac.HMAC(huk, hashes.SHA256())
if not compat:
usage_word = struct.pack('<I', 0)
h.update(usage_word)
h.update(data)
return h.finalize()
def main():
args = get_args()
if args.testkey:
if args.cid or args.huk or args.compat:
print('--cid, --huk, or --compat '
'cannot be given together with --testkey')
sys.exit(1)
# The test key hardcoded in OP-TEE
key = bytes.fromhex('''D3 EB 3E C3 6E 33 4C 9F
98 8C E2 C0 B8 59 54 61
0D 2B CF 86 64 84 4D F2
AB 56 E6 C6 1B B7 01 E4''')
else:
if not args.cid:
print('--cid is required without --testkey')
sys.exit(1)
if not args.huk:
print('--huk is required without --testkey')
sys.exit(1)
if len(args.cid) != 16:
print(f'Invalid CID length, expected 16 bytes got {len(args.cid)}',
file=sys.stderr)
sys.exit(1)
if len(args.huk) != 16:
print(f'Invalid HUK length, expected 16 bytes got {len(args.huk)}',
file=sys.stderr)
sys.exit(1)
if not args.quiet:
print(f'HUK: {args.huk.hex()} length {len(args.huk)}')
print(f'RPMB CID: {args.cid.hex()} length {len(args.cid)}')
key = derive_key(args.huk, args.cid, args.compat)
if args.quiet:
print(key.hex())
else:
print(f'RPMB key: {key.hex()}')
print(f' length {len(key)}')
if args.testkey:
print('''
*********************************************************************
*** Please note that the test key should only be used for testing ***
*** purposes since it's well known and the same for all devices. ***
*********************************************************************''')
else:
print('''
Please take care to double-check the provided input since writing the RPMB
key is an irreversible step.''')
if __name__ == "__main__":
main()
|