scripts: add derive_rpmb_key.py
Adds the script derive_rpmb_key.py that can derive the RPMB key OP-TEE uses
offline or in normal world during a production step.
Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
Reviewed-by: Jerome Forissier <jerome.forissier@linaro.org>
Acked-by: Etienne Carriere <etienne.carriere@foss.st.com>
diff --git a/scripts/derive_rpmb_key.py b/scripts/derive_rpmb_key.py
new file mode 100755
index 0000000..13f9a93
--- /dev/null
+++ b/scripts/derive_rpmb_key.py
@@ -0,0 +1,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()