xref: /optee_os/scripts/sign_rproc_fw.py (revision 39a4a0ee33c737fecd3bef73631e7900dd591da0)
1e8ef5353SArnaud Pouliquen#!/usr/bin/env python3
2e8ef5353SArnaud Pouliquen# SPDX-License-Identifier: BSD-2-Clause
3e8ef5353SArnaud Pouliquen#
4e8ef5353SArnaud Pouliquen# Copyright (C) 2023, STMicroelectronics
5e8ef5353SArnaud Pouliquen#
6e8ef5353SArnaud Pouliquen
7e8ef5353SArnaud Pouliquentry:
8e8ef5353SArnaud Pouliquen    from elftools.elf.elffile import ELFFile
9e8ef5353SArnaud Pouliquen    from elftools.elf.sections import SymbolTableSection
10e8ef5353SArnaud Pouliquen    from elftools.elf.enums import ENUM_P_TYPE_ARM
11e8ef5353SArnaud Pouliquen    from elftools.elf.enums import *
12e8ef5353SArnaud Pouliquenexcept ImportError:
13e8ef5353SArnaud Pouliquen    print("""
14e8ef5353SArnaud Pouliquen***
15e8ef5353SArnaud PouliquenERROR: "pyelftools" python module is not installed or version < 0.25.
16e8ef5353SArnaud Pouliquen***
17e8ef5353SArnaud Pouliquen""")
18e8ef5353SArnaud Pouliquen    raise
19e8ef5353SArnaud Pouliquen
20e8ef5353SArnaud Pouliquentry:
21e8ef5353SArnaud Pouliquen    from Cryptodome.Hash import SHA256
22e8ef5353SArnaud Pouliquen    from Cryptodome.Signature import pkcs1_15
23e8ef5353SArnaud Pouliquen    from Cryptodome.PublicKey import RSA
24e8ef5353SArnaud Pouliquen    from Cryptodome.Signature import DSS
25e8ef5353SArnaud Pouliquen    from Cryptodome.PublicKey import ECC
26e8ef5353SArnaud Pouliquenexcept ImportError:
27e8ef5353SArnaud Pouliquen    print("""
28e8ef5353SArnaud Pouliquen            ***
29e8ef5353SArnaud Pouliquen            ERROR: "pycryptodomex" python module should be installed.
30e8ef5353SArnaud Pouliquen            ***
31e8ef5353SArnaud Pouliquen        """)
32e8ef5353SArnaud Pouliquen    raise
33e8ef5353SArnaud Pouliquen
34e8ef5353SArnaud Pouliquenimport os
35e8ef5353SArnaud Pouliquenimport sys
36e8ef5353SArnaud Pouliquenimport struct
37e8ef5353SArnaud Pouliquenimport logging
38e8ef5353SArnaud Pouliquenimport binascii
39e8ef5353SArnaud Pouliquen
40e8ef5353SArnaud Pouliquen#  Generated file structure:
41e8ef5353SArnaud Pouliquen#
42e8ef5353SArnaud Pouliquen#                   -----+-------------+
43e8ef5353SArnaud Pouliquen#                  /     |    Magic    |  32-bit word, magic value equal to
44e8ef5353SArnaud Pouliquen#                 /      +-------------+  0x3543A468
45e8ef5353SArnaud Pouliquen#                /       +-------------+
46e8ef5353SArnaud Pouliquen#               /        |   version   |  32-bit word, version of the format
47e8ef5353SArnaud Pouliquen#              /         +-------------+
48e8ef5353SArnaud Pouliquen# +-----------+          +-------------+
49e8ef5353SArnaud Pouliquen# |   Header  |          |  TLV size   |  32-bit word, size of the TLV
50e8ef5353SArnaud Pouliquen# +-----------+          +-------------+  (aligned on 64-bit), in bytes.
51e8ef5353SArnaud Pouliquen#              \         +-------------+
52e8ef5353SArnaud Pouliquen#               \        |  sign size  |  32-bit word, size of the signature
53e8ef5353SArnaud Pouliquen#                \       +-------------+  (aligned on 64-bit), in bytes.
54e8ef5353SArnaud Pouliquen#                 \      +-------------+
55e8ef5353SArnaud Pouliquen#                  \     | images size |  32-bit word, size of the images to
56e8ef5353SArnaud Pouliquen#                   -----+-------------+  load (aligned on 64-bit), in bytes.
57e8ef5353SArnaud Pouliquen#
58e8ef5353SArnaud Pouliquen#                        +-------------+  Information used to authenticate the
59e8ef5353SArnaud Pouliquen#                        |     TLV     |  images and boot the remote processor,
60e8ef5353SArnaud Pouliquen#                        |             |  stored in Type-Length-Value format.
61e8ef5353SArnaud Pouliquen#                        +-------------+  'Type' and 'Length' are 32-bit words.
62e8ef5353SArnaud Pouliquen#
63e8ef5353SArnaud Pouliquen#                        +-------------+
64e8ef5353SArnaud Pouliquen#                        | Signature   |   Signature of the header and the TLV.
65e8ef5353SArnaud Pouliquen#                        +-------------+
66e8ef5353SArnaud Pouliquen#
67e8ef5353SArnaud Pouliquen#                        +-------------+
68e8ef5353SArnaud Pouliquen#                        |   Firmware  |
69e8ef5353SArnaud Pouliquen#                        |    image 1  |
70e8ef5353SArnaud Pouliquen#                        +-------------+
71e8ef5353SArnaud Pouliquen#                               ...
72e8ef5353SArnaud Pouliquen#                        +-------------+
73e8ef5353SArnaud Pouliquen#                        |   Firmware  |
74e8ef5353SArnaud Pouliquen#                        |    image n  |
75e8ef5353SArnaud Pouliquen#                        +-------------+
76e8ef5353SArnaud Pouliquen
77e8ef5353SArnaud Pouliquen# Generic type definitions
78e8ef5353SArnaud PouliquenTLV_TYPES = {
79e8ef5353SArnaud Pouliquen        'SIGNTYPE': 0x00000001,   # algorithm used for signature
80e8ef5353SArnaud Pouliquen        'HASHTYPE': 0x00000002,   # algorithm used for computing segment's hash
81e8ef5353SArnaud Pouliquen        'NUM_IMG':  0x00000003,   # number of images to load
82e8ef5353SArnaud Pouliquen        'IMGTYPE':  0x00000004,   # array of type of images to load
83e8ef5353SArnaud Pouliquen        'IMGSIZE':  0x00000005,   # array of the size of the images to load
84e8ef5353SArnaud Pouliquen        'HASHTABLE': 0x000000010,  # segment hash table for authentication
85e8ef5353SArnaud Pouliquen        'PKEYINFO': 0x0000000011,  # information to retrieve signature key
86e8ef5353SArnaud Pouliquen}
87e8ef5353SArnaud Pouliquen
88e8ef5353SArnaud PouliquenGENERIC_TLV_TYPE_RANGE = range(0x00000000, 0x00010000)
89e8ef5353SArnaud PouliquenPLATFORM_TLV_TYPE_RANGE = range(0x00010000, 0x00020000)
90e8ef5353SArnaud Pouliquen
91e8ef5353SArnaud PouliquenHEADER_MAGIC = 0x3543A468
92e8ef5353SArnaud Pouliquen
93e8ef5353SArnaud Pouliquenlogging.basicConfig(stream=sys.stderr, level=logging.INFO)
94e8ef5353SArnaud Pouliquen
95e8ef5353SArnaud PouliquenENUM_HASH_TYPE = dict(
96e8ef5353SArnaud Pouliquen    SHA256=1,
97e8ef5353SArnaud Pouliquen)
98e8ef5353SArnaud Pouliquen
99e8ef5353SArnaud PouliquenENUM_SIGNATURE_TYPE = dict(
100e8ef5353SArnaud Pouliquen    RSA=1,
101e8ef5353SArnaud Pouliquen    ECC=2,
102e8ef5353SArnaud Pouliquen)
103e8ef5353SArnaud Pouliquen
104e8ef5353SArnaud PouliquenENUM_BINARY_TYPE = dict(
105e8ef5353SArnaud Pouliquen    ELF=1,
106e8ef5353SArnaud Pouliquen)
107e8ef5353SArnaud Pouliquen
108e8ef5353SArnaud Pouliquen
109e8ef5353SArnaud Pouliquendef dump_buffer(buf, step=16, name="", logger=logging.debug, indent=""):
110e8ef5353SArnaud Pouliquen    logger("%s%s:" % (indent, name))
111e8ef5353SArnaud Pouliquen    for i in range(0, len(buf), step):
112e8ef5353SArnaud Pouliquen        logger("%s    " % (indent) + " ".
113e8ef5353SArnaud Pouliquen               join(["%02X" % c for c in buf[i:i+step]]))
114e8ef5353SArnaud Pouliquen    logger("\n")
115e8ef5353SArnaud Pouliquen
116e8ef5353SArnaud Pouliquen
117e8ef5353SArnaud Pouliquenclass TLV():
118e8ef5353SArnaud Pouliquen    def __init__(self):
119e8ef5353SArnaud Pouliquen        self.buf = bytearray()
120e8ef5353SArnaud Pouliquen        self.tlvs = {}
121e8ef5353SArnaud Pouliquen
122e8ef5353SArnaud Pouliquen    def add(self, kind, payload):
123e8ef5353SArnaud Pouliquen        """
124e8ef5353SArnaud Pouliquen        Add a TLV record. Argument type is either the type scalar ID or a
125e8ef5353SArnaud Pouliquen        matching string defined in TLV_TYPES.
126e8ef5353SArnaud Pouliquen        """
127e8ef5353SArnaud Pouliquen        if isinstance(kind, int):
128e8ef5353SArnaud Pouliquen            buf = struct.pack('II', kind, len(payload))
129e8ef5353SArnaud Pouliquen        else:
130e8ef5353SArnaud Pouliquen            buf = struct.pack('II', TLV_TYPES[kind], len(payload))
131e8ef5353SArnaud Pouliquen
132e8ef5353SArnaud Pouliquen        # Ensure that each TLV is 64-bit aligned
133e8ef5353SArnaud Pouliquen        align_64b = (len(payload) + len(buf)) % 8
134e8ef5353SArnaud Pouliquen        self.buf += buf
135e8ef5353SArnaud Pouliquen        self.buf += payload
136e8ef5353SArnaud Pouliquen        if align_64b:
137e8ef5353SArnaud Pouliquen            self.buf += bytearray(8 - align_64b)
138e8ef5353SArnaud Pouliquen
139e8ef5353SArnaud Pouliquen    def add_plat_tlv(self, cust_tlv):
140e8ef5353SArnaud Pouliquen        # Get list of custom protected TLVs from the command-line
141e8ef5353SArnaud Pouliquen        for tlv in cust_tlv:
142e8ef5353SArnaud Pouliquen            type_id = int(tlv[0], 0)
143e8ef5353SArnaud Pouliquen
144e8ef5353SArnaud Pouliquen            if type_id not in PLATFORM_TLV_TYPE_RANGE:
145e8ef5353SArnaud Pouliquen                raise Exception('TLV %s not in range' % hex(type_id))
146e8ef5353SArnaud Pouliquen
147e8ef5353SArnaud Pouliquen            value = tlv[1]
148e8ef5353SArnaud Pouliquen            if value.startswith('0x'):
149e8ef5353SArnaud Pouliquen                int_val = int(value[2:], 16)
150e8ef5353SArnaud Pouliquen                self.tlvs[type_id] = int_val.to_bytes(4, 'little')
151e8ef5353SArnaud Pouliquen            else:
152e8ef5353SArnaud Pouliquen                self.tlvs[type_id] = value.encode('utf-8')
153e8ef5353SArnaud Pouliquen
154e8ef5353SArnaud Pouliquen        if self.tlvs is not None:
155e8ef5353SArnaud Pouliquen            for type_id, value in self.tlvs.items():
156e8ef5353SArnaud Pouliquen                self.add(type_id, value)
157e8ef5353SArnaud Pouliquen
158e8ef5353SArnaud Pouliquen    def get(self):
159e8ef5353SArnaud Pouliquen        """
160e8ef5353SArnaud Pouliquen        Get a byte-array that concatenates all the TLV added.
161e8ef5353SArnaud Pouliquen        """
162e8ef5353SArnaud Pouliquen        if len(self.buf) == 0:
163e8ef5353SArnaud Pouliquen            return bytes()
164e8ef5353SArnaud Pouliquen        return bytes(self.buf)
165e8ef5353SArnaud Pouliquen
166e8ef5353SArnaud Pouliquen
167e8ef5353SArnaud Pouliquenclass RSA_Signature(object):
168e8ef5353SArnaud Pouliquen
169e8ef5353SArnaud Pouliquen    def __init__(self, key):
170e8ef5353SArnaud Pouliquen        self._hasher = SHA256.new()
171e8ef5353SArnaud Pouliquen        self.signer = pkcs1_15.new(key)
172e8ef5353SArnaud Pouliquen
173e8ef5353SArnaud Pouliquen    def hash_compute(self, segment):
174e8ef5353SArnaud Pouliquen        self._hasher.update(segment)
175e8ef5353SArnaud Pouliquen
176e8ef5353SArnaud Pouliquen    def sign(self):
177e8ef5353SArnaud Pouliquen        return self.signer.sign(self._hasher)
178e8ef5353SArnaud Pouliquen
179e8ef5353SArnaud Pouliquen
180e8ef5353SArnaud Pouliquenclass ECC_Signature(object):
181e8ef5353SArnaud Pouliquen
182e8ef5353SArnaud Pouliquen    def __init__(self, key):
183e8ef5353SArnaud Pouliquen        self._hasher = SHA256.new()
184e8ef5353SArnaud Pouliquen        self.signer = DSS.new(key, 'fips-186-3')
185e8ef5353SArnaud Pouliquen
186e8ef5353SArnaud Pouliquen    def hash_compute(self, segment):
187e8ef5353SArnaud Pouliquen        self._hasher.update(segment)
188e8ef5353SArnaud Pouliquen
189e8ef5353SArnaud Pouliquen    def sign(self):
190e8ef5353SArnaud Pouliquen        return self.signer.sign(self._hasher)
191e8ef5353SArnaud Pouliquen
192e8ef5353SArnaud Pouliquen
193e8ef5353SArnaud PouliquenSignature = {
194e8ef5353SArnaud Pouliquen        1: RSA_Signature,
195e8ef5353SArnaud Pouliquen        2: ECC_Signature,
196e8ef5353SArnaud Pouliquen}
197e8ef5353SArnaud Pouliquen
198e8ef5353SArnaud Pouliquen
199e8ef5353SArnaud Pouliquenclass SegmentHashStruct:
200e8ef5353SArnaud Pouliquen    pass
201e8ef5353SArnaud Pouliquen
202e8ef5353SArnaud Pouliquen
203e8ef5353SArnaud Pouliquenclass SegmentHash(object):
204e8ef5353SArnaud Pouliquen    '''
205e8ef5353SArnaud Pouliquen        Hash table based on Elf program segments
206e8ef5353SArnaud Pouliquen    '''
207e8ef5353SArnaud Pouliquen    def __init__(self, img):
208e8ef5353SArnaud Pouliquen        self._num_segments = img.num_segments()
209e8ef5353SArnaud Pouliquen        self._pack_fmt = '<%dL' % 8
210e8ef5353SArnaud Pouliquen        self.img = img
211e8ef5353SArnaud Pouliquen        self.hashProgTable = bytes()
212e8ef5353SArnaud Pouliquen        self._offset = 0
213e8ef5353SArnaud Pouliquen
214e8ef5353SArnaud Pouliquen    def get_table(self):
215e8ef5353SArnaud Pouliquen        '''
216e8ef5353SArnaud Pouliquen            Create a segment hash table containing for each segment:
217e8ef5353SArnaud Pouliquen                - the segments header
218e8ef5353SArnaud Pouliquen                - a hash of the segment
219e8ef5353SArnaud Pouliquen        '''
220e8ef5353SArnaud Pouliquen        h = SHA256.new()
221e8ef5353SArnaud Pouliquen        seg = SegmentHashStruct()
222e8ef5353SArnaud Pouliquen        self.size = (h.digest_size + 32) * self._num_segments
223e8ef5353SArnaud Pouliquen        logging.debug("hash section size %d" % self.size)
224e8ef5353SArnaud Pouliquen        del h
225e8ef5353SArnaud Pouliquen        self.buf = bytearray(self.size)
226e8ef5353SArnaud Pouliquen        self._bufview_ = memoryview(self.buf)
227e8ef5353SArnaud Pouliquen
228e8ef5353SArnaud Pouliquen        for i in range(self._num_segments):
229e8ef5353SArnaud Pouliquen            h = SHA256.new()
230e8ef5353SArnaud Pouliquen            segment = self.img.get_segment(i)
231e8ef5353SArnaud Pouliquen            seg.header = self.img.get_segment(i).header
232e8ef5353SArnaud Pouliquen            logging.debug("compute hash for segment offset %s" % seg.header)
233e8ef5353SArnaud Pouliquen            h.update(segment.data())
234e8ef5353SArnaud Pouliquen            seg.hash = h.digest()
235e8ef5353SArnaud Pouliquen            logging.debug("hash computed: %s" % seg.hash)
236e8ef5353SArnaud Pouliquen            del h
237e8ef5353SArnaud Pouliquen            struct.pack_into('<I', self._bufview_, self._offset,
238e8ef5353SArnaud Pouliquen                             ENUM_P_TYPE_ARM[seg.header.p_type])
239e8ef5353SArnaud Pouliquen            self._offset += 4
240e8ef5353SArnaud Pouliquen            struct.pack_into('<7I', self._bufview_, self._offset,
241e8ef5353SArnaud Pouliquen                             seg.header.p_offset, seg.header.p_vaddr,
242e8ef5353SArnaud Pouliquen                             seg.header.p_paddr, seg.header.p_filesz,
243e8ef5353SArnaud Pouliquen                             seg.header.p_memsz, seg.header.p_flags,
244e8ef5353SArnaud Pouliquen                             seg.header.p_align)
245e8ef5353SArnaud Pouliquen            self._offset += 28
246e8ef5353SArnaud Pouliquen            struct.pack_into('<32B', self._bufview_, self._offset, *seg.hash)
247e8ef5353SArnaud Pouliquen            self._offset += 32
248e8ef5353SArnaud Pouliquen        dump_buffer(self.buf, name='hash table', indent="\t")
249e8ef5353SArnaud Pouliquen        return self.buf
250e8ef5353SArnaud Pouliquen
251e8ef5353SArnaud Pouliquen
252e8ef5353SArnaud Pouliquenclass ImageHeader(object):
253e8ef5353SArnaud Pouliquen    '''
254e8ef5353SArnaud Pouliquen        Image header
255e8ef5353SArnaud Pouliquen    '''
256e8ef5353SArnaud Pouliquen
257e8ef5353SArnaud Pouliquen    magic = 'HELF'   # SHDR_MAGIC
258e8ef5353SArnaud Pouliquen    version = 1
259e8ef5353SArnaud Pouliquen
260e8ef5353SArnaud Pouliquen    MAGIC_OFFSET = 0
261e8ef5353SArnaud Pouliquen    VERSION_OFFSET = 4
262e8ef5353SArnaud Pouliquen    SIGN_LEN_OFFSET = 8
263e8ef5353SArnaud Pouliquen    IMG_LEN_OFFSET = 12
264e8ef5353SArnaud Pouliquen    TLV_LEN_OFFSET = 16
265e8ef5353SArnaud Pouliquen    PTLV_LEN_OFFSET = 20
266e8ef5353SArnaud Pouliquen
267e8ef5353SArnaud Pouliquen    def __init__(self):
268e8ef5353SArnaud Pouliquen        self.size = 56
269e8ef5353SArnaud Pouliquen
270e8ef5353SArnaud Pouliquen        self.magic = HEADER_MAGIC
271e8ef5353SArnaud Pouliquen        self.version = 1
272e8ef5353SArnaud Pouliquen        self.tlv_length = 0
273e8ef5353SArnaud Pouliquen        self.sign_length = 0
274e8ef5353SArnaud Pouliquen        self.img_length = 0
275e8ef5353SArnaud Pouliquen
276e8ef5353SArnaud Pouliquen        self.shdr = struct.pack('<IIIII',
277e8ef5353SArnaud Pouliquen                                self.magic, self.version,
278e8ef5353SArnaud Pouliquen                                self.tlv_length, self.sign_length,
279e8ef5353SArnaud Pouliquen                                self.img_length)
280e8ef5353SArnaud Pouliquen
281e8ef5353SArnaud Pouliquen    def dump(self):
282e8ef5353SArnaud Pouliquen        logging.debug("\tMAGIC\t\t= %08X" % (self.magic))
283e8ef5353SArnaud Pouliquen        logging.debug("\tHEADER_VERSION\t= %08X" % (self.version))
284e8ef5353SArnaud Pouliquen        logging.debug("\tTLV_LENGTH\t= %08X" % (self.tlv_length))
285e8ef5353SArnaud Pouliquen        logging.debug("\tSIGN_LENGTH\t= %08X" % (self.sign_length))
286e8ef5353SArnaud Pouliquen        logging.debug("\tIMAGE_LENGTH\t= %08X" % (self.img_length))
287e8ef5353SArnaud Pouliquen
288e8ef5353SArnaud Pouliquen    def get_packed(self):
289e8ef5353SArnaud Pouliquen        return struct.pack('<IIIII',
290e8ef5353SArnaud Pouliquen                           self.magic, self.version,
291e8ef5353SArnaud Pouliquen                           self.tlv_length, self.sign_length, self.img_length)
292e8ef5353SArnaud Pouliquen
293e8ef5353SArnaud Pouliquen
294e8ef5353SArnaud Pouliquendef get_args(logger):
295e8ef5353SArnaud Pouliquen    from argparse import ArgumentParser, RawDescriptionHelpFormatter
296e8ef5353SArnaud Pouliquen    import textwrap
297e8ef5353SArnaud Pouliquen
298e8ef5353SArnaud Pouliquen    parser = ArgumentParser(
299e8ef5353SArnaud Pouliquen        description='Sign a remote processor firmware loadable by OP-TEE.',
300e8ef5353SArnaud Pouliquen        usage='\n   %(prog)s [ arguments ]\n\n'
301e8ef5353SArnaud Pouliquen        '   Generate signed loadable binary \n' +
302e8ef5353SArnaud Pouliquen        '   Takes arguments --in, --out --key\n' +
303e8ef5353SArnaud Pouliquen        '   %(prog)s --help  show available arguments\n\n')
304e8ef5353SArnaud Pouliquen    parser.add_argument('--in', required=True, dest='in_file',
305e8ef5353SArnaud Pouliquen                        help='Name of firmware input file ' +
306e8ef5353SArnaud Pouliquen                             '(can be used multiple times)', action='append')
307e8ef5353SArnaud Pouliquen    parser.add_argument('--out', required=True, dest='out_file',
308e8ef5353SArnaud Pouliquen                        help='Name of the signed firmware output file')
309e8ef5353SArnaud Pouliquen    parser.add_argument('--key', required=True,
310e8ef5353SArnaud Pouliquen                        help='Name of signing key file',
311e8ef5353SArnaud Pouliquen                        dest='key_file')
312e8ef5353SArnaud Pouliquen    parser.add_argument('--key_info', required=False,
313e8ef5353SArnaud Pouliquen                        help='Name file containing extra key information',
314e8ef5353SArnaud Pouliquen                        dest='key_info')
315e8ef5353SArnaud Pouliquen    parser.add_argument('--key_type', required=False,
316e8ef5353SArnaud Pouliquen                        help='Type of signing key: should be RSA or ECC',
317e8ef5353SArnaud Pouliquen                        default='RSA',
318e8ef5353SArnaud Pouliquen                        dest='key_type')
319*39a4a0eeSArnaud Pouliquen    parser.add_argument('--key_pwd', required=False,
320*39a4a0eeSArnaud Pouliquen                        help='passphrase for the private key decryption',
321*39a4a0eeSArnaud Pouliquen                        dest='key_pwd')
322e8ef5353SArnaud Pouliquen    parser.add_argument('--plat-tlv', required=False, nargs=2,
323e8ef5353SArnaud Pouliquen                        metavar=("ID", "value"), action='append',
324e8ef5353SArnaud Pouliquen                        help='Platform TLV that will be placed into image '
325e8ef5353SArnaud Pouliquen                             'plat_tlv area. Add "0x" prefix to interpret '
326e8ef5353SArnaud Pouliquen                             'the value as an integer, otherwise it will be '
327e8ef5353SArnaud Pouliquen                             'interpreted as a string. Option can be used '
328e8ef5353SArnaud Pouliquen                             'multiple times to add multiple TLVs.',
329e8ef5353SArnaud Pouliquen                        default=[], dest='plat_tlv')
330e8ef5353SArnaud Pouliquen
331e8ef5353SArnaud Pouliquen    parsed = parser.parse_args()
332e8ef5353SArnaud Pouliquen
333e8ef5353SArnaud Pouliquen    # Set defaults for optional arguments.
334e8ef5353SArnaud Pouliquen
335e8ef5353SArnaud Pouliquen    if parsed.out_file is None:
336e8ef5353SArnaud Pouliquen        parsed.out_file = str(parsed.in_file)+'.sig'
337e8ef5353SArnaud Pouliquen
338e8ef5353SArnaud Pouliquen    return parsed
339e8ef5353SArnaud Pouliquen
340e8ef5353SArnaud Pouliquen
341*39a4a0eeSArnaud Pouliquendef rsa_key(key_file, key_pwd):
342*39a4a0eeSArnaud Pouliquen    return RSA.importKey(open(key_file).read(), key_pwd)
343e8ef5353SArnaud Pouliquen
344e8ef5353SArnaud Pouliquen
345*39a4a0eeSArnaud Pouliquendef ecc_key(key_file, key_pwd):
346*39a4a0eeSArnaud Pouliquen    return ECC.import_key(open(key_file).read(), key_pwd)
347e8ef5353SArnaud Pouliquen
348e8ef5353SArnaud Pouliquen
349e8ef5353SArnaud Pouliquenkey_type = {
350e8ef5353SArnaud Pouliquen        1: rsa_key,
351e8ef5353SArnaud Pouliquen        2: ecc_key,
352e8ef5353SArnaud Pouliquen}
353e8ef5353SArnaud Pouliquen
354e8ef5353SArnaud Pouliquen
355e8ef5353SArnaud Pouliquendef rsa_sig_size(key):
356e8ef5353SArnaud Pouliquen    return key.size_in_bytes()
357e8ef5353SArnaud Pouliquen
358e8ef5353SArnaud Pouliquen
359e8ef5353SArnaud Pouliquendef ecc_sig_size(key):
360e8ef5353SArnaud Pouliquen    # to be improve...
361e8ef5353SArnaud Pouliquen    # DSA size is N/4  so 64 for DSA (L,N) = (2048, 256)
362e8ef5353SArnaud Pouliquen    return 64
363e8ef5353SArnaud Pouliquen
364e8ef5353SArnaud Pouliquen
365e8ef5353SArnaud Pouliquensig_size_type = {
366e8ef5353SArnaud Pouliquen        1: rsa_sig_size,
367e8ef5353SArnaud Pouliquen        2: ecc_sig_size,
368e8ef5353SArnaud Pouliquen}
369e8ef5353SArnaud Pouliquen
370e8ef5353SArnaud Pouliquen
371e8ef5353SArnaud Pouliquendef main():
372e8ef5353SArnaud Pouliquen    from Cryptodome.Signature import pss
373e8ef5353SArnaud Pouliquen    from Cryptodome.Hash import SHA256
374e8ef5353SArnaud Pouliquen    from Cryptodome.PublicKey import RSA
375e8ef5353SArnaud Pouliquen    import base64
376e8ef5353SArnaud Pouliquen    import logging
377e8ef5353SArnaud Pouliquen    import struct
378e8ef5353SArnaud Pouliquen
379e8ef5353SArnaud Pouliquen    logging.basicConfig()
380e8ef5353SArnaud Pouliquen    logger = logging.getLogger(os.path.basename(__file__))
381e8ef5353SArnaud Pouliquen
382e8ef5353SArnaud Pouliquen    args = get_args(logger)
383e8ef5353SArnaud Pouliquen
384e8ef5353SArnaud Pouliquen    # Initialise the header */
385e8ef5353SArnaud Pouliquen    s_header = ImageHeader()
386e8ef5353SArnaud Pouliquen    tlv = TLV()
387e8ef5353SArnaud Pouliquen
388e8ef5353SArnaud Pouliquen    sign_type = ENUM_SIGNATURE_TYPE[args.key_type]
389e8ef5353SArnaud Pouliquen    get_key = key_type.get(sign_type, lambda: "Invalid sign type")
390e8ef5353SArnaud Pouliquen
391*39a4a0eeSArnaud Pouliquen    key = get_key(args.key_file, args.key_pwd)
392e8ef5353SArnaud Pouliquen
393e8ef5353SArnaud Pouliquen    if not key.has_private():
394e8ef5353SArnaud Pouliquen        logger.error('Provided key cannot be used for signing, ')
395e8ef5353SArnaud Pouliquen        sys.exit(1)
396e8ef5353SArnaud Pouliquen
397e8ef5353SArnaud Pouliquen    tlv.add('SIGNTYPE', sign_type.to_bytes(1, 'little'))
398e8ef5353SArnaud Pouliquen
399e8ef5353SArnaud Pouliquen    images_type = []
400e8ef5353SArnaud Pouliquen    hash_tlv = bytearray()
401e8ef5353SArnaud Pouliquen    images_size = []
402e8ef5353SArnaud Pouliquen
403e8ef5353SArnaud Pouliquen    # Firmware image
404e8ef5353SArnaud Pouliquen    for inputf in args.in_file:
405e8ef5353SArnaud Pouliquen        logging.debug("image  %s" % inputf)
406e8ef5353SArnaud Pouliquen        input_file = open(inputf, 'rb')
407e8ef5353SArnaud Pouliquen        img = ELFFile(input_file)
408e8ef5353SArnaud Pouliquen
409e8ef5353SArnaud Pouliquen        # Only ARM machine has been tested and well supported yet.
410e8ef5353SArnaud Pouliquen        # Indeed this script uses of ENUM_P_TYPE_ARM dic
411e8ef5353SArnaud Pouliquen        assert img.get_machine_arch() in ["ARM"]
412e8ef5353SArnaud Pouliquen
413e8ef5353SArnaud Pouliquen        # Need to reopen the file to get the raw data
414e8ef5353SArnaud Pouliquen        with open(inputf, 'rb') as f:
415e8ef5353SArnaud Pouliquen            bin_img = f.read()
416e8ef5353SArnaud Pouliquen        size = len(bin_img)
417e8ef5353SArnaud Pouliquen        align_64b = size % 8
418e8ef5353SArnaud Pouliquen        if align_64b:
419e8ef5353SArnaud Pouliquen            size += 8 - align_64b
420e8ef5353SArnaud Pouliquen
421e8ef5353SArnaud Pouliquen        images_size.extend(size.to_bytes(4, 'little'))
422e8ef5353SArnaud Pouliquen        s_header.img_length += size
423e8ef5353SArnaud Pouliquen        f.close()
424e8ef5353SArnaud Pouliquen
425e8ef5353SArnaud Pouliquen        # Store image type information
426e8ef5353SArnaud Pouliquen        bin_type = ENUM_BINARY_TYPE['ELF']
427e8ef5353SArnaud Pouliquen        images_type += bin_type.to_bytes(1, 'little')
428e8ef5353SArnaud Pouliquen
429e8ef5353SArnaud Pouliquen        # Compute the hash table and add it to TLV blob
430e8ef5353SArnaud Pouliquen        hash_table = SegmentHash(img)
431e8ef5353SArnaud Pouliquen        hash_tlv.extend(hash_table.get_table())
432e8ef5353SArnaud Pouliquen
433e8ef5353SArnaud Pouliquen    # Add image information
434e8ef5353SArnaud Pouliquen    # The 'IMGTYPE' contains a byte array of the image type (ENUM_BINARY_TYPE).
435e8ef5353SArnaud Pouliquen    # The 'IMGSIZE' contains a byte array of the size (32-bit) of each image.
436e8ef5353SArnaud Pouliquen    tlv.add('NUM_IMG', len(args.in_file).to_bytes(1, 'little'))
437e8ef5353SArnaud Pouliquen    tlv.add('IMGTYPE', bytearray(images_type))
438e8ef5353SArnaud Pouliquen    tlv.add('IMGSIZE', bytearray(images_size))
439e8ef5353SArnaud Pouliquen
440e8ef5353SArnaud Pouliquen    # Add hash type information in TLV blob
441e8ef5353SArnaud Pouliquen    # The 'HASHTYPE' TLV contains a byte associated to ENUM_HASH_TYPE.
442e8ef5353SArnaud Pouliquen    hash_type = ENUM_HASH_TYPE['SHA256']
443e8ef5353SArnaud Pouliquen    tlv.add('HASHTYPE', hash_type.to_bytes(1, 'little'))
444e8ef5353SArnaud Pouliquen
445e8ef5353SArnaud Pouliquen    # Add hash table information in TLV blob
446e8ef5353SArnaud Pouliquen    # The HASHTABLE TLV contains a byte array containing all the ELF segment
447e8ef5353SArnaud Pouliquen    # with associated hash.
448e8ef5353SArnaud Pouliquen    tlv.add('HASHTABLE', hash_tlv)
449e8ef5353SArnaud Pouliquen
450e8ef5353SArnaud Pouliquen    # Add optional key information to TLV
451e8ef5353SArnaud Pouliquen    if args.key_info:
452e8ef5353SArnaud Pouliquen        with open(args.key_info, 'rb') as f:
453e8ef5353SArnaud Pouliquen            key_info = f.read()
454e8ef5353SArnaud Pouliquen        tlv.add('PKEYINFO', key_info)
455e8ef5353SArnaud Pouliquen
456e8ef5353SArnaud Pouliquen    # Compute custom TLV that will be passed to the platform PTA
457e8ef5353SArnaud Pouliquen    # Get list of custom protected TLVs from the command-line
458e8ef5353SArnaud Pouliquen    if args.plat_tlv:
459e8ef5353SArnaud Pouliquen        tlv.add_plat_tlv(args.plat_tlv)
460e8ef5353SArnaud Pouliquen
461e8ef5353SArnaud Pouliquen    # Get the TLV area and compute its size (with 64 bit alignment)
462e8ef5353SArnaud Pouliquen    tlvs_buff = tlv.get()
463e8ef5353SArnaud Pouliquen    s_header.tlv_length = len(tlvs_buff)
464e8ef5353SArnaud Pouliquen
465e8ef5353SArnaud Pouliquen    align_64b = 8 - (s_header.tlv_length % 8)
466e8ef5353SArnaud Pouliquen    if align_64b:
467e8ef5353SArnaud Pouliquen        s_header.tlv_length += 8 - align_64b
468e8ef5353SArnaud Pouliquen        tlvs_buff += bytearray(8 - align_64b)
469e8ef5353SArnaud Pouliquen
470e8ef5353SArnaud Pouliquen    dump_buffer(tlvs_buff, name='TLVS', indent="\t")
471e8ef5353SArnaud Pouliquen
472e8ef5353SArnaud Pouliquen    # Signature chunk
473e8ef5353SArnaud Pouliquen    sign_size = sig_size_type.get(ENUM_SIGNATURE_TYPE[args.key_type],
474e8ef5353SArnaud Pouliquen                                  lambda: "Invalid sign type")(key)
475e8ef5353SArnaud Pouliquen    s_header.sign_length = sign_size
476e8ef5353SArnaud Pouliquen
477e8ef5353SArnaud Pouliquen    # Construct the Header
478e8ef5353SArnaud Pouliquen    header = s_header.get_packed()
479e8ef5353SArnaud Pouliquen
480e8ef5353SArnaud Pouliquen    # Generate signature
481e8ef5353SArnaud Pouliquen    signer = Signature.get(ENUM_SIGNATURE_TYPE[args.key_type])(key)
482e8ef5353SArnaud Pouliquen
483e8ef5353SArnaud Pouliquen    signer.hash_compute(header)
484e8ef5353SArnaud Pouliquen    signer.hash_compute(tlvs_buff)
485e8ef5353SArnaud Pouliquen    signature = signer.sign()
486e8ef5353SArnaud Pouliquen    if len(signature) != sign_size:
487e8ef5353SArnaud Pouliquen        raise Exception(("Actual signature length is not equal to ",
488e8ef5353SArnaud Pouliquen                         "the computed one: {} != {}".
489e8ef5353SArnaud Pouliquen                         format(len(signature), sign_size)))
490e8ef5353SArnaud Pouliquen
491e8ef5353SArnaud Pouliquen    s_header.dump()
492e8ef5353SArnaud Pouliquen
493e8ef5353SArnaud Pouliquen    with open(args.out_file, 'wb') as f:
494e8ef5353SArnaud Pouliquen        f.write(header)
495e8ef5353SArnaud Pouliquen        f.write(tlvs_buff)
496e8ef5353SArnaud Pouliquen        f.write(signature)
497e8ef5353SArnaud Pouliquen        align_64b = sign_size % 8
498e8ef5353SArnaud Pouliquen        if align_64b:
499e8ef5353SArnaud Pouliquen            f.write(bytearray(8 - align_64b))
500e8ef5353SArnaud Pouliquen        for inputf in args.in_file:
501e8ef5353SArnaud Pouliquen            with open(inputf, 'rb') as fin:
502e8ef5353SArnaud Pouliquen                bin_img = fin.read()
503e8ef5353SArnaud Pouliquen            f.write(bin_img)
504e8ef5353SArnaud Pouliquen            fin.close()
505e8ef5353SArnaud Pouliquen            align_64b = len(bin_img) % 8
506e8ef5353SArnaud Pouliquen            if align_64b:
507e8ef5353SArnaud Pouliquen                f.write(bytearray(8 - align_64b))
508e8ef5353SArnaud Pouliquen
509e8ef5353SArnaud Pouliquen
510e8ef5353SArnaud Pouliquenif __name__ == "__main__":
511e8ef5353SArnaud Pouliquen    main()
512