1#! /usr/bin/env python3
2#
3# -----------------------------------------------------------------------------
4# Copyright (c) 2020-2022, Arm Limited. All rights reserved.
5#
6# SPDX-License-Identifier: BSD-3-Clause
7#
8# -----------------------------------------------------------------------------
9
10import re
11import os
12import sys
13import click
14
15# Add the cwd to the path so that if there is a version of imgtool in there then
16# it gets used over the system imgtool. Used so that imgtool from upstream
17# mcuboot is preferred over system imgtool
18cwd = os.getcwd()
19sys.path = [cwd] + sys.path
20import imgtool
21import imgtool.main
22
23parser_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../'))
24sys.path.append(parser_path)
25import macro_parser
26
27sign_bin_size_re = re.compile(r"^\s*RE_SIGN_BIN_SIZE\s*=\s*(.*)")
28load_addr_re = re.compile(r"^\s*RE_IMAGE_LOAD_ADDRESS\s*=\s*(.*)")
29rom_fixed_re = re.compile(r"^\s*RE_IMAGE_ROM_FIXED\s*=\s*(.*)")
30
31#This works around Python 2 and Python 3 handling character encodings
32#differently. More information about this issue at
33#https://click.palletsprojects.com/en/5.x/python3
34os.environ['LC_ALL'] = 'C.UTF-8'
35os.environ['LANG'] = 'C.UTF-8'
36
37@click.argument('outfile')
38@click.argument('infile')
39@click.option('-R', '--erased-val', type=click.Choice(['0', '0xff']),
40              required=False, help='The value that is read back from erased '
41                                   'flash.')
42@click.option('-x', '--hex-addr', type=imgtool.main.BasedIntParamType(),
43              required=False, help='Adjust address in hex output file.')
44@click.option('--measured-boot-record', default=False, is_flag=True, help='Add '
45              'CBOR encoded measured boot record to the image manifest.')
46@click.option('--save-enctlv', default=False, is_flag=True,
47              help='When upgrading, save encrypted key TLVs instead of plain '
48                   'keys. Enable when BOOT_SWAP_SAVE_ENCTLV config option '
49                   'was set.')
50@click.option('-E', '--encrypt', metavar='filename',
51              help='Encrypt image using the provided public key')
52@click.option('-e', '--endian', type=click.Choice(['little', 'big']),
53              default='little', help="Select little or big endian")
54@click.option('--overwrite-only', default=False, is_flag=True,
55              help='Use overwrite-only instead of swap upgrades')
56@click.option('-M', '--max-sectors', type=int,
57              help='When padding allow for this amount of sectors (defaults '
58                   'to 128)')
59@click.option('--confirm', default=False, is_flag=True,
60              help='When padding the image, mark it as confirmed')
61@click.option('--pad', default=False, is_flag=True,
62              help='Pad image to the size determined by --layout, adding '
63                   'trailer magic')
64@click.option('-l', '--layout', help='The file containing the macros of the '
65                                     'slot sizes')
66@click.option('--pad-header', default=False, is_flag=True,
67              help='Adds --erased-val (defaults to 0xff) --header-size times '
68                   'at the beginning of the image')
69@click.option('-H', '--header-size',
70              callback=imgtool.main.validate_header_size,
71              type=imgtool.main.BasedIntParamType(), required=True)
72@click.option('-d', '--dependencies', callback=imgtool.main.get_dependencies,
73              required=False, help='''Add dependence on another image, format:
74                                   "(<image_ID>,<image_version>), ... "''')
75@click.option('-s', '--security-counter',
76              callback=imgtool.main.validate_security_counter,
77              help='Specify the value of security counter. Use the `auto` '
78              'keyword to automatically generate it from the image version.')
79@click.option('-L', '--encrypt-keylen', type=click.Choice(['128', '256']),
80              default='128',
81              help='Specify the value of encrypt key length. Default 128.')
82@click.option('-v', '--version', callback=imgtool.main.validate_version,
83              required=True)
84@click.option('--align', type=click.Choice(['1', '2', '4', '8', '16', '32']),
85              required=True)
86@click.option('--public-key-format', type=click.Choice(['hash', 'full']),
87              default='hash', help='In what format to add the public key to '
88              'the image manifest: full key or hash of the key.')
89@click.option('-k', '--key', metavar='filename')
90@click.command(help='''Create a signed or unsigned image\n
91               INFILE and OUTFILE are parsed as Intel HEX if the params have
92               .hex extension, otherwise binary format is used''')
93def wrap(key, align, version, header_size, pad_header, layout, pad, confirm,
94         max_sectors, overwrite_only, endian, encrypt, infile, outfile,
95         dependencies, hex_addr, erased_val, save_enctlv, public_key_format,
96         security_counter, encrypt_keylen, measured_boot_record):
97
98    slot_size = macro_parser.evaluate_macro(layout, sign_bin_size_re, 0, 1)
99    load_addr = macro_parser.evaluate_macro(layout, load_addr_re, 0, 1)
100    rom_fixed = macro_parser.evaluate_macro(layout, rom_fixed_re, 0, 1)
101
102    if measured_boot_record:
103        if "_s" in layout:
104            record_sw_type = "SPE"
105        elif "_ns" in layout:
106            record_sw_type = "NSPE"
107        else:
108            record_sw_type = "NSPE_SPE"
109    else:
110        record_sw_type = None
111
112    if int(align) <= 8 :
113        #default behaviour for max_align
114        max_align=8
115    else:
116        #max_align must be set to align
117        max_align=align
118
119    img = imgtool.image.Image(version=imgtool.version.decode_version(version),
120                              header_size=header_size, pad_header=pad_header,
121                              pad=pad, confirm=confirm, align=int(align),
122                              slot_size=slot_size, max_sectors=max_sectors,
123                              overwrite_only=overwrite_only, endian=endian,
124                              load_addr=load_addr, rom_fixed=rom_fixed,
125                              erased_val=erased_val,
126                              save_enctlv=save_enctlv,
127                              security_counter=security_counter,
128                              max_align=max_align)
129
130    img.load(infile)
131    key = imgtool.main.load_key(key) if key else None
132    enckey = imgtool.main.load_key(encrypt) if encrypt else None
133    if enckey and key:
134        if (isinstance(key, imgtool.keys.RSA) and
135           not isinstance(enckey, imgtool.keys.RSAPublic)):
136            # FIXME
137            raise click.UsageError("Signing and encryption must use the same "
138                                   "type of key")
139    img.create(key, public_key_format, enckey, dependencies, record_sw_type,
140               None, encrypt_keylen=int(encrypt_keylen))
141    img.save(outfile, hex_addr)
142
143
144if __name__ == '__main__':
145    wrap()
146