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 19SHDR_BOOTSTRAP_TA = 1 20SHDR_ENCRYPTED_TA = 2 21SHDR_MAGIC = 0x4f545348 22SHDR_SIZE = 20 23EHDR_SIZE = 12 24UUID_SIZE = 16 25# Use 12 bytes for nonce per recommendation 26NONCE_SIZE = 12 27TAG_SIZE = 16 28 29 30def value_to_key(db, val): 31 for k, v in db.items(): 32 if v == val: 33 return k 34 35 36def uuid_parse(s): 37 from uuid import UUID 38 return UUID(s) 39 40 41def int_parse(str): 42 return int(str, 0) 43 44 45def get_args(): 46 def arg_add_uuid(parser): 47 parser.add_argument( 48 '--uuid', required=True, type=uuid_parse, 49 help='String UUID of the TA') 50 51 def arg_add_key(parser): 52 parser.add_argument( 53 '--key', required=True, help=''' 54 Name of signing and verification key file (PEM format) or an 55 Amazon Resource Name (arn:) of an AWS KMS asymmetric key. 56 At least public key for the commands digest, stitch, and 57 verify, else a private key''') 58 59 def arg_add_enc_key(parser): 60 parser.add_argument( 61 '--enc-key', required=False, help='Encryption key string') 62 63 def arg_add_enc_key_type(parser): 64 parser.add_argument( 65 '--enc-key-type', required=False, 66 default='SHDR_ENC_KEY_DEV_SPECIFIC', 67 choices=list(enc_key_type.keys()), help=''' 68 Encryption key type, 69 Defaults to SHDR_ENC_KEY_DEV_SPECIFIC.''') 70 71 def arg_add_ta_version(parser): 72 parser.add_argument( 73 '--ta-version', required=False, type=int_parse, default=0, help=''' 74 TA version stored as a 32-bit unsigned integer and used for 75 rollback protection of TA install in the secure database. 76 Defaults to 0.''') 77 78 def arg_add_sig(parser): 79 parser.add_argument( 80 '--sig', required=True, dest='sigf', 81 help='Name of signature input file, defaults to <UUID>.sig') 82 83 def arg_add_dig(parser): 84 parser.add_argument( 85 '--dig', required=True, dest='digf', 86 help='Name of digest output file, defaults to <UUID>.dig') 87 88 def arg_add_in(parser): 89 parser.add_argument( 90 '--in', required=False, dest='inf', help=''' 91 Name of application input file, defaults to 92 <UUID>.stripped.elf''') 93 94 def arg_add_out(parser): 95 parser.add_argument( 96 '--out', required=True, dest='outf', 97 help='Name of application output file, defaults to <UUID>.ta') 98 99 def arg_add_algo(parser): 100 parser.add_argument( 101 '--algo', required=False, choices=list(sig_tee_alg.keys()), 102 default='TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256', help=''' 103 The hash and signature algorithm. 104 Defaults to TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256.''') 105 106 def get_outf_default(parsed): 107 return str(parsed.uuid) + '.ta' 108 109 def get_inf_default(parsed): 110 return str(parsed.uuid) + '.stripped.elf' 111 112 def get_sigf_default(parsed): 113 return str(parsed.uuid) + '.sig' 114 115 def get_digf_default(parsed): 116 return str(parsed.uuid) + '.dig' 117 118 def assign_default_value(parsed, attr, func): 119 if hasattr(parsed, attr) and getattr(parsed, attr) is None: 120 setattr(parsed, attr, func(parsed)) 121 122 import argparse 123 import textwrap 124 125 parser = argparse.ArgumentParser( 126 description='Sign and encrypt (optional) a Trusted Application ' + 127 ' for OP-TEE.', 128 usage='%(prog)s <command> ...', 129 epilog='<command> -h for detailed help') 130 subparsers = parser.add_subparsers( 131 title='valid commands, with possible aliases in ()', 132 dest='command', metavar='') 133 134 parser_sign_enc = subparsers.add_parser( 135 'sign-enc', prog=parser.prog + ' sign-enc', 136 help='Generate signed and optionally encrypted loadable TA image file') 137 parser_sign_enc.set_defaults(func=command_sign_enc) 138 arg_add_uuid(parser_sign_enc) 139 arg_add_ta_version(parser_sign_enc) 140 arg_add_in(parser_sign_enc) 141 arg_add_out(parser_sign_enc) 142 arg_add_key(parser_sign_enc) 143 arg_add_enc_key(parser_sign_enc) 144 arg_add_enc_key_type(parser_sign_enc) 145 arg_add_algo(parser_sign_enc) 146 147 parser_digest = subparsers.add_parser( 148 'digest', aliases=['generate-digest'], prog=parser.prog + ' digest', 149 formatter_class=argparse.RawDescriptionHelpFormatter, 150 help='Generate loadable TA binary image digest for offline signing', 151 epilog=textwrap.dedent('''\ 152 example offline signing command using OpenSSL for algorithm 153 TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256: 154 base64 -d <UUID>.dig | \\ 155 openssl pkeyutl -sign -inkey <KEYFILE>.pem \\ 156 -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pss \\ 157 -pkeyopt rsa_pss_saltlen:digest \\ 158 -pkeyopt rsa_mgf1_md:sha256 | \\ 159 base64 > <UUID>.sig 160 161 example offline signing command using OpenSSL for algorithm 162 TEE_ALG_RSASSA_PKCS1_V1_5_SHA256: 163 base64 -d <UUID>.dig | \\ 164 openssl pkeyutl -sign -inkey <KEYFILE>.pem \\ 165 -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pkcs1 | \\ 166 base64 > <UUID>.sig 167 ''')) 168 parser_digest.set_defaults(func=command_digest) 169 arg_add_uuid(parser_digest) 170 arg_add_ta_version(parser_digest) 171 arg_add_in(parser_digest) 172 arg_add_key(parser_digest) 173 arg_add_enc_key(parser_digest) 174 arg_add_enc_key_type(parser_digest) 175 arg_add_algo(parser_digest) 176 arg_add_dig(parser_digest) 177 178 parser_stitch = subparsers.add_parser( 179 'stitch', aliases=['stitch-ta'], prog=parser.prog + ' stich', 180 help='Generate loadable signed and encrypted TA binary image file' + 181 ' from TA raw image and its signature') 182 parser_stitch.set_defaults(func=command_stitch) 183 arg_add_uuid(parser_stitch) 184 arg_add_ta_version(parser_stitch) 185 arg_add_in(parser_stitch) 186 arg_add_key(parser_stitch) 187 arg_add_out(parser_stitch) 188 arg_add_enc_key(parser_stitch) 189 arg_add_enc_key_type(parser_stitch) 190 arg_add_algo(parser_stitch) 191 arg_add_sig(parser_stitch) 192 193 parser_verify = subparsers.add_parser( 194 'verify', prog=parser.prog + ' verify', 195 help='Verify signed TA binary') 196 parser_verify.set_defaults(func=command_verify) 197 arg_add_uuid(parser_verify) 198 arg_add_in(parser_verify) 199 arg_add_key(parser_verify) 200 201 parser_display = subparsers.add_parser( 202 'display', prog=parser.prog + ' display', 203 help='Parses and displays a signed TA binary') 204 parser_display.set_defaults(func=command_display) 205 arg_add_in(parser_display) 206 207 argv = sys.argv[1:] 208 if (len(argv) > 0 and argv[0][0] == '-' and 209 argv[0] != '-h' and argv[0] != '--help'): 210 # The default sub-command is 'sign-enc' so add it to the parser 211 # if one is missing 212 argv = ['sign-enc'] + argv 213 214 parsed = parser.parse_args(argv) 215 216 if parsed.command is None: 217 parser.print_help() 218 sys.exit(1) 219 220 # Set a few defaults if defined for the current command 221 assign_default_value(parsed, 'inf', get_inf_default) 222 assign_default_value(parsed, 'outf', get_outf_default) 223 assign_default_value(parsed, 'sigf', get_sigf_default) 224 assign_default_value(parsed, 'digf', get_digf_default) 225 226 return parsed 227 228 229def load_asymmetric_key(arg_key): 230 if arg_key.startswith('arn:'): 231 from sign_helper_kms import _RSAPrivateKeyInKMS 232 key = _RSAPrivateKeyInKMS(arg_key) 233 else: 234 from cryptography.hazmat.backends import default_backend 235 from cryptography.hazmat.primitives.serialization import ( 236 load_pem_private_key, load_pem_public_key) 237 238 with open(arg_key, 'rb') as f: 239 data = f.read() 240 241 try: 242 key = load_pem_private_key(data, password=None, 243 backend=default_backend()) 244 except ValueError: 245 key = load_pem_public_key(data, backend=default_backend()) 246 247 return key 248 249 250class BinaryImage: 251 def __init__(self, arg_inf, arg_key): 252 from cryptography.hazmat.primitives import hashes 253 254 # Exactly what inf is holding isn't determined a this stage 255 with open(arg_inf, 'rb') as f: 256 self.inf = f.read() 257 258 if arg_key is None: 259 self.key = None 260 else: 261 self.key = load_asymmetric_key(arg_key) 262 self.sig_len = math.ceil(self.key.key_size / 8) 263 264 self.chosen_hash = hashes.SHA256() 265 self.digest_len = self.chosen_hash.digest_size 266 267 def __pack_img(self, img_type, sign_algo): 268 import struct 269 270 self.sig_algo = sign_algo 271 self.img_type = img_type 272 self.shdr = struct.pack('<IIIIHH', SHDR_MAGIC, img_type, len(self.img), 273 sig_tee_alg[sign_algo], self.digest_len, 274 self.sig_len) 275 276 def __calc_digest(self): 277 from cryptography.hazmat.backends import default_backend 278 from cryptography.hazmat.primitives import hashes 279 280 h = hashes.Hash(self.chosen_hash, default_backend()) 281 h.update(self.shdr) 282 h.update(self.ta_uuid) 283 h.update(self.ta_version) 284 if hasattr(self, 'ehdr'): 285 h.update(self.ehdr) 286 h.update(self.nonce) 287 h.update(self.tag) 288 h.update(self.img) 289 return h.finalize() 290 291 def encrypt_ta(self, enc_key, key_type, sig_algo, uuid, ta_version): 292 from cryptography.hazmat.primitives.ciphers.aead import AESGCM 293 import struct 294 import os 295 296 self.img = self.inf 297 298 cipher = AESGCM(bytes.fromhex(enc_key)) 299 self.nonce = os.urandom(NONCE_SIZE) 300 out = cipher.encrypt(self.nonce, self.img, None) 301 self.ciphertext = out[:-TAG_SIZE] 302 # Authentication Tag is always the last bytes 303 self.tag = out[-TAG_SIZE:] 304 305 enc_algo = enc_tee_alg['TEE_ALG_AES_GCM'] 306 flags = enc_key_type[key_type] 307 self.ehdr = struct.pack('<IIHH', enc_algo, flags, len(self.nonce), 308 len(self.tag)) 309 310 self.__pack_img(SHDR_ENCRYPTED_TA, sig_algo) 311 self.ta_uuid = uuid.bytes 312 self.ta_version = struct.pack('<I', ta_version) 313 self.img_digest = self.__calc_digest() 314 315 def set_bootstrap_ta(self, sig_algo, uuid, ta_version): 316 import struct 317 318 self.img = self.inf 319 self.__pack_img(SHDR_BOOTSTRAP_TA, sig_algo) 320 self.ta_uuid = uuid.bytes 321 self.ta_version = struct.pack('<I', ta_version) 322 self.img_digest = self.__calc_digest() 323 324 def parse(self): 325 import struct 326 327 offs = 0 328 self.shdr = self.inf[offs:offs + SHDR_SIZE] 329 [magic, img_type, img_size, algo_value, digest_len, 330 sig_len] = struct.unpack('<IIIIHH', self.shdr) 331 offs += SHDR_SIZE 332 333 if magic != SHDR_MAGIC: 334 raise Exception("Unexpected magic: 0x{:08x}".format(magic)) 335 336 if algo_value not in sig_tee_alg.values(): 337 raise Exception('Unrecognized algorithm: 0x{:08x}' 338 .format(algo_value)) 339 self.sig_algo = value_to_key(sig_tee_alg, algo_value) 340 341 if digest_len != self.digest_len: 342 raise Exception("Unexpected digest len: {}".format(digest_len)) 343 344 self.img_digest = self.inf[offs:offs + digest_len] 345 offs += digest_len 346 self.sig = self.inf[offs:offs + sig_len] 347 offs += sig_len 348 349 if img_type == SHDR_BOOTSTRAP_TA or img_type == SHDR_ENCRYPTED_TA: 350 self.ta_uuid = self.inf[offs:offs + UUID_SIZE] 351 offs += UUID_SIZE 352 self.ta_version = self.inf[offs:offs + 4] 353 offs += 4 354 if img_type == SHDR_ENCRYPTED_TA: 355 self.ehdr = self.inf[offs: offs + EHDR_SIZE] 356 offs += EHDR_SIZE 357 [enc_algo, flags, nonce_len, 358 tag_len] = struct.unpack('<IIHH', self.ehdr) 359 if enc_value not in enc_tee_alg.values(): 360 raise Exception('Unrecognized encrypt algorithm: 0x{:08x}' 361 .format(enc_value)) 362 if nonce_len != 12: 363 raise Exception("Unexpected nonce len: {}" 364 .format(nonce_len)) 365 self.nonce = self.inf[offs:offs + nonce_len] 366 offs += nonce_len 367 368 if tag_len != 16: 369 raise Exception("Unexpected tag len: {}".format(tag_len)) 370 self.tag = self.inf[-tag_len:] 371 self.ciphertext = self.inf[offs:-tag_len] 372 if len(self.ciphertext) != img_size: 373 raise Exception("Unexpected ciphertext size: ", 374 "got {}, expected {}" 375 .format(len(self.ciphertext), img_size)) 376 else: 377 self.img = self.inf[offs:] 378 if len(self.img) != img_size: 379 raise Exception("Unexpected img size: got {}, expected {}" 380 .format(len(self.img), img_size)) 381 else: 382 raise Exception("Unsupported image type: {}".format(img_type)) 383 384 def display(self): 385 import binascii 386 import struct 387 import uuid 388 389 offs = 0 390 shdr = self.inf[offs:offs + SHDR_SIZE] 391 [magic, img_type, img_size, algo_value, digest_len, 392 sig_len] = struct.unpack('<IIIIHH', shdr) 393 offs += SHDR_SIZE 394 395 if magic != SHDR_MAGIC: 396 Exception("Unexpected magic: 0x{:08x}".format(magic)) 397 398 img_type_name = 'Unknown' 399 if img_type == SHDR_BOOTSTRAP_TA: 400 print('Bootstrap TA') 401 img_type_name = 'SHDR_BOOTSTRAP_TA' 402 if img_type == SHDR_ENCRYPTED_TA: 403 print('Encrypted TA') 404 img_type_name = 'SHDR_ENCRYPTED_TA' 405 406 algo_name = 'Unknown' 407 if algo_value in sig_tee_alg.values(): 408 algo_name = value_to_key(sig_tee_alg, algo_value) 409 410 print(' struct shdr') 411 print(' magic: 0x{:08x}'.format(magic)) 412 print(' img_type: {} ({})'.format(img_type, img_type_name)) 413 print(' img_size: {} bytes'.format(img_size)) 414 print(' algo: 0x{:08x} ({})'.format(algo_value, algo_name)) 415 print(' hash_size: {} bytes'.format(digest_len)) 416 print(' sig_size: {} bytes'.format(sig_len)) 417 418 if algo_value not in sig_tee_alg.values(): 419 raise Exception('Unrecognized algorithm: 0x{:08x}' 420 .format(algo_value)) 421 422 if digest_len != self.digest_len: 423 raise Exception("Unexpected digest len: {}".format(digest_len)) 424 425 img_digest = self.inf[offs:offs + digest_len] 426 print(' hash: {}' 427 .format(binascii.hexlify(img_digest).decode('ascii'))) 428 offs += digest_len 429 sig = self.inf[offs:offs + sig_len] 430 offs += sig_len 431 432 if img_type == SHDR_BOOTSTRAP_TA or img_type == SHDR_ENCRYPTED_TA: 433 print(' struct shdr_bootstrap_ta') 434 ta_uuid = self.inf[offs:offs + UUID_SIZE] 435 print(' uuid: {}'.format(uuid.UUID(bytes=ta_uuid))) 436 offs += UUID_SIZE 437 [ta_version] = struct.unpack('<I', self.inf[offs:offs + 4]) 438 print(' ta_version: {}'.format(ta_version)) 439 440 offs += 4 441 if img_type == SHDR_ENCRYPTED_TA: 442 ehdr = self.inf[offs: offs + EHDR_SIZE] 443 offs += EHDR_SIZE 444 [enc_algo, flags, nonce_len, 445 tag_len] = struct.unpack('<IIHH', ehdr) 446 447 print(' struct shdr_encrypted_ta') 448 enc_algo_name = 'Unkown' 449 if enc_algo in enc_tee_alg.values(): 450 enc_algo_name = value_to_key(enc_tee_alg, enc_algo) 451 print(' enc_algo: 0x{:08x} ({})' 452 .format(enc_algo, enc_algo_name)) 453 454 if enc_algo not in enc_tee_alg.values(): 455 raise Exception('Unrecognized encrypt algorithm: 0x{:08x}' 456 .format(enc_value)) 457 458 flags_name = 'Unkown' 459 if flags in enc_key_type.values(): 460 flags_name = value_to_key(enc_key_type, flags) 461 print(' flags: 0x{:x} ({})'.format(flags, flags_name)) 462 463 print(' iv_size: {} (bytes)'.format(nonce_len)) 464 if nonce_len != NONCE_SIZE: 465 raise Exception("Unexpected nonce len: {}" 466 .format(nonce_len)) 467 nonce = self.inf[offs:offs + nonce_len] 468 print(' iv: {}' 469 .format(binascii.hexlify(nonce).decode('ascii'))) 470 offs += nonce_len 471 472 print(' tag_size: {} (bytes)'.format(tag_len)) 473 if tag_len != TAG_SIZE: 474 raise Exception("Unexpected tag len: {}".format(tag_len)) 475 tag = self.inf[-tag_len:] 476 print(' tag: {}' 477 .format(binascii.hexlify(tag).decode('ascii'))) 478 ciphertext = self.inf[offs:-tag_len] 479 print(' TA offset: {} (0x{:x}) bytes'.format(offs, offs)) 480 print(' TA size: {} (0x{:x}) bytes' 481 .format(len(ciphertext), len(ciphertext))) 482 if len(ciphertext) != img_size: 483 raise Exception("Unexpected ciphertext size: ", 484 "got {}, expected {}" 485 .format(len(ciphertext), img_size)) 486 offs += tag_len 487 else: 488 img = self.inf[offs:] 489 print(' TA offset: {} (0x{:x}) bytes'.format(offs, offs)) 490 print(' TA size: {} (0x{:x}) bytes' 491 .format(len(img), len(img))) 492 if len(img) != img_size: 493 raise Exception("Unexpected img size: got {}, expected {}" 494 .format(len(img), img_size)) 495 else: 496 raise Exception("Unsupported image type: {}".format(img_type)) 497 498 def decrypt_ta(enc_key): 499 from cryptography.hazmat.primitives.ciphers.aead import AESGCM 500 501 cipher = AESGCM(bytes.fromhex(enc_key)) 502 self.img = cipher.decrypt(self.nonce, self.ciphertext, None) 503 504 def __get_padding(self): 505 from cryptography.hazmat.primitives.asymmetric import padding 506 507 if self.sig_algo == 'TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256': 508 pad = padding.PSS(mgf=padding.MGF1(self.chosen_hash), 509 salt_length=self.digest_len) 510 elif self.sig_algo == 'TEE_ALG_RSASSA_PKCS1_V1_5_SHA256': 511 pad = padding.PKCS1v15() 512 513 return pad 514 515 def sign(self): 516 from cryptography.hazmat.primitives.asymmetric import utils 517 from cryptography.hazmat.primitives.asymmetric import rsa 518 519 if not isinstance(self.key, rsa.RSAPrivateKey): 520 logger.error('Provided key cannot be used for signing, ' + 521 'please use offline-signing mode.') 522 sys.exit(1) 523 else: 524 self.sig = self.key.sign(self.img_digest, self.__get_padding(), 525 utils.Prehashed(self.chosen_hash)) 526 527 if len(self.sig) != self.sig_len: 528 raise Exception(("Actual signature length is not equal to ", 529 "the computed one: {} != {}"). 530 format(len(self.sig), self.sig_len)) 531 532 def add_signature(self, sigf): 533 import base64 534 535 with open(sigf, 'r') as f: 536 self.sig = base64.b64decode(f.read()) 537 538 if len(self.sig) != self.sig_len: 539 raise Exception(("Actual signature length is not equal to ", 540 "the expected one: {} != {}"). 541 format(len(self.sig), self.sig_len)) 542 543 def verify_signature(self): 544 from cryptography.hazmat.primitives.asymmetric import utils 545 from cryptography.hazmat.primitives.asymmetric import rsa 546 from cryptography import exceptions 547 548 if isinstance(self.key, rsa.RSAPrivateKey): 549 pkey = self.key.public_key() 550 else: 551 pkey = self.key 552 553 try: 554 pkey.verify(self.sig, self.img_digest, self.__get_padding(), 555 utils.Prehashed(self.chosen_hash)) 556 except exceptions.InvalidSignature: 557 logger.error('Verification failed, ignoring given signature.') 558 sys.exit(1) 559 560 def verify_digest(self): 561 if self.img_digest != self.__calc_digest(): 562 raise Exception('Hash digest does not match') 563 564 def verify_uuid(self, uuid): 565 if self.ta_uuid != uuid.bytes: 566 raise Exception('UUID does not match') 567 568 def write(self, outf): 569 with open(outf, 'wb') as f: 570 f.write(self.shdr) 571 f.write(self.img_digest) 572 f.write(self.sig) 573 f.write(self.ta_uuid) 574 f.write(self.ta_version) 575 if hasattr(self, 'ehdr'): 576 f.write(self.ehdr) 577 f.write(self.nonce) 578 f.write(self.tag) 579 f.write(self.ciphertext) 580 else: 581 f.write(self.img) 582 583 584def load_ta_image(args): 585 ta_image = BinaryImage(args.inf, args.key) 586 587 if args.enc_key: 588 ta_image.encrypt_ta(args.enc_key, args.enc_key_type, 589 args.algo, args.uuid, args.ta_version) 590 else: 591 ta_image.set_bootstrap_ta(args.algo, args.uuid, args.ta_version) 592 593 return ta_image 594 595 596def command_sign_enc(args): 597 ta_image = load_ta_image(args) 598 ta_image.sign() 599 ta_image.write(args.outf) 600 logger.info('Successfully signed application.') 601 602 603def command_digest(args): 604 import base64 605 606 ta_image = load_ta_image(args) 607 with open(args.digf, 'wb+') as digfile: 608 digfile.write(base64.b64encode(ta_image.img_digest)) 609 610 611def command_stitch(args): 612 ta_image = load_ta_image(args) 613 ta_image.add_signature(args.sigf) 614 ta_image.verify_signature() 615 ta_image.write(args.outf) 616 logger.info('Successfully applied signature.') 617 618 619def command_verify(args): 620 ta_image = BinaryImage(args.inf, args.key) 621 ta_image.parse() 622 if hasattr(ta_image, 'ciphertext'): 623 if args.enc_key is None: 624 logger.error('--enc_key needed to decrypt TA') 625 sys.exit(1) 626 ta_image.decrypt_ta(args.enc_key) 627 ta_image.verify_signature() 628 ta_image.verify_digest() 629 ta_image.verify_uuid(args.uuid) 630 logger.info('Trusted application is correctly verified.') 631 632 633def command_display(args): 634 ta_image = BinaryImage(args.inf, None) 635 ta_image.display() 636 637 638def main(): 639 import logging 640 import os 641 642 global logger 643 logging.basicConfig() 644 logger = logging.getLogger(os.path.basename(__file__)) 645 646 args = get_args() 647 args.func(args) 648 649 650if __name__ == "__main__": 651 main() 652