#!/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 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('