#!/usr/bin/env python3 # SPDX-License-Identifier: BSD-2-Clause # # Copyright (c) 2015, 2017, 2019, Linaro Limited # import sys algo = {'TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256': 0x70414930, 'TEE_ALG_RSASSA_PKCS1_V1_5_SHA256': 0x70004830} def uuid_parse(s): from uuid import UUID return UUID(s) def int_parse(str): return int(str, 0) def get_args(logger): from argparse import ArgumentParser, RawDescriptionHelpFormatter import textwrap command_base = ['sign-enc', 'digest', 'stitch'] command_aliases_digest = ['generate-digest'] command_aliases_stitch = ['stitch-ta'] command_aliases = command_aliases_digest + command_aliases_stitch command_choices = command_base + command_aliases dat = '[' + ', '.join(command_aliases_digest) + ']' sat = '[' + ', '.join(command_aliases_stitch) + ']' parser = ArgumentParser( description='Sign and encrypt (optional) a Tusted Application for' + ' OP-TEE.', usage='\n %(prog)s command [ arguments ]\n\n' ' command:\n' + ' sign-enc Generate signed and optionally encrypted loadable' + ' TA image file.\n' + ' Takes arguments --uuid, --ta-version, --in, --out,' + ' --key\n' + ' and --enc-key (optional).\n' + ' digest Generate loadable TA binary image digest' + ' for offline\n' + ' signing. Takes arguments --uuid, --ta-version,' + ' --in, --key,\n' ' --enc-key (optional), --algo (optional) and' + ' --dig.\n' + ' stitch Generate loadable signed and encrypted TA binary' + ' image file from\n' + ' TA raw image and its signature. Takes' + ' arguments\n' + ' --uuid, --in, --key, --enc-key (optional), --out,' + ' --algo (optional) and --sig.\n\n' + ' %(prog)s --help show available commands and arguments\n\n', formatter_class=RawDescriptionHelpFormatter, epilog=textwrap.dedent('''\ If no command is given, the script will default to "sign-enc". command aliases: The command \'digest\' can be aliased by ''' + dat + ''' The command \'stitch\' can be aliased by ''' + sat + '\n' + ''' 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\n 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.add_argument( 'command', choices=command_choices, nargs='?', default='sign-enc', help='Command, one of [' + ', '.join(command_base) + ']') parser.add_argument('--uuid', required=True, type=uuid_parse, help='String UUID of the TA') parser.add_argument('--key', required=True, help='Name of signing key file (PEM format)') parser.add_argument('--enc-key', required=False, help='Encryption key string') 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\n' + 'rollback protection of TA install in the secure database.\n' + 'Defaults to 0.') parser.add_argument( '--sig', required=False, dest='sigf', help='Name of signature input file, defaults to .sig') parser.add_argument( '--dig', required=False, dest='digf', help='Name of digest output file, defaults to .dig') parser.add_argument( '--in', required=True, dest='inf', help='Name of application input file, defaults to .stripped.elf') parser.add_argument( '--out', required=False, dest='outf', help='Name of application output file, defaults to .ta') parser.add_argument('--algo', required=False, choices=list(algo.keys()), default='TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256', help='The hash and signature algorithm, ' + 'defaults to TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256. ' + 'Allowed values are: ' + ', '.join(list(algo.keys())), metavar='') parsed = parser.parse_args() # Check parameter combinations if parsed.digf is None and \ parsed.outf is not None and \ parsed.command in ['digest'] + command_aliases_digest: logger.error('A digest was requested, but argument --out was given.' + ' Did you mean:\n ' + parser.prog+' --dig ' + parsed.outf + ' ...') sys.exit(1) if parsed.digf is not None \ and parsed.outf is not None \ and parsed.command in ['digest'] + command_aliases_digest: logger.warn('A digest was requested, but arguments --dig and ' + '--out were given.\n' + ' --out will be ignored.') # Set defaults for optional arguments. if parsed.sigf is None: parsed.sigf = str(parsed.uuid)+'.sig' if parsed.digf is None: parsed.digf = str(parsed.uuid)+'.dig' if parsed.inf is None: parsed.inf = str(parsed.uuid)+'.stripped.elf' if parsed.outf is None: parsed.outf = str(parsed.uuid)+'.ta' return parsed def main(): try: from Cryptodome.Signature import pss from Cryptodome.Signature import pkcs1_15 from Cryptodome.Hash import SHA256 from Cryptodome.PublicKey import RSA except ImportError: from Crypto.Signature import pss from Crypto.Signature import pkcs1_15 from Crypto.Hash import SHA256 from Crypto.PublicKey import RSA import base64 import logging import os import struct logging.basicConfig() logger = logging.getLogger(os.path.basename(__file__)) args = get_args(logger) with open(args.key, 'rb') as f: key = RSA.importKey(f.read()) with open(args.inf, 'rb') as f: img = f.read() h = SHA256.new() digest_len = h.digest_size sig_len = key.size_in_bytes() img_size = len(img) hdr_version = args.ta_version # struct shdr_bootstrap_ta::ta_version magic = 0x4f545348 # SHDR_MAGIC if args.enc_key: img_type = 2 # SHDR_ENCRYPTED_TA else: img_type = 1 # SHDR_BOOTSTRAP_TA shdr = struct.pack('