1#!/usr/bin/env python3 2# SPDX-License-Identifier: BSD-2-Clause 3# 4# Copyright (c) 2015, 2017, 2019, Linaro Limited 5# 6 7import sys 8import math 9 10 11sig_tee_alg = {'TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256': 0x70414930, 12 'TEE_ALG_RSASSA_PKCS1_V1_5_SHA256': 0x70004830} 13 14enc_tee_alg = {'TEE_ALG_AES_GCM': 0x40000810} 15 16enc_key_type = {'SHDR_ENC_KEY_DEV_SPECIFIC': 0x0, 17 'SHDR_ENC_KEY_CLASS_WIDE': 0x1} 18 19TEE_ATTR_RSA_MODULUS = 0xD0000130 20TEE_ATTR_RSA_PUBLIC_EXPONENT = 0xD0000230 21 22SHDR_BOOTSTRAP_TA = 1 23SHDR_ENCRYPTED_TA = 2 24SHDR_SUBKEY = 3 25SHDR_MAGIC = 0x4f545348 26SHDR_SIZE = 20 27SK_HDR_SIZE = 20 28EHDR_SIZE = 12 29UUID_SIZE = 16 30# Use 12 bytes for nonce per recommendation 31NONCE_SIZE = 12 32TAG_SIZE = 16 33 34 35def value_to_key(db, val): 36 for k, v in db.items(): 37 if v == val: 38 return k 39 40 41def uuid_v5_sha512(namespace_bytes, name): 42 from cryptography.hazmat.primitives import hashes 43 from uuid import UUID 44 45 h = hashes.Hash(hashes.SHA512()) 46 h.update(namespace_bytes + bytes(name, 'utf-8')) 47 digest = h.finalize() 48 return UUID(bytes=digest[:16], version=5) 49 50 51def name_img_to_str(name_img): 52 return name_img.decode().split('\x00', 1)[0] 53 54 55def uuid_parse(s): 56 from uuid import UUID 57 return UUID(s) 58 59 60def int_parse(str): 61 return int(str, 0) 62 63 64def get_args(): 65 import argparse 66 import textwrap 67 68 class OnlyOne(argparse.Action): 69 def __call__(self, parser, namespace, values, option_string=None): 70 a = self.dest + '_assigned' 71 if getattr(namespace, a, False): 72 raise argparse.ArgumentError(self, 'Can only be given once') 73 setattr(namespace, a, True) 74 setattr(namespace, self.dest, values) 75 76 def arg_add_uuid(parser): 77 parser.add_argument( 78 '--uuid', required=True, type=uuid_parse, 79 help='String UUID of the TA') 80 81 def arg_add_key(parser): 82 parser.add_argument( 83 '--key', required=True, help=''' 84 Name of signing and verification key file (PEM format) or an 85 Amazon Resource Name (arn:) of an AWS KMS asymmetric key. 86 At least public key for the commands digest, stitch, and 87 verify, else a private key''') 88 89 def arg_add_enc_key(parser): 90 parser.add_argument( 91 '--enc-key', required=False, help='Encryption key string') 92 93 def arg_add_enc_key_type(parser): 94 parser.add_argument( 95 '--enc-key-type', required=False, 96 default='SHDR_ENC_KEY_DEV_SPECIFIC', 97 choices=list(enc_key_type.keys()), help=''' 98 Encryption key type, 99 Defaults to SHDR_ENC_KEY_DEV_SPECIFIC.''') 100 101 def arg_add_ta_version(parser): 102 parser.add_argument( 103 '--ta-version', required=False, type=int_parse, default=0, help=''' 104 TA version stored as a 32-bit unsigned integer and used for 105 rollback protection of TA install in the secure database. 106 Defaults to 0.''') 107 108 def arg_add_sig(parser): 109 parser.add_argument( 110 '--sig', required=True, dest='sigf', 111 help='Name of signature input file, defaults to <UUID>.sig') 112 113 def arg_add_dig(parser): 114 parser.add_argument( 115 '--dig', required=True, dest='digf', 116 help='Name of digest output file, defaults to <UUID>.dig') 117 118 def arg_add_in(parser): 119 parser.add_argument( 120 '--in', required=False, dest='inf', help=''' 121 Name of application input file, defaults to 122 <UUID>.stripped.elf''') 123 124 def arg_add_out(parser): 125 parser.add_argument( 126 '--out', required=True, dest='outf', 127 help='Name of application output file, defaults to <UUID>.ta') 128 129 def arg_add_algo(parser): 130 parser.add_argument( 131 '--algo', required=False, choices=list(sig_tee_alg.keys()), 132 default='TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256', help=''' 133 The hash and signature algorithm. 134 Defaults to TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256.''') 135 136 def arg_add_subkey(parser): 137 parser.add_argument( 138 '--subkey', action=OnlyOne, help='Name of subkey input file') 139 140 def arg_add_name(parser): 141 parser.add_argument('--name', 142 help='Input name for subspace of a subkey') 143 144 def arg_add_subkey_uuid_in(parser): 145 parser.add_argument( 146 '--in', required=True, dest='inf', 147 help='Name of subkey input file') 148 149 def arg_add_max_depth(parser): 150 parser.add_argument( 151 '--max-depth', required=False, type=int_parse, help=''' 152 Max depth of subkeys below this subkey''') 153 154 def arg_add_name_size(parser): 155 parser.add_argument( 156 '--name-size', required=True, type=int_parse, help=''' 157 Size of (unsigned) input name for subspace of a subkey. 158 Set to 0 to create an identity subkey (a subkey having 159 the same UUID as the next subkey or TA)''') 160 161 def arg_add_subkey_version(parser): 162 parser.add_argument( 163 '--subkey-version', required=False, type=int_parse, default=0, 164 help='Subkey version used for rollback protection') 165 166 def arg_add_subkey_in(parser): 167 parser.add_argument( 168 '--in', required=True, dest='inf', help=''' 169 Name of PEM file with the public key of the new subkey''') 170 171 def arg_add_subkey_out(parser): 172 parser.add_argument( 173 '--out', required=True, dest='outf', 174 help='Name of subkey output file') 175 176 def get_outf_default(parsed): 177 return str(parsed.uuid) + '.ta' 178 179 def get_inf_default(parsed): 180 return str(parsed.uuid) + '.stripped.elf' 181 182 def get_sigf_default(parsed): 183 return str(parsed.uuid) + '.sig' 184 185 def get_digf_default(parsed): 186 return str(parsed.uuid) + '.dig' 187 188 def assign_default_value(parsed, attr, func): 189 if hasattr(parsed, attr) and getattr(parsed, attr) is None: 190 setattr(parsed, attr, func(parsed)) 191 192 parser = argparse.ArgumentParser( 193 description='Sign and encrypt (optional) a Trusted Application ' + 194 ' for OP-TEE.', 195 usage='%(prog)s <command> ...', 196 epilog='<command> -h for detailed help') 197 subparsers = parser.add_subparsers( 198 title='valid commands, with possible aliases in ()', 199 dest='command', metavar='') 200 201 parser_sign_enc = subparsers.add_parser( 202 'sign-enc', prog=parser.prog + ' sign-enc', 203 help='Generate signed and optionally encrypted loadable TA image file') 204 parser_sign_enc.set_defaults(func=command_sign_enc) 205 arg_add_uuid(parser_sign_enc) 206 arg_add_ta_version(parser_sign_enc) 207 arg_add_in(parser_sign_enc) 208 arg_add_out(parser_sign_enc) 209 arg_add_key(parser_sign_enc) 210 arg_add_subkey(parser_sign_enc) 211 arg_add_name(parser_sign_enc) 212 arg_add_enc_key(parser_sign_enc) 213 arg_add_enc_key_type(parser_sign_enc) 214 arg_add_algo(parser_sign_enc) 215 216 parser_digest = subparsers.add_parser( 217 'digest', aliases=['generate-digest'], prog=parser.prog + ' digest', 218 formatter_class=argparse.RawDescriptionHelpFormatter, 219 help='Generate loadable TA binary image digest for offline signing', 220 epilog=textwrap.dedent('''\ 221 example offline signing command using OpenSSL for algorithm 222 TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256: 223 base64 -d <UUID>.dig | \\ 224 openssl pkeyutl -sign -inkey <KEYFILE>.pem \\ 225 -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pss \\ 226 -pkeyopt rsa_pss_saltlen:digest \\ 227 -pkeyopt rsa_mgf1_md:sha256 | \\ 228 base64 > <UUID>.sig 229 230 example offline signing command using OpenSSL for algorithm 231 TEE_ALG_RSASSA_PKCS1_V1_5_SHA256: 232 base64 -d <UUID>.dig | \\ 233 openssl pkeyutl -sign -inkey <KEYFILE>.pem \\ 234 -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pkcs1 | \\ 235 base64 > <UUID>.sig 236 ''')) 237 parser_digest.set_defaults(func=command_digest) 238 arg_add_uuid(parser_digest) 239 arg_add_ta_version(parser_digest) 240 arg_add_in(parser_digest) 241 arg_add_key(parser_digest) 242 arg_add_enc_key(parser_digest) 243 arg_add_enc_key_type(parser_digest) 244 arg_add_algo(parser_digest) 245 arg_add_dig(parser_digest) 246 247 parser_stitch = subparsers.add_parser( 248 'stitch', aliases=['stitch-ta'], prog=parser.prog + ' stitch', 249 help='Generate loadable signed and encrypted TA binary image file' + 250 ' from TA raw image and its signature') 251 parser_stitch.set_defaults(func=command_stitch) 252 arg_add_uuid(parser_stitch) 253 arg_add_ta_version(parser_stitch) 254 arg_add_in(parser_stitch) 255 arg_add_key(parser_stitch) 256 arg_add_out(parser_stitch) 257 arg_add_enc_key(parser_stitch) 258 arg_add_enc_key_type(parser_stitch) 259 arg_add_algo(parser_stitch) 260 arg_add_sig(parser_stitch) 261 262 parser_verify = subparsers.add_parser( 263 'verify', prog=parser.prog + ' verify', 264 help='Verify signed TA binary') 265 parser_verify.set_defaults(func=command_verify) 266 arg_add_uuid(parser_verify) 267 arg_add_in(parser_verify) 268 arg_add_key(parser_verify) 269 270 parser_display = subparsers.add_parser( 271 'display', prog=parser.prog + ' display', 272 help='Parses and displays a signed TA binary') 273 parser_display.set_defaults(func=command_display) 274 arg_add_in(parser_display) 275 276 parser_subkey_uuid = subparsers.add_parser( 277 'subkey-uuid', prog=parser.prog + ' subkey-uuid', 278 help='calculate the UUID of next TA or subkey') 279 parser_subkey_uuid.set_defaults(func=command_subkey_uuid) 280 arg_add_subkey_uuid_in(parser_subkey_uuid) 281 arg_add_name(parser_subkey_uuid) 282 283 parser_sign_subkey = subparsers.add_parser( 284 'sign-subkey', prog=parser.prog + ' sign-subkey', 285 help='Sign a subkey') 286 parser_sign_subkey.set_defaults(func=command_sign_subkey) 287 arg_add_name(parser_sign_subkey) 288 arg_add_subkey_in(parser_sign_subkey) 289 arg_add_uuid(parser_sign_subkey) 290 arg_add_key(parser_sign_subkey) 291 arg_add_subkey_out(parser_sign_subkey) 292 arg_add_max_depth(parser_sign_subkey) 293 arg_add_name_size(parser_sign_subkey) 294 arg_add_subkey(parser_sign_subkey) 295 arg_add_subkey_version(parser_sign_subkey) 296 arg_add_algo(parser_sign_subkey) 297 298 argv = sys.argv[1:] 299 if (len(argv) > 0 and argv[0][0] == '-' and 300 argv[0] != '-h' and argv[0] != '--help'): 301 # The default sub-command is 'sign-enc' so add it to the parser 302 # if one is missing 303 argv = ['sign-enc'] + argv 304 305 parsed = parser.parse_args(argv) 306 307 if parsed.command is None: 308 parser.print_help() 309 sys.exit(1) 310 311 # Set a few defaults if defined for the current command 312 assign_default_value(parsed, 'inf', get_inf_default) 313 assign_default_value(parsed, 'outf', get_outf_default) 314 assign_default_value(parsed, 'sigf', get_sigf_default) 315 assign_default_value(parsed, 'digf', get_digf_default) 316 317 return parsed 318 319 320def load_asymmetric_key_img(data): 321 from cryptography.hazmat.backends import default_backend 322 from cryptography.hazmat.primitives.serialization import ( 323 load_pem_private_key, load_pem_public_key) 324 325 try: 326 return load_pem_private_key(data, password=None, 327 backend=default_backend()) 328 except ValueError: 329 return load_pem_public_key(data, backend=default_backend()) 330 331 332def load_asymmetric_key(arg_key): 333 if arg_key.startswith('arn:'): 334 from sign_helper_kms import _RSAPrivateKeyInKMS 335 return _RSAPrivateKeyInKMS(arg_key) 336 else: 337 with open(arg_key, 'rb') as f: 338 return load_asymmetric_key_img(f.read()) 339 340 341class BinaryImage: 342 def __init__(self, arg_inf, arg_key): 343 from cryptography.hazmat.primitives import hashes 344 345 # Exactly what inf is holding isn't determined a this stage 346 if isinstance(arg_inf, str): 347 with open(arg_inf, 'rb') as f: 348 self.inf = f.read() 349 else: 350 self.inf = arg_inf 351 352 if arg_key is None: 353 self.key = None 354 else: 355 if isinstance(arg_key, str): 356 self.key = load_asymmetric_key(arg_key) 357 else: 358 self.key = arg_key 359 self.sig_size = math.ceil(self.key.key_size / 8) 360 361 self.chosen_hash = hashes.SHA256() 362 self.hash_size = self.chosen_hash.digest_size 363 364 def __pack_img(self, img_type, sign_algo): 365 import struct 366 367 self.sig_algo = sign_algo 368 self.img_type = img_type 369 self.shdr = struct.pack('<IIIIHH', SHDR_MAGIC, img_type, len(self.img), 370 sig_tee_alg[sign_algo], self.hash_size, 371 self.sig_size) 372 373 def __calc_digest(self): 374 from cryptography.hazmat.backends import default_backend 375 from cryptography.hazmat.primitives import hashes 376 377 h = hashes.Hash(self.chosen_hash, default_backend()) 378 h.update(self.shdr) 379 if hasattr(self, 'ta_uuid'): 380 h.update(self.ta_uuid) 381 h.update(self.ta_version) 382 if hasattr(self, 'ehdr'): 383 h.update(self.ehdr) 384 h.update(self.nonce) 385 h.update(self.tag) 386 h.update(self.img) 387 return h.finalize() 388 389 def encrypt_ta(self, enc_key, key_type, sig_algo, uuid, ta_version): 390 from cryptography.hazmat.primitives.ciphers.aead import AESGCM 391 import struct 392 import os 393 394 self.img = self.inf 395 396 cipher = AESGCM(bytes.fromhex(enc_key)) 397 self.nonce = os.urandom(NONCE_SIZE) 398 out = cipher.encrypt(self.nonce, self.img, None) 399 self.ciphertext = out[:-TAG_SIZE] 400 # Authentication Tag is always the last bytes 401 self.tag = out[-TAG_SIZE:] 402 403 enc_algo = enc_tee_alg['TEE_ALG_AES_GCM'] 404 flags = enc_key_type[key_type] 405 self.ehdr = struct.pack('<IIHH', enc_algo, flags, len(self.nonce), 406 len(self.tag)) 407 408 self.__pack_img(SHDR_ENCRYPTED_TA, sig_algo) 409 self.ta_uuid = uuid.bytes 410 self.ta_version = struct.pack('<I', ta_version) 411 self.img_digest = self.__calc_digest() 412 413 def set_bootstrap_ta(self, sig_algo, uuid, ta_version): 414 import struct 415 416 self.img = self.inf 417 self.__pack_img(SHDR_BOOTSTRAP_TA, sig_algo) 418 self.ta_uuid = uuid.bytes 419 self.ta_version = struct.pack('<I', ta_version) 420 self.img_digest = self.__calc_digest() 421 422 def set_subkey(self, sign_algo, name, uuid, subkey_version, max_depth, 423 name_size): 424 from cryptography.hazmat.primitives.asymmetric import rsa 425 import struct 426 427 self.subkey_name = name 428 429 subkey_key = load_asymmetric_key_img(self.inf) 430 if isinstance(subkey_key, rsa.RSAPrivateKey): 431 subkey_pkey = subkey_key.public_key() 432 else: 433 subkey_pkey = subkey_key 434 435 if max_depth is None: 436 if hasattr(self, 'previous_max_depth'): 437 if self.previous_max_depth <= 0: 438 logger.error('Max depth of previous subkey is {}, ' 439 .format(self.previous_max_depth) + 440 'cannot use a smaller value') 441 sys.exit(1) 442 443 max_depth = self.previous_max_depth - 1 444 else: 445 max_depth = 0 446 else: 447 if (hasattr(self, 'previous_max_depth') and 448 max_depth >= getattr(self, 'previous_max_depth')): 449 logger.error('Max depth of previous subkey is {} ' 450 .format(self.previous_max_depth) + 451 'and the next value must be smaller') 452 sys.exit(1) 453 454 def int_to_bytes(x: int) -> bytes: 455 return x.to_bytes((x.bit_length() + 8) // 8, 'big') 456 457 n_bytes = int_to_bytes(subkey_pkey.public_numbers().n) 458 e_bytes = int_to_bytes(subkey_pkey.public_numbers().e) 459 attrs_end_offs = 16 + 5 * 4 + 2 * 3 * 4 460 shdr_subkey = struct.pack('<IIIIIIIIIII', 461 name_size, subkey_version, 462 max_depth, sig_tee_alg[sign_algo], 2, 463 TEE_ATTR_RSA_MODULUS, 464 attrs_end_offs, len(n_bytes), 465 TEE_ATTR_RSA_PUBLIC_EXPONENT, 466 attrs_end_offs + len(n_bytes), 467 len(e_bytes)) 468 self.img = uuid.bytes + shdr_subkey + n_bytes + e_bytes 469 self.__pack_img(SHDR_SUBKEY, sign_algo) 470 self.img_digest = self.__calc_digest() 471 472 def parse(self): 473 from cryptography.hazmat.primitives.asymmetric import rsa 474 import struct 475 476 offs = 0 477 self.shdr = self.inf[offs:offs + SHDR_SIZE] 478 [magic, img_type, img_size, algo_value, hash_size, 479 sig_size] = struct.unpack('<IIIIHH', self.shdr) 480 offs += SHDR_SIZE 481 482 if magic != SHDR_MAGIC: 483 raise Exception("Unexpected magic: 0x{:08x}".format(magic)) 484 485 if algo_value not in sig_tee_alg.values(): 486 raise Exception('Unrecognized algorithm: 0x{:08x}' 487 .format(algo_value)) 488 self.sig_algo = value_to_key(sig_tee_alg, algo_value) 489 490 if hash_size != self.hash_size: 491 raise Exception("Unexpected digest len: {}".format(hash_size)) 492 493 self.img_digest = self.inf[offs:offs + hash_size] 494 offs += hash_size 495 self.sig = self.inf[offs:offs + sig_size] 496 offs += sig_size 497 498 if img_type == SHDR_BOOTSTRAP_TA or img_type == SHDR_ENCRYPTED_TA: 499 self.ta_uuid = self.inf[offs:offs + UUID_SIZE] 500 offs += UUID_SIZE 501 self.ta_version = self.inf[offs:offs + 4] 502 offs += 4 503 if img_type == SHDR_ENCRYPTED_TA: 504 self.ehdr = self.inf[offs: offs + EHDR_SIZE] 505 offs += EHDR_SIZE 506 [enc_algo, flags, nonce_len, 507 tag_len] = struct.unpack('<IIHH', self.ehdr) 508 if enc_value not in enc_tee_alg.values(): 509 raise Exception('Unrecognized encrypt algorithm: 0x{:08x}' 510 .format(enc_value)) 511 if nonce_len != 12: 512 raise Exception("Unexpected nonce len: {}" 513 .format(nonce_len)) 514 self.nonce = self.inf[offs:offs + nonce_len] 515 offs += nonce_len 516 517 if tag_len != 16: 518 raise Exception("Unexpected tag len: {}".format(tag_len)) 519 self.tag = self.inf[-tag_len:] 520 self.ciphertext = self.inf[offs:-tag_len] 521 if len(self.ciphertext) != img_size: 522 raise Exception("Unexpected ciphertext size: ", 523 "got {}, expected {}" 524 .format(len(self.ciphertext), img_size)) 525 self.img = self.ciphertext 526 else: 527 self.img = self.inf[offs:] 528 if len(self.img) != img_size: 529 raise Exception("Unexpected img size: got {}, expected {}" 530 .format(len(self.img), img_size)) 531 elif img_type == SHDR_SUBKEY: 532 subkey_offs = offs 533 self.uuid = self.inf[offs:offs + UUID_SIZE] 534 offs += UUID_SIZE 535 self.subkey_hdr = self.inf[offs:offs + SK_HDR_SIZE] 536 [self.name_size, self.subkey_version, self.max_depth, self.algo, 537 self.attr_count] = struct.unpack('<IIIII', self.subkey_hdr) 538 offs += len(self.subkey_hdr) 539 self.attr = self.inf[offs:offs + img_size - 540 UUID_SIZE - len(self.subkey_hdr)] 541 offs += len(self.attr) 542 self.name_img = self.inf[offs:offs + self.name_size] 543 offs += self.name_size 544 self.next_inf = self.inf[offs:] 545 546 def find_attr(attr): 547 if self.attr_count <= 0: 548 return None 549 for n in range(self.attr_count): 550 o = subkey_offs + UUID_SIZE + SK_HDR_SIZE + n * 12 551 [attr_value, attr_offs, 552 attr_len] = struct.unpack('<III', self.inf[o: o + 12]) 553 if attr_value == attr: 554 o = subkey_offs + attr_offs 555 return self.inf[o:o + attr_len] 556 return None 557 558 n_bytes = find_attr(TEE_ATTR_RSA_MODULUS) 559 e_bytes = find_attr(TEE_ATTR_RSA_PUBLIC_EXPONENT) 560 e = int.from_bytes(e_bytes, 'big') 561 n = int.from_bytes(n_bytes, 'big') 562 self.subkey_key = rsa.RSAPublicNumbers(e, n).public_key() 563 564 self.img = self.inf[subkey_offs:offs - self.name_size] 565 if len(self.img) != img_size: 566 raise Exception("Unexpected img size: got {}, expected {}" 567 .format(len(self.img), img_size)) 568 else: 569 raise Exception("Unsupported image type: {}".format(img_type)) 570 571 def display(self): 572 import binascii 573 import struct 574 import uuid 575 576 def display_ta(): 577 nonlocal offs 578 ta_uuid = self.inf[offs:offs + UUID_SIZE] 579 print(' struct shdr_bootstrap_ta') 580 print(' uuid: {}'.format(uuid.UUID(bytes=ta_uuid))) 581 offs += UUID_SIZE 582 [ta_version] = struct.unpack('<I', self.inf[offs:offs + 4]) 583 print(' ta_version: {}'.format(ta_version)) 584 585 offs += 4 586 if img_type == SHDR_ENCRYPTED_TA: 587 ehdr = self.inf[offs: offs + EHDR_SIZE] 588 offs += EHDR_SIZE 589 [enc_algo, flags, nonce_len, 590 tag_len] = struct.unpack('<IIHH', ehdr) 591 592 print(' struct shdr_encrypted_ta') 593 enc_algo_name = 'Unkown' 594 if enc_algo in enc_tee_alg.values(): 595 enc_algo_name = value_to_key(enc_tee_alg, enc_algo) 596 print(' enc_algo: 0x{:08x} ({})' 597 .format(enc_algo, enc_algo_name)) 598 599 if enc_algo not in enc_tee_alg.values(): 600 raise Exception('Unrecognized encrypt algorithm: 0x{:08x}' 601 .format(enc_algo)) 602 603 flags_name = 'Unkown' 604 if flags in enc_key_type.values(): 605 flags_name = value_to_key(enc_key_type, flags) 606 print(' flags: 0x{:x} ({})'.format(flags, flags_name)) 607 608 print(' iv_size: {} (bytes)'.format(nonce_len)) 609 if nonce_len != NONCE_SIZE: 610 raise Exception("Unexpected nonce len: {}" 611 .format(nonce_len)) 612 nonce = self.inf[offs:offs + nonce_len] 613 print(' iv: {}' 614 .format(binascii.hexlify(nonce).decode('ascii'))) 615 offs += nonce_len 616 617 print(' tag_size: {} (bytes)'.format(tag_len)) 618 if tag_len != TAG_SIZE: 619 raise Exception("Unexpected tag len: {}".format(tag_len)) 620 tag = self.inf[offs:offs+tag_len] 621 print(' tag: {}' 622 .format(binascii.hexlify(tag).decode('ascii'))) 623 offs += tag_len 624 625 ciphertext = self.inf[offs:] 626 print(' TA offset: {} (0x{:x}) bytes'.format(offs, offs)) 627 print(' TA size: {} (0x{:x}) bytes' 628 .format(len(ciphertext), len(ciphertext))) 629 if len(ciphertext) != img_size: 630 raise Exception("Unexpected ciphertext size: ", 631 "got {}, expected {}" 632 .format(len(ciphertext), img_size)) 633 else: 634 img = self.inf[offs:] 635 print(' TA offset: {} (0x{:x}) bytes'.format(offs, offs)) 636 print(' TA size: {} (0x{:x}) bytes' 637 .format(len(img), len(img))) 638 if len(img) != img_size: 639 raise Exception("Unexpected img size: got {}, expected {}" 640 .format(len(img), img_size)) 641 offs += img_size 642 643 offs = 0 644 while offs < len(self.inf): 645 if offs > 0: 646 # name_size is the previous subkey header 647 name_img = self.inf[offs:offs + name_size] 648 print(' next name: "{}"'.format(name_img_to_str(name_img))) 649 offs += name_size 650 print('Next header at offset: {} (0x{:x})' 651 .format(offs, offs)) 652 653 shdr = self.inf[offs:offs + SHDR_SIZE] 654 [magic, img_type, img_size, algo_value, hash_size, 655 sig_size] = struct.unpack('<IIIIHH', shdr) 656 offs += SHDR_SIZE 657 658 if magic != SHDR_MAGIC: 659 Exception("Unexpected magic: 0x{:08x}".format(magic)) 660 661 img_type_name = 'Unknown' 662 if img_type == SHDR_BOOTSTRAP_TA: 663 print('Bootstrap TA') 664 img_type_name = 'SHDR_BOOTSTRAP_TA' 665 if img_type == SHDR_ENCRYPTED_TA: 666 print('Encrypted TA') 667 img_type_name = 'SHDR_ENCRYPTED_TA' 668 if img_type == SHDR_SUBKEY: 669 print('Subkey') 670 img_type_name = 'SHDR_SUBKEY' 671 672 algo_name = 'Unknown' 673 if algo_value in sig_tee_alg.values(): 674 algo_name = value_to_key(sig_tee_alg, algo_value) 675 676 print(' struct shdr') 677 print(' magic: 0x{:08x}'.format(magic)) 678 print(' img_type: {} ({})'.format(img_type, img_type_name)) 679 print(' img_size: {} bytes'.format(img_size)) 680 print(' algo: 0x{:08x} ({})'.format(algo_value, algo_name)) 681 print(' hash_size: {} bytes'.format(hash_size)) 682 print(' sig_size: {} bytes'.format(sig_size)) 683 684 if algo_value not in sig_tee_alg.values(): 685 raise Exception('Unrecognized algorithm: 0x{:08x}' 686 .format(algo_value)) 687 688 if hash_size != self.hash_size: 689 raise Exception("Unexpected digest len: {}".format(hash_size)) 690 691 img_digest = self.inf[offs:offs + hash_size] 692 print(' hash: {}' 693 .format(binascii.hexlify(img_digest).decode('ascii'))) 694 offs += hash_size 695 sig = self.inf[offs:offs + sig_size] 696 offs += sig_size 697 698 if img_type == SHDR_BOOTSTRAP_TA or img_type == SHDR_ENCRYPTED_TA: 699 display_ta() 700 elif img_type == SHDR_SUBKEY: 701 img_uuid = self.inf[offs:offs + UUID_SIZE] 702 img_subkey = self.inf[offs + UUID_SIZE: 703 offs + UUID_SIZE + SK_HDR_SIZE] 704 [name_size, subkey_version, max_depth, algo, 705 attr_count] = struct.unpack('<IIIII', img_subkey) 706 if algo not in sig_tee_alg.values(): 707 raise Exception('Unrecognized algorithm: 0x{:08x}' 708 .format(algo)) 709 algo_name = value_to_key(sig_tee_alg, algo) 710 print(' struct shdr_subkey') 711 print(' uuid: {}'.format(uuid.UUID(bytes=img_uuid))) 712 print(' name_size: {}'.format(name_size)) 713 print(' subkey_version: {}'.format(subkey_version)) 714 print(' max_depth: {}'.format(max_depth)) 715 print(' algo: 0x{:08x} ({})'.format(algo, algo_name)) 716 print(' attr_count: {}'.format(attr_count)) 717 offs += img_size 718 else: 719 raise Exception("Unsupported image type: {}".format(img_type)) 720 721 def decrypt_ta(enc_key): 722 from cryptography.hazmat.primitives.ciphers.aead import AESGCM 723 724 cipher = AESGCM(bytes.fromhex(enc_key)) 725 self.img = cipher.decrypt(self.nonce, self.ciphertext, None) 726 727 def __get_padding(self): 728 from cryptography.hazmat.primitives.asymmetric import padding 729 730 if self.sig_algo == 'TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256': 731 pad = padding.PSS(mgf=padding.MGF1(self.chosen_hash), 732 salt_length=self.hash_size) 733 elif self.sig_algo == 'TEE_ALG_RSASSA_PKCS1_V1_5_SHA256': 734 pad = padding.PKCS1v15() 735 736 return pad 737 738 def sign(self): 739 from cryptography.hazmat.primitives.asymmetric import utils 740 from cryptography.hazmat.primitives.asymmetric import rsa 741 742 if not isinstance(self.key, rsa.RSAPrivateKey): 743 logger.error('Provided key cannot be used for signing, ' + 744 'please use offline-signing mode.') 745 sys.exit(1) 746 else: 747 self.sig = self.key.sign(self.img_digest, self.__get_padding(), 748 utils.Prehashed(self.chosen_hash)) 749 750 if len(self.sig) != self.sig_size: 751 raise Exception(("Actual signature length is not equal to ", 752 "the computed one: {} != {}"). 753 format(len(self.sig), self.sig_size)) 754 755 def add_signature(self, sigf): 756 import base64 757 758 with open(sigf, 'r') as f: 759 self.sig = base64.b64decode(f.read()) 760 761 if len(self.sig) != self.sig_size: 762 raise Exception(("Actual signature length is not equal to ", 763 "the expected one: {} != {}"). 764 format(len(self.sig), self.sig_size)) 765 766 def verify_signature(self): 767 from cryptography.hazmat.primitives.asymmetric import utils 768 from cryptography.hazmat.primitives.asymmetric import rsa 769 from cryptography import exceptions 770 771 if isinstance(self.key, rsa.RSAPrivateKey): 772 pkey = self.key.public_key() 773 else: 774 pkey = self.key 775 776 try: 777 pkey.verify(self.sig, self.img_digest, self.__get_padding(), 778 utils.Prehashed(self.chosen_hash)) 779 except exceptions.InvalidSignature: 780 logger.error('Verification failed, ignoring given signature.') 781 sys.exit(1) 782 783 def verify_digest(self): 784 if self.img_digest != self.__calc_digest(): 785 raise Exception('Hash digest does not match') 786 787 def verify_uuid(self, uuid): 788 if self.ta_uuid != uuid.bytes: 789 raise Exception('UUID does not match') 790 791 def add_subkey(self, subkey_file, name): 792 sk_image = BinaryImage(subkey_file, None) 793 self.subkey_img = sk_image.inf 794 sk_image.parse() 795 if not hasattr(sk_image, 'next_inf'): 796 logger.error('Invalid subkey file') 797 sys.exit(1) 798 while len(sk_image.next_inf) > 0: 799 sk_image = BinaryImage(sk_image.next_inf, None) 800 sk_image.parse() 801 802 if name is None: 803 name = '' 804 self.previous_max_depth = sk_image.max_depth 805 self.name_img = str.encode(name).ljust(sk_image.name_size, b'\0') 806 807 def write(self, outf): 808 with open(outf, 'wb') as f: 809 if hasattr(self, 'subkey_img'): 810 f.write(self.subkey_img) 811 f.write(self.name_img) 812 f.write(self.shdr) 813 f.write(self.img_digest) 814 f.write(self.sig) 815 if hasattr(self, 'ta_uuid'): 816 f.write(self.ta_uuid) 817 f.write(self.ta_version) 818 if hasattr(self, 'ehdr'): 819 f.write(self.ehdr) 820 f.write(self.nonce) 821 f.write(self.tag) 822 f.write(self.ciphertext) 823 else: 824 f.write(self.img) 825 826 827def load_ta_image(args): 828 ta_image = BinaryImage(args.inf, args.key) 829 830 if args.enc_key: 831 ta_image.encrypt_ta(args.enc_key, args.enc_key_type, 832 args.algo, args.uuid, args.ta_version) 833 else: 834 ta_image.set_bootstrap_ta(args.algo, args.uuid, args.ta_version) 835 836 return ta_image 837 838 839def command_sign_enc(args): 840 ta_image = load_ta_image(args) 841 if args.subkey: 842 ta_image.add_subkey(args.subkey, args.name) 843 ta_image.sign() 844 ta_image.write(args.outf) 845 logger.info('Successfully signed application.') 846 847 848def command_sign_subkey(args): 849 image = BinaryImage(args.inf, args.key) 850 if args.subkey: 851 image.add_subkey(args.subkey, args.name) 852 image.set_subkey(args.algo, args.name, args.uuid, args.subkey_version, 853 args.max_depth, args.name_size) 854 image.sign() 855 image.write(args.outf) 856 logger.info('Successfully signed subkey.') 857 858 859def command_digest(args): 860 import base64 861 862 ta_image = load_ta_image(args) 863 with open(args.digf, 'wb+') as digfile: 864 digfile.write(base64.b64encode(ta_image.img_digest)) 865 866 867def command_stitch(args): 868 ta_image = load_ta_image(args) 869 ta_image.add_signature(args.sigf) 870 ta_image.verify_signature() 871 ta_image.write(args.outf) 872 logger.info('Successfully applied signature.') 873 874 875def command_verify(args): 876 import uuid 877 878 image = BinaryImage(args.inf, args.key) 879 next_uuid = None 880 max_depth = -1 881 while True: 882 image.parse() 883 if hasattr(image, 'subkey_hdr'): # Subkey 884 print('Subkey UUID: {}'.format(uuid.UUID(bytes=image.uuid))) 885 image.verify_signature() 886 image.verify_digest() 887 if next_uuid: 888 if uuid.UUID(bytes=image.uuid) != next_uuid: 889 raise Exception('UUID {} does not match {}' 890 .format(uuid.UUID(bytes=image.uuid), 891 next_uuid)) 892 if max_depth >= 0: 893 if image.max_depth < 0 or image.max_depth >= max_depth: 894 raise Exception('Invalid max_depth {} not less than {}' 895 .format(image.max_depth, max_depth)) 896 max_depth = image.max_depth 897 if len(image.next_inf) == 0: 898 logger.info('Subkey is correctly verified.') 899 return 900 if image.name_size > 0: 901 next_uuid = uuid_v5_sha512(image.uuid, 902 name_img_to_str(image.name_img)) 903 else: 904 next_uuid = image.uuid 905 image = BinaryImage(image.next_inf, image.subkey_key) 906 else: # TA 907 print('TA UUID: {}'.format(uuid.UUID(bytes=image.ta_uuid))) 908 if next_uuid: 909 if uuid.UUID(bytes=image.ta_uuid) != next_uuid: 910 raise Exception('UUID {} does not match {}' 911 .format(uuid.UUID(bytes=image.ta_uuid), 912 next_uuid)) 913 if hasattr(image, 'ciphertext'): 914 if args.enc_key is None: 915 logger.error('--enc_key needed to decrypt TA') 916 sys.exit(1) 917 image.decrypt_ta(args.enc_key) 918 image.verify_signature() 919 image.verify_digest() 920 image.verify_uuid(args.uuid) 921 logger.info('Trusted application is correctly verified.') 922 return 923 924 925def command_display(args): 926 ta_image = BinaryImage(args.inf, None) 927 ta_image.display() 928 929 930def command_subkey_uuid(args): 931 import uuid 932 933 sk_image = BinaryImage(args.inf, None) 934 sk_image.parse() 935 if not hasattr(sk_image, 'next_inf'): 936 logger.error('Invalid subkey file') 937 sys.exit(1) 938 print('Subkey UUID: {}'.format(uuid.UUID(bytes=sk_image.uuid))) 939 while len(sk_image.next_inf) > 0: 940 sk_image = BinaryImage(sk_image.next_inf, None) 941 sk_image.parse() 942 print('Subkey UUID: {}'.format(uuid.UUID(bytes=sk_image.uuid))) 943 if args.name: 944 if len(args.name) > sk_image.name_size: 945 logger.error('Length of name ({}) '.format(len(args.name)) + 946 'is larger than max name size ({})' 947 .format(sk_image.name_size)) 948 sys.exit(1) 949 print('Next subkey UUID: {}' 950 .format(uuid_v5_sha512(sk_image.uuid, args.name))) 951 else: 952 print('Next subkey UUID unchanged: {}' 953 .format(uuid.UUID(bytes=sk_image.uuid))) 954 955 956def main(): 957 import logging 958 import os 959 960 global logger 961 logging.basicConfig() 962 logger = logging.getLogger(os.path.basename(__file__)) 963 964 args = get_args() 965 args.func(args) 966 967 968if __name__ == "__main__": 969 main() 970