blob: c2af9157af1d563db7bbe31051305f2f0d61ef7c [file] [log] [blame]
Rouven Czerwinskibbaeed42019-08-07 20:07:00 +02001#!/usr/bin/env python3
Jens Wiklandercd5cf432017-11-28 16:59:15 +01002# SPDX-License-Identifier: BSD-2-Clause
Sumit Garg2de17fd2019-10-23 12:47:24 +05303#
4# Copyright (c) 2015, 2017, 2019, Linaro Limited
5#
Jens Wiklandercd5cf432017-11-28 16:59:15 +01006
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +02007import sys
Donald Chan169eac12021-10-24 14:22:54 -07008import math
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +02009
Jerome Forissier4a477922018-11-14 11:02:49 +010010
Mingyuan Xiangcf3d6ac2020-09-17 19:01:02 +080011algo = {'TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256': 0x70414930,
12 'TEE_ALG_RSASSA_PKCS1_V1_5_SHA256': 0x70004830}
13
Donald Chanc45a84b2022-01-01 22:32:45 +000014enc_key_type = {'SHDR_ENC_KEY_DEV_SPECIFIC': 0x0,
15 'SHDR_ENC_KEY_CLASS_WIDE': 0x1}
16
Donald Chana797f202022-01-10 19:31:13 +000017SHDR_BOOTSTRAP_TA = 1
18SHDR_ENCRYPTED_TA = 2
19SHDR_MAGIC = 0x4f545348
20SHDR_SIZE = 20
21
Mingyuan Xiangcf3d6ac2020-09-17 19:01:02 +080022
Jens Wiklandercd5cf432017-11-28 16:59:15 +010023def uuid_parse(s):
24 from uuid import UUID
25 return UUID(s)
26
27
28def int_parse(str):
29 return int(str, 0)
30
Jens Wiklanderbc420742015-05-05 14:59:15 +020031
Jens Wiklander5e153002022-10-06 11:43:54 +020032def get_args():
Jens Wiklanderf182afc2022-10-04 10:19:27 +020033 def arg_add_uuid(parser):
34 parser.add_argument(
35 '--uuid', required=True, type=uuid_parse,
36 help='String UUID of the TA')
37
38 def arg_add_key(parser):
39 parser.add_argument(
40 '--key', required=True, help='''
41 Name of signing and verification key file (PEM format) or an
42 Amazon Resource Name (arn:) of an AWS KMS asymmetric key.
43 At least public key for the commands digest, stitch, and
44 verify, else a private key''')
45
46 def arg_add_enc_key(parser):
47 parser.add_argument(
48 '--enc-key', required=False, help='Encryption key string')
49
50 def arg_add_enc_key_type(parser):
51 parser.add_argument(
52 '--enc-key-type', required=False,
53 default='SHDR_ENC_KEY_DEV_SPECIFIC',
54 choices=list(enc_key_type.keys()), help='''
55 Encryption key type,
56 Defaults to SHDR_ENC_KEY_DEV_SPECIFIC.''')
57
58 def arg_add_ta_version(parser):
59 parser.add_argument(
60 '--ta-version', required=False, type=int_parse, default=0, help='''
61 TA version stored as a 32-bit unsigned integer and used for
62 rollback protection of TA install in the secure database.
63 Defaults to 0.''')
64
65 def arg_add_sig(parser):
66 parser.add_argument(
67 '--sig', required=True, dest='sigf',
68 help='Name of signature input file, defaults to <UUID>.sig')
69
70 def arg_add_dig(parser):
71 parser.add_argument(
72 '--dig', required=True, dest='digf',
73 help='Name of digest output file, defaults to <UUID>.dig')
74
75 def arg_add_in(parser):
76 parser.add_argument(
77 '--in', required=False, dest='inf', help='''
78 Name of application input file, defaults to
79 <UUID>.stripped.elf''')
80
81 def arg_add_out(parser):
82 parser.add_argument(
83 '--out', required=True, dest='outf',
84 help='Name of application output file, defaults to <UUID>.ta')
85
86 def arg_add_algo(parser):
87 parser.add_argument(
88 '--algo', required=False, choices=list(algo.keys()),
89 default='TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256', help='''
90 The hash and signature algorithm.
91 Defaults to TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256.''')
92
93 def get_outf_default(parsed):
94 return str(parsed.uuid) + '.ta'
95
96 def get_inf_default(parsed):
97 return str(parsed.uuid) + '.stripped.elf'
98
99 def get_sigf_default(parsed):
100 return str(parsed.uuid) + '.sig'
101
102 def get_digf_default(parsed):
103 return str(parsed.uuid) + '.dig'
104
105 def assign_default_value(parsed, attr, func):
106 if hasattr(parsed, attr) and getattr(parsed, attr) is None:
107 setattr(parsed, attr, func(parsed))
108
109 import argparse
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200110 import textwrap
Jens Wiklanderbc420742015-05-05 14:59:15 +0200111
Jens Wiklanderf182afc2022-10-04 10:19:27 +0200112 parser = argparse.ArgumentParser(
113 description='Sign and encrypt (optional) a Trusted Application ' +
114 ' for OP-TEE.',
115 usage='%(prog)s <command> ...',
116 epilog='<command> -h for detailed help')
117 subparsers = parser.add_subparsers(
118 title='valid commands, with possible aliases in ()',
119 dest='command', metavar='')
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200120
Jens Wiklanderf182afc2022-10-04 10:19:27 +0200121 parser_sign_enc = subparsers.add_parser(
122 'sign-enc', prog=parser.prog + ' sign-enc',
123 help='Generate signed and optionally encrypted loadable TA image file')
124 arg_add_uuid(parser_sign_enc)
125 arg_add_ta_version(parser_sign_enc)
126 arg_add_in(parser_sign_enc)
127 arg_add_out(parser_sign_enc)
128 arg_add_key(parser_sign_enc)
129 arg_add_enc_key(parser_sign_enc)
130 arg_add_enc_key_type(parser_sign_enc)
131 arg_add_algo(parser_sign_enc)
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200132
Jens Wiklanderf182afc2022-10-04 10:19:27 +0200133 parser_digest = subparsers.add_parser(
134 'digest', aliases=['generate-digest'], prog=parser.prog + ' digest',
135 formatter_class=argparse.RawDescriptionHelpFormatter,
136 help='Generate loadable TA binary image digest for offline signing',
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200137 epilog=textwrap.dedent('''\
Mingyuan Xiangcf3d6ac2020-09-17 19:01:02 +0800138 example offline signing command using OpenSSL for algorithm
139 TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256:
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200140 base64 -d <UUID>.dig | \\
141 openssl pkeyutl -sign -inkey <KEYFILE>.pem \\
Mingyuan Xiangcf3d6ac2020-09-17 19:01:02 +0800142 -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pss \\
143 -pkeyopt rsa_pss_saltlen:digest \\
144 -pkeyopt rsa_mgf1_md:sha256 | \\
Jens Wiklanderf182afc2022-10-04 10:19:27 +0200145 base64 > <UUID>.sig
146
Mingyuan Xiangcf3d6ac2020-09-17 19:01:02 +0800147 example offline signing command using OpenSSL for algorithm
148 TEE_ALG_RSASSA_PKCS1_V1_5_SHA256:
149 base64 -d <UUID>.dig | \\
150 openssl pkeyutl -sign -inkey <KEYFILE>.pem \\
151 -pkeyopt digest:sha256 -pkeyopt rsa_padding_mode:pkcs1 | \\
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200152 base64 > <UUID>.sig
153 '''))
Jens Wiklanderf182afc2022-10-04 10:19:27 +0200154 arg_add_uuid(parser_digest)
155 arg_add_ta_version(parser_digest)
156 arg_add_in(parser_digest)
157 arg_add_key(parser_digest)
158 arg_add_enc_key(parser_digest)
159 arg_add_enc_key_type(parser_digest)
160 arg_add_algo(parser_digest)
161 arg_add_dig(parser_digest)
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200162
Jens Wiklanderf182afc2022-10-04 10:19:27 +0200163 parser_stitch = subparsers.add_parser(
164 'stitch', aliases=['stitch-ta'], prog=parser.prog + ' stich',
165 help='Generate loadable signed and encrypted TA binary image file' +
166 ' from TA raw image and its signature')
167 arg_add_uuid(parser_stitch)
168 arg_add_ta_version(parser_stitch)
169 arg_add_in(parser_stitch)
170 arg_add_key(parser_stitch)
171 arg_add_out(parser_stitch)
172 arg_add_enc_key(parser_stitch)
173 arg_add_enc_key_type(parser_stitch)
174 arg_add_algo(parser_stitch)
175 arg_add_sig(parser_stitch)
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200176
Jens Wiklanderf182afc2022-10-04 10:19:27 +0200177 parser_verify = subparsers.add_parser(
178 'verify', prog=parser.prog + ' verify',
179 help='Verify signed TA binary')
180 arg_add_uuid(parser_verify)
181 arg_add_in(parser_verify)
182 arg_add_key(parser_verify)
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200183
Jens Wiklanderf182afc2022-10-04 10:19:27 +0200184 argv = sys.argv[1:]
185 if (len(argv) > 0 and argv[0][0] == '-' and
186 argv[0] != '-h' and argv[0] != '--help'):
187 # The default sub-command is 'sign-enc' so add it to the parser
188 # if one is missing
189 argv = ['sign-enc'] + argv
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200190
Jens Wiklanderf182afc2022-10-04 10:19:27 +0200191 parsed = parser.parse_args(argv)
192
193 if parsed.command is None:
194 parser.print_help()
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200195 sys.exit(1)
196
Jens Wiklanderf182afc2022-10-04 10:19:27 +0200197 # Set a few defaults if defined for the current command
198 assign_default_value(parsed, 'inf', get_inf_default)
199 assign_default_value(parsed, 'outf', get_outf_default)
200 assign_default_value(parsed, 'sigf', get_sigf_default)
201 assign_default_value(parsed, 'digf', get_digf_default)
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200202
203 return parsed
Jens Wiklandercd5cf432017-11-28 16:59:15 +0100204
Jens Wiklanderbc420742015-05-05 14:59:15 +0200205
206def main():
Donald Chan169eac12021-10-24 14:22:54 -0700207 from cryptography import exceptions
208 from cryptography.hazmat.backends import default_backend
209 from cryptography.hazmat.primitives import serialization
210 from cryptography.hazmat.primitives import hashes
211 from cryptography.hazmat.primitives.asymmetric import padding
212 from cryptography.hazmat.primitives.asymmetric import rsa
213 from cryptography.hazmat.primitives.asymmetric import utils
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200214 import base64
215 import logging
216 import os
Jens Wiklandercd5cf432017-11-28 16:59:15 +0100217 import struct
Jens Wiklanderbc420742015-05-05 14:59:15 +0200218
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200219 logging.basicConfig()
Jens Wiklander5e153002022-10-06 11:43:54 +0200220 global logger
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200221 logger = logging.getLogger(os.path.basename(__file__))
Jens Wiklanderbc420742015-05-05 14:59:15 +0200222
Jens Wiklander5e153002022-10-06 11:43:54 +0200223 args = get_args()
Jens Wiklanderbc420742015-05-05 14:59:15 +0200224
Donald Chan05c007f2022-01-05 17:54:00 +0000225 if args.key.startswith('arn:'):
226 from sign_helper_kms import _RSAPrivateKeyInKMS
227 key = _RSAPrivateKeyInKMS(args.key)
228 else:
229 with open(args.key, 'rb') as f:
230 data = f.read()
Donald Chan169eac12021-10-24 14:22:54 -0700231
Donald Chan05c007f2022-01-05 17:54:00 +0000232 try:
233 key = serialization.load_pem_private_key(
234 data,
235 password=None,
236 backend=default_backend())
237 except ValueError:
238 key = serialization.load_pem_public_key(
239 data,
240 backend=default_backend())
Jens Wiklanderbc420742015-05-05 14:59:15 +0200241
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200242 with open(args.inf, 'rb') as f:
243 img = f.read()
244
Donald Chan169eac12021-10-24 14:22:54 -0700245 chosen_hash = hashes.SHA256()
246 h = hashes.Hash(chosen_hash, default_backend())
Jens Wiklanderbc420742015-05-05 14:59:15 +0200247
Donald Chan169eac12021-10-24 14:22:54 -0700248 digest_len = chosen_hash.digest_size
249 sig_len = math.ceil(key.key_size / 8)
Volodymyr Babchuk90ad2452019-08-21 21:00:32 +0300250
Jens Wiklandercd5cf432017-11-28 16:59:15 +0100251 img_size = len(img)
Jens Wiklanderbc420742015-05-05 14:59:15 +0200252
Etienne Carriere47844622019-08-12 11:33:24 +0200253 hdr_version = args.ta_version # struct shdr_bootstrap_ta::ta_version
254
Donald Chana797f202022-01-10 19:31:13 +0000255 magic = SHDR_MAGIC
Sumit Garg2de17fd2019-10-23 12:47:24 +0530256 if args.enc_key:
Donald Chana797f202022-01-10 19:31:13 +0000257 img_type = SHDR_ENCRYPTED_TA
Sumit Garg2de17fd2019-10-23 12:47:24 +0530258 else:
Donald Chana797f202022-01-10 19:31:13 +0000259 img_type = SHDR_BOOTSTRAP_TA
Etienne Carriere47844622019-08-12 11:33:24 +0200260
Jens Wiklandercd5cf432017-11-28 16:59:15 +0100261 shdr = struct.pack('<IIIIHH',
Mingyuan Xiangcf3d6ac2020-09-17 19:01:02 +0800262 magic, img_type, img_size, algo[args.algo],
263 digest_len, sig_len)
Jens Wiklandercd5cf432017-11-28 16:59:15 +0100264 shdr_uuid = args.uuid.bytes
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200265 shdr_version = struct.pack('<I', hdr_version)
Jens Wiklanderbc420742015-05-05 14:59:15 +0200266
Sumit Garg2de17fd2019-10-23 12:47:24 +0530267 if args.enc_key:
Donald Chan169eac12021-10-24 14:22:54 -0700268 from cryptography.hazmat.primitives.ciphers.aead import AESGCM
269 cipher = AESGCM(bytes.fromhex(args.enc_key))
270 # Use 12 bytes for nonce per recommendation
271 nonce = os.urandom(12)
272 out = cipher.encrypt(nonce, img, None)
273 ciphertext = out[:-16]
274 # Authentication Tag is always the last 16 bytes
275 tag = out[-16:]
Sumit Garg2de17fd2019-10-23 12:47:24 +0530276
Donald Chanc45a84b2022-01-01 22:32:45 +0000277 enc_algo = 0x40000810 # TEE_ALG_AES_GCM
278 flags = enc_key_type[args.enc_key_type]
Sumit Garg2de17fd2019-10-23 12:47:24 +0530279 ehdr = struct.pack('<IIHH',
Donald Chan169eac12021-10-24 14:22:54 -0700280 enc_algo, flags, len(nonce), len(tag))
Sumit Garg2de17fd2019-10-23 12:47:24 +0530281
Jens Wiklandercd5cf432017-11-28 16:59:15 +0100282 h.update(shdr)
283 h.update(shdr_uuid)
284 h.update(shdr_version)
Sumit Garg2de17fd2019-10-23 12:47:24 +0530285 if args.enc_key:
286 h.update(ehdr)
Donald Chan169eac12021-10-24 14:22:54 -0700287 h.update(nonce)
Sumit Garg2de17fd2019-10-23 12:47:24 +0530288 h.update(tag)
Jens Wiklandercd5cf432017-11-28 16:59:15 +0100289 h.update(img)
Donald Chan169eac12021-10-24 14:22:54 -0700290 img_digest = h.finalize()
Jens Wiklanderbc420742015-05-05 14:59:15 +0200291
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200292 def write_image_with_signature(sig):
293 with open(args.outf, 'wb') as f:
294 f.write(shdr)
295 f.write(img_digest)
296 f.write(sig)
297 f.write(shdr_uuid)
298 f.write(shdr_version)
Sumit Garg2de17fd2019-10-23 12:47:24 +0530299 if args.enc_key:
300 f.write(ehdr)
Donald Chan169eac12021-10-24 14:22:54 -0700301 f.write(nonce)
Sumit Garg2de17fd2019-10-23 12:47:24 +0530302 f.write(tag)
303 f.write(ciphertext)
304 else:
305 f.write(img)
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200306
Sumit Garg2de17fd2019-10-23 12:47:24 +0530307 def sign_encrypt_ta():
Donald Chan169eac12021-10-24 14:22:54 -0700308 if not isinstance(key, rsa.RSAPrivateKey):
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200309 logger.error('Provided key cannot be used for signing, ' +
310 'please use offline-signing mode.')
Markus S. Wamser6ff2e3f2019-08-02 14:48:46 +0200311 sys.exit(1)
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200312 else:
Donald Chan169eac12021-10-24 14:22:54 -0700313 if args.algo == 'TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256':
314 sig = key.sign(
315 img_digest,
316 padding.PSS(
317 mgf=padding.MGF1(chosen_hash),
318 salt_length=digest_len
319 ),
320 utils.Prehashed(chosen_hash)
321 )
322 elif args.algo == 'TEE_ALG_RSASSA_PKCS1_V1_5_SHA256':
323 sig = key.sign(
324 img_digest,
325 padding.PKCS1v15(),
326 utils.Prehashed(chosen_hash)
327 )
328
Volodymyr Babchuk90ad2452019-08-21 21:00:32 +0300329 if len(sig) != sig_len:
330 raise Exception(("Actual signature length is not equal to ",
331 "the computed one: {} != {}").
332 format(len(sig), sig_len))
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200333 write_image_with_signature(sig)
334 logger.info('Successfully signed application.')
335
336 def generate_digest():
337 with open(args.digf, 'wb+') as digfile:
338 digfile.write(base64.b64encode(img_digest))
339
340 def stitch_ta():
341 try:
342 with open(args.sigf, 'r') as sigfile:
343 sig = base64.b64decode(sigfile.read())
344 except IOError:
Markus S. Wamser6ff2e3f2019-08-02 14:48:46 +0200345 if not os.path.exists(args.digf):
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200346 generate_digest()
347 logger.error('No signature file found. Please sign\n %s\n' +
348 'offline and place the signature at \n %s\n' +
349 'or pass a different location ' +
350 'using the --sig argument.\n',
351 args.digf, args.sigf)
Markus S. Wamser6ff2e3f2019-08-02 14:48:46 +0200352 sys.exit(1)
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200353 else:
Mingyuan Xiangcf3d6ac2020-09-17 19:01:02 +0800354 try:
Donald Chan169eac12021-10-24 14:22:54 -0700355 if args.algo == 'TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256':
356 key.verify(
357 sig,
358 img_digest,
359 padding.PSS(
360 mgf=padding.MGF1(chosen_hash),
361 salt_length=digest_len
362 ),
363 utils.Prehashed(chosen_hash)
364 )
365 elif args.algo == 'TEE_ALG_RSASSA_PKCS1_V1_5_SHA256':
366 key.verify(
367 sig,
368 img_digest,
369 padding.PKCS1v15(),
370 utils.Prehashed(chosen_hash)
371 )
372 except exceptions.InvalidSignature:
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200373 logger.error('Verification failed, ignoring given signature.')
Markus S. Wamser6ff2e3f2019-08-02 14:48:46 +0200374 sys.exit(1)
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200375
Donald Chan169eac12021-10-24 14:22:54 -0700376 write_image_with_signature(sig)
377 logger.info('Successfully applied signature.')
378
Donald Chan51eee1e2022-01-11 18:54:25 +0000379 def verify_ta():
380 # Extract header
381 [magic,
382 img_type,
383 img_size,
384 algo_value,
385 digest_len,
386 sig_len] = struct.unpack('<IIIIHH', img[:SHDR_SIZE])
387
388 # Extract digest and signature
389 start, end = SHDR_SIZE, SHDR_SIZE + digest_len
390 digest = img[start:end]
391
392 start, end = end, SHDR_SIZE + digest_len + sig_len
393 signature = img[start:end]
394
395 # Extract UUID and TA version
396 start, end = end, end + 16 + 4
397 [uuid, ta_version] = struct.unpack('<16sI', img[start:end])
398
399 if magic != SHDR_MAGIC:
400 raise Exception("Unexpected magic: 0x{:08x}".format(magic))
401
402 if img_type != SHDR_BOOTSTRAP_TA:
403 raise Exception("Unsupported image type: {}".format(img_type))
404
405 if algo_value not in algo.values():
406 raise Exception('Unrecognized algorithm: 0x{:08x}'
407 .format(algo_value))
408
409 # Verify signature against hash digest
410 if algo_value == 0x70414930:
411 key.verify(
412 signature,
413 digest,
414 padding.PSS(
415 mgf=padding.MGF1(chosen_hash),
416 salt_length=digest_len
417 ),
418 utils.Prehashed(chosen_hash)
419 )
420 else:
421 key.verify(
422 signature,
423 digest,
424 padding.PKCS1v15(),
425 utils.Prehashed(chosen_hash)
426 )
427
428 h = hashes.Hash(chosen_hash, default_backend())
429
430 # sizeof(struct shdr)
431 h.update(img[:SHDR_SIZE])
432
433 # sizeof(struct shdr_bootstrap_ta)
434 h.update(img[start:end])
435
436 # raw image
437 start = end
438 end += img_size
439 h.update(img[start:end])
440
441 if digest != h.finalize():
442 raise Exception('Hash digest does not match')
443
444 logger.info('Trusted application is correctly verified.')
445
Markus S. Wamser6ff2e3f2019-08-02 14:48:46 +0200446 # dispatch command
447 {
Sumit Garg2de17fd2019-10-23 12:47:24 +0530448 'sign-enc': sign_encrypt_ta,
Markus S. Wamser1cdd95a2019-04-30 12:03:12 +0200449 'digest': generate_digest,
450 'generate-digest': generate_digest,
451 'stitch': stitch_ta,
Donald Chan51eee1e2022-01-11 18:54:25 +0000452 'stitch-ta': stitch_ta,
453 'verify': verify_ta,
Sumit Garg2de17fd2019-10-23 12:47:24 +0530454 }.get(args.command, 'sign_encrypt_ta')()
Jens Wiklandercd5cf432017-11-28 16:59:15 +0100455
Jens Wiklanderbc420742015-05-05 14:59:15 +0200456
457if __name__ == "__main__":
Jens Wiklandercd5cf432017-11-28 16:59:15 +0100458 main()