#!/usr/bin/env python3 # SPDX-License-Identifier: BSD-2-Clause # # Copyright (c) 2015, 2017, 2019, Linaro Limited # import sys import math sig_tee_alg = {'TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256': 0x70414930, 'TEE_ALG_RSASSA_PKCS1_V1_5_SHA256': 0x70004830} enc_tee_alg = {'TEE_ALG_AES_GCM': 0x40000810} enc_key_type = {'SHDR_ENC_KEY_DEV_SPECIFIC': 0x0, 'SHDR_ENC_KEY_CLASS_WIDE': 0x1} SHDR_BOOTSTRAP_TA = 1 SHDR_ENCRYPTED_TA = 2 SHDR_MAGIC = 0x4f545348 SHDR_SIZE = 20 EHDR_SIZE = 12 UUID_SIZE = 16 # Use 12 bytes for nonce per recommendation NONCE_SIZE = 12 TAG_SIZE = 16 def value_to_key(db, val): for k, v in db.items(): if v == val: return k def uuid_parse(s): from uuid import UUID return UUID(s) def int_parse(str): return int(str, 0) def get_args(): def arg_add_uuid(parser): parser.add_argument( '--uuid', required=True, type=uuid_parse, help='String UUID of the TA') def arg_add_key(parser): parser.add_argument( '--key', required=True, help=''' Name of signing and verification key file (PEM format) or an Amazon Resource Name (arn:) of an AWS KMS asymmetric key. At least public key for the commands digest, stitch, and verify, else a private key''') def arg_add_enc_key(parser): parser.add_argument( '--enc-key', required=False, help='Encryption key string') def arg_add_enc_key_type(parser): parser.add_argument( '--enc-key-type', required=False, default='SHDR_ENC_KEY_DEV_SPECIFIC', choices=list(enc_key_type.keys()), help=''' Encryption key type, Defaults to SHDR_ENC_KEY_DEV_SPECIFIC.''') def arg_add_ta_version(parser): parser.add_argument( '--ta-version', required=False, type=int_parse, default=0, help=''' TA version stored as a 32-bit unsigned integer and used for rollback protection of TA install in the secure database. Defaults to 0.''') def arg_add_sig(parser): parser.add_argument( '--sig', required=True, dest='sigf', help='Name of signature input file, defaults to .sig') def arg_add_dig(parser): parser.add_argument( '--dig', required=True, dest='digf', help='Name of digest output file, defaults to .dig') def arg_add_in(parser): parser.add_argument( '--in', required=False, dest='inf', help=''' Name of application input file, defaults to .stripped.elf''') def arg_add_out(parser): parser.add_argument( '--out', required=True, dest='outf', help='Name of application output file, defaults to .ta') def arg_add_algo(parser): parser.add_argument( '--algo', required=False, choices=list(sig_tee_alg.keys()), default='TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256', help=''' The hash and signature algorithm. Defaults to TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256.''') def get_outf_default(parsed): return str(parsed.uuid) + '.ta' def get_inf_default(parsed): return str(parsed.uuid) + '.stripped.elf' def get_sigf_default(parsed): return str(parsed.uuid) + '.sig' def get_digf_default(parsed): return str(parsed.uuid) + '.dig' def assign_default_value(parsed, attr, func): if hasattr(parsed, attr) and getattr(parsed, attr) is None: setattr(parsed, attr, func(parsed)) import argparse import textwrap parser = argparse.ArgumentParser( description='Sign and encrypt (optional) a Trusted Application ' + ' for OP-TEE.', usage='%(prog)s ...', epilog=' -h for detailed help') subparsers = parser.add_subparsers( title='valid commands, with possible aliases in ()', dest='command', metavar='') parser_sign_enc = subparsers.add_parser( 'sign-enc', prog=parser.prog + ' sign-enc', help='Generate signed and optionally encrypted loadable TA image file') parser_sign_enc.set_defaults(func=command_sign_enc) arg_add_uuid(parser_sign_enc) arg_add_ta_version(parser_sign_enc) arg_add_in(parser_sign_enc) arg_add_out(parser_sign_enc) arg_add_key(parser_sign_enc) arg_add_enc_key(parser_sign_enc) arg_add_enc_key_type(parser_sign_enc) arg_add_algo(parser_sign_enc) parser_digest = subparsers.add_parser( 'digest', aliases=['generate-digest'], prog=parser.prog + ' digest', formatter_class=argparse.RawDescriptionHelpFormatter, help='Generate loadable TA binary image digest for offline signing', epilog=textwrap.dedent('''\ example offline signing command using OpenSSL for algorithm TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256: base64 -d .dig | \\ openssl pkeyutl -sign -inkey .pem \\ -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pss \\ -pkeyopt rsa_pss_saltlen:digest \\ -pkeyopt rsa_mgf1_md:sha256 | \\ base64 > .sig example offline signing command using OpenSSL for algorithm TEE_ALG_RSASSA_PKCS1_V1_5_SHA256: base64 -d .dig | \\ openssl pkeyutl -sign -inkey .pem \\ -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pkcs1 | \\ base64 > .sig ''')) parser_digest.set_defaults(func=command_digest) arg_add_uuid(parser_digest) arg_add_ta_version(parser_digest) arg_add_in(parser_digest) arg_add_key(parser_digest) arg_add_enc_key(parser_digest) arg_add_enc_key_type(parser_digest) arg_add_algo(parser_digest) arg_add_dig(parser_digest) parser_stitch = subparsers.add_parser( 'stitch', aliases=['stitch-ta'], prog=parser.prog + ' stich', help='Generate loadable signed and encrypted TA binary image file' + ' from TA raw image and its signature') parser_stitch.set_defaults(func=command_stitch) arg_add_uuid(parser_stitch) arg_add_ta_version(parser_stitch) arg_add_in(parser_stitch) arg_add_key(parser_stitch) arg_add_out(parser_stitch) arg_add_enc_key(parser_stitch) arg_add_enc_key_type(parser_stitch) arg_add_algo(parser_stitch) arg_add_sig(parser_stitch) parser_verify = subparsers.add_parser( 'verify', prog=parser.prog + ' verify', help='Verify signed TA binary') parser_verify.set_defaults(func=command_verify) arg_add_uuid(parser_verify) arg_add_in(parser_verify) arg_add_key(parser_verify) parser_display = subparsers.add_parser( 'display', prog=parser.prog + ' display', help='Parses and displays a signed TA binary') parser_display.set_defaults(func=command_display) arg_add_in(parser_display) argv = sys.argv[1:] if (len(argv) > 0 and argv[0][0] == '-' and argv[0] != '-h' and argv[0] != '--help'): # The default sub-command is 'sign-enc' so add it to the parser # if one is missing argv = ['sign-enc'] + argv parsed = parser.parse_args(argv) if parsed.command is None: parser.print_help() sys.exit(1) # Set a few defaults if defined for the current command assign_default_value(parsed, 'inf', get_inf_default) assign_default_value(parsed, 'outf', get_outf_default) assign_default_value(parsed, 'sigf', get_sigf_default) assign_default_value(parsed, 'digf', get_digf_default) return parsed def load_asymmetric_key(arg_key): if arg_key.startswith('arn:'): from sign_helper_kms import _RSAPrivateKeyInKMS key = _RSAPrivateKeyInKMS(arg_key) else: from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.serialization import ( load_pem_private_key, load_pem_public_key) with open(arg_key, 'rb') as f: data = f.read() try: key = load_pem_private_key(data, password=None, backend=default_backend()) except ValueError: key = load_pem_public_key(data, backend=default_backend()) return key class BinaryImage: def __init__(self, arg_inf, arg_key): from cryptography.hazmat.primitives import hashes # Exactly what inf is holding isn't determined a this stage with open(arg_inf, 'rb') as f: self.inf = f.read() if arg_key is None: self.key = None else: self.key = load_asymmetric_key(arg_key) self.sig_len = math.ceil(self.key.key_size / 8) self.chosen_hash = hashes.SHA256() self.digest_len = self.chosen_hash.digest_size def __pack_img(self, img_type, sign_algo): import struct self.sig_algo = sign_algo self.img_type = img_type self.shdr = struct.pack('