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