From e8ef53536bda348596a5c529bacbec909e51eef4 Mon Sep 17 00:00:00 2001 From: Arnaud Pouliquen Date: Thu, 3 Sep 2020 14:18:58 +0200 Subject: [PATCH] scripts: add remote processor firmware signature tool Add a script that signs one or several remote processor ELF firmware that will be authenticated by the remoteproc TA. This tool adds a binary header, a signature and a TLV list. The header contains a magic number, a version number and the size of the different blobs (signature, images, TLV list blobs). The signature contains a signature authenticating the header blob hash and the TLV blob hash. The TLV blob contains a list of data formatted as Type/Length/Value fields. It contains information for the remoteproc TA and the remoteproc platform specific PTA. The TLV types from 0 to 0x00010000 are predefined information used by the remoteproc TA: - algorithm used for signature - algorithm used for computing segment's hash - number of images to load - types of the images to load - sizes of the images to load - a copy of the elf segment tables with associated hash the TLV types from 0x00010000 to 0x00020000 contains information transferred to the remoteproc platform PTA. Signed-off-by: Arnaud Pouliquen Reviewed-by: Etienne Carriere Acked-by: Jens Wiklander --- scripts/sign_rproc_fw.py | 511 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 511 insertions(+) create mode 100755 scripts/sign_rproc_fw.py diff --git a/scripts/sign_rproc_fw.py b/scripts/sign_rproc_fw.py new file mode 100755 index 00000000000..79d21e5a266 --- /dev/null +++ b/scripts/sign_rproc_fw.py @@ -0,0 +1,511 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (C) 2023, STMicroelectronics +# + +try: + from elftools.elf.elffile import ELFFile + from elftools.elf.sections import SymbolTableSection + from elftools.elf.enums import ENUM_P_TYPE_ARM + from elftools.elf.enums import * +except ImportError: + print(""" +*** +ERROR: "pyelftools" python module is not installed or version < 0.25. +*** +""") + raise + +try: + from Cryptodome.Hash import SHA256 + from Cryptodome.Signature import pkcs1_15 + from Cryptodome.PublicKey import RSA + from Cryptodome.Signature import DSS + from Cryptodome.PublicKey import ECC +except ImportError: + print(""" + *** + ERROR: "pycryptodomex" python module should be installed. + *** + """) + raise + +import os +import sys +import struct +import logging +import binascii + +# Generated file structure: +# +# -----+-------------+ +# / | Magic | 32-bit word, magic value equal to +# / +-------------+ 0x3543A468 +# / +-------------+ +# / | version | 32-bit word, version of the format +# / +-------------+ +# +-----------+ +-------------+ +# | Header | | TLV size | 32-bit word, size of the TLV +# +-----------+ +-------------+ (aligned on 64-bit), in bytes. +# \ +-------------+ +# \ | sign size | 32-bit word, size of the signature +# \ +-------------+ (aligned on 64-bit), in bytes. +# \ +-------------+ +# \ | images size | 32-bit word, size of the images to +# -----+-------------+ load (aligned on 64-bit), in bytes. +# +# +-------------+ Information used to authenticate the +# | TLV | images and boot the remote processor, +# | | stored in Type-Length-Value format. +# +-------------+ 'Type' and 'Length' are 32-bit words. +# +# +-------------+ +# | Signature | Signature of the header and the TLV. +# +-------------+ +# +# +-------------+ +# | Firmware | +# | image 1 | +# +-------------+ +# ... +# +-------------+ +# | Firmware | +# | image n | +# +-------------+ + +# Generic type definitions +TLV_TYPES = { + 'SIGNTYPE': 0x00000001, # algorithm used for signature + 'HASHTYPE': 0x00000002, # algorithm used for computing segment's hash + 'NUM_IMG': 0x00000003, # number of images to load + 'IMGTYPE': 0x00000004, # array of type of images to load + 'IMGSIZE': 0x00000005, # array of the size of the images to load + 'HASHTABLE': 0x000000010, # segment hash table for authentication + 'PKEYINFO': 0x0000000011, # information to retrieve signature key +} + +GENERIC_TLV_TYPE_RANGE = range(0x00000000, 0x00010000) +PLATFORM_TLV_TYPE_RANGE = range(0x00010000, 0x00020000) + +HEADER_MAGIC = 0x3543A468 + +logging.basicConfig(stream=sys.stderr, level=logging.INFO) + +ENUM_HASH_TYPE = dict( + SHA256=1, +) + +ENUM_SIGNATURE_TYPE = dict( + RSA=1, + ECC=2, +) + +ENUM_BINARY_TYPE = dict( + ELF=1, +) + + +def dump_buffer(buf, step=16, name="", logger=logging.debug, indent=""): + logger("%s%s:" % (indent, name)) + for i in range(0, len(buf), step): + logger("%s " % (indent) + " ". + join(["%02X" % c for c in buf[i:i+step]])) + logger("\n") + + +class TLV(): + def __init__(self): + self.buf = bytearray() + self.tlvs = {} + + def __len__(self): + return TLV_INFO_SIZE + len(self.buf) + + def add(self, kind, payload): + """ + Add a TLV record. Argument type is either the type scalar ID or a + matching string defined in TLV_TYPES. + """ + if isinstance(kind, int): + buf = struct.pack('II', kind, len(payload)) + else: + buf = struct.pack('II', TLV_TYPES[kind], len(payload)) + + # Ensure that each TLV is 64-bit aligned + align_64b = (len(payload) + len(buf)) % 8 + self.buf += buf + self.buf += payload + if align_64b: + self.buf += bytearray(8 - align_64b) + + def add_plat_tlv(self, cust_tlv): + # Get list of custom protected TLVs from the command-line + for tlv in cust_tlv: + type_id = int(tlv[0], 0) + + if type_id not in PLATFORM_TLV_TYPE_RANGE: + raise Exception('TLV %s not in range' % hex(type_id)) + + value = tlv[1] + if value.startswith('0x'): + int_val = int(value[2:], 16) + self.tlvs[type_id] = int_val.to_bytes(4, 'little') + else: + self.tlvs[type_id] = value.encode('utf-8') + + if self.tlvs is not None: + for type_id, value in self.tlvs.items(): + self.add(type_id, value) + + def get(self): + """ + Get a byte-array that concatenates all the TLV added. + """ + if len(self.buf) == 0: + return bytes() + return bytes(self.buf) + + +class RSA_Signature(object): + + def __init__(self, key): + self._hasher = SHA256.new() + self.signer = pkcs1_15.new(key) + + def hash_compute(self, segment): + self._hasher.update(segment) + + def sign(self): + return self.signer.sign(self._hasher) + + +class ECC_Signature(object): + + def __init__(self, key): + self._hasher = SHA256.new() + self.signer = DSS.new(key, 'fips-186-3') + + def hash_compute(self, segment): + self._hasher.update(segment) + + def sign(self): + return self.signer.sign(self._hasher) + + +Signature = { + 1: RSA_Signature, + 2: ECC_Signature, +} + + +class SegmentHashStruct: + pass + + +class SegmentHash(object): + ''' + Hash table based on Elf program segments + ''' + def __init__(self, img): + self._num_segments = img.num_segments() + self._pack_fmt = '<%dL' % 8 + self.img = img + self.hashProgTable = bytes() + self._offset = 0 + + def get_table(self): + ''' + Create a segment hash table containing for each segment: + - the segments header + - a hash of the segment + ''' + h = SHA256.new() + seg = SegmentHashStruct() + self.size = (h.digest_size + 32) * self._num_segments + logging.debug("hash section size %d" % self.size) + del h + self.buf = bytearray(self.size) + self._bufview_ = memoryview(self.buf) + + for i in range(self._num_segments): + h = SHA256.new() + segment = self.img.get_segment(i) + seg.header = self.img.get_segment(i).header + logging.debug("compute hash for segment offset %s" % seg.header) + h.update(segment.data()) + seg.hash = h.digest() + logging.debug("hash computed: %s" % seg.hash) + del h + struct.pack_into('