# SPDX-License-Identifier: GPL-2.0+ # Copyright (c) 2022 Texas Instruments Incorporated - https://www.ti.com/ # Written by Neha Malcom Francis # # Support for generation of TI secured bootloaders booted by ROM from binman.entry import EntryArg from binman.etype.x509_cert import Entry_x509_cert import hashlib from dtoc import fdt_util from u_boot_pylib import tools VALID_SHAS = [256, 384, 512, 224] SHA_OIDS = {256:'2.16.840.1.101.3.4.2.1', 384:'2.16.840.1.101.3.4.2.2', 512:'2.16.840.1.101.3.4.2.3', 224:'2.16.840.1.101.3.4.2.4'} class Entry_ti_secure_rom(Entry_x509_cert): """Entry containing a TI x509 certificate binary for images booted by ROM Properties / Entry arguments: - keyfile: Filename of file containing key to sign binary with - combined: boolean if device follows combined boot flow - countersign: boolean if device contains countersigned system firmware - load: load address of SPL - sw-rev: software revision - sha: Hash function to be used for signing - core: core on which bootloader runs, valid cores are 'secure' and 'public' - content: phandle of SPL in case of legacy bootflow or phandles of component binaries in case of combined bootflow - core-opts (optional): split-mode (0) or lockstep mode (1) set to 0 by default The following properties are only for generating a combined bootflow binary: - sysfw-inner-cert: boolean if binary contains sysfw inner certificate - dm-data: boolean if binary contains dm-data binary - content-sbl: phandle of SPL binary - content-sysfw: phandle of sysfw binary - content-sysfw-data: phandle of sysfw-data or tifs-data binary - content-sysfw-inner-cert (optional): phandle of sysfw inner certificate binary - content-dm-data (optional): phandle of dm-data binary - load-sysfw: load address of sysfw binary - load-sysfw-data: load address of sysfw-data or tifs-data binary - load-sysfw-inner-cert (optional): load address of sysfw inner certificate binary - load-dm-data (optional): load address of dm-data binary Output files: - input. - input file passed to openssl - config. - input file generated for openssl (which is used as the config file) - cert. - output file generated by openssl (which is used as the entry contents) openssl signs the provided data, using the TI templated config file and writes the signature in this entry. This allows verification that the data is genuine. """ def __init__(self, section, etype, node): super().__init__(section, etype, node) self.openssl = None def ReadNode(self): super().ReadNode() self.combined = fdt_util.GetBool(self._node, 'combined', False) self.countersign = fdt_util.GetBool(self._node, 'countersign', False) self.fsstub = fdt_util.GetBool(self._node, 'fsstub', False) self.load_addr = fdt_util.GetInt(self._node, 'load', 0x00000000) self.sw_rev = fdt_util.GetInt(self._node, 'sw-rev', 1) self.sha = fdt_util.GetInt(self._node, 'sha', 512) self.core = fdt_util.GetString(self._node, 'core', 'secure') self.bootcore_opts = fdt_util.GetInt(self._node, 'core-opts') self.key_fname = self.GetEntryArgsOrProps([ EntryArg('keyfile', str)], required=True)[0] if self.combined: self.sysfw_inner_cert = fdt_util.GetBool(self._node, 'sysfw-inner-cert', False) self.load_addr_sysfw = fdt_util.GetInt(self._node, 'load-sysfw', 0x00000000) self.load_addr_sysfw_data = fdt_util.GetInt(self._node, 'load-sysfw-data', 0x00000000) self.dm_data = fdt_util.GetBool(self._node, 'dm-data', False) if self.dm_data: self.load_addr_dm_data = fdt_util.GetInt(self._node, 'load-dm-data', 0x00000000) self.req_dist_name = {'C': 'US', 'ST': 'TX', 'L': 'Dallas', 'O': 'Texas Instruments Incorporated', 'OU': 'Processors', 'CN': 'TI Support', 'emailAddress': 'support@ti.com'} def NonCombinedGetCertificate(self, required): """Generate certificate for legacy boot flow Args: required: True if the data must be present, False if it is OK to return None Returns: bytes content of the entry, which is the certificate binary for the provided data """ if self.bootcore_opts is None: self.bootcore_opts = 0 if self.core == 'secure': if self.countersign: self.cert_type = 3 else: self.cert_type = 2 self.bootcore = 0 else: self.cert_type = 1 self.bootcore = 16 return super().GetCertificate(required=required, type='rom') def CombinedGetCertificate(self, required): """Generate certificate for combined boot flow Args: required: True if the data must be present, False if it is OK to return None Returns: bytes content of the entry, which is the certificate binary for the provided data """ uniq = self.GetUniqueName() self.num_comps = 3 self.sha_type = SHA_OIDS[self.sha] if self.bootcore_opts is None: self.bootcore_opts = 0 # sbl self.content = fdt_util.GetPhandleList(self._node, 'content-sbl') input_data_sbl = self.GetContents(required) if input_data_sbl is None: return None input_fname_sbl = tools.get_output_filename('input.%s' % uniq) tools.write_file(input_fname_sbl, input_data_sbl) indata_sbl = tools.read_file(input_fname_sbl) self.hashval_sbl = hashlib.sha512(indata_sbl).hexdigest() self.imagesize_sbl = len(indata_sbl) # sysfw self.content = fdt_util.GetPhandleList(self._node, 'content-sysfw') input_data_sysfw = self.GetContents(required) input_fname_sysfw = tools.get_output_filename('input.%s' % uniq) tools.write_file(input_fname_sysfw, input_data_sysfw) indata_sysfw = tools.read_file(input_fname_sysfw) self.hashval_sysfw = hashlib.sha512(indata_sysfw).hexdigest() self.imagesize_sysfw = len(indata_sysfw) # sysfw data self.content = fdt_util.GetPhandleList(self._node, 'content-sysfw-data') input_data_sysfw_data = self.GetContents(required) input_fname_sysfw_data = tools.get_output_filename('input.%s' % uniq) tools.write_file(input_fname_sysfw_data, input_data_sysfw_data) indata_sysfw_data = tools.read_file(input_fname_sysfw_data) self.hashval_sysfw_data = hashlib.sha512(indata_sysfw_data).hexdigest() self.imagesize_sysfw_data = len(indata_sysfw_data) # sysfw inner cert self.sysfw_inner_cert_ext_boot_block = "" self.sysfw_inner_cert_ext_boot_sequence_string = "" imagesize_sysfw_inner_cert = 0 if self.sysfw_inner_cert: self.content = fdt_util.GetPhandleList(self._node, 'content-sysfw-inner-cert') input_data_sysfw_inner_cert = self.GetContents(required) input_fname_sysfw_inner_cert = tools.get_output_filename('input.%s' % uniq) tools.write_file(input_fname_sysfw_inner_cert, input_data_sysfw_inner_cert) indata_sysfw_inner_cert = tools.read_file(input_fname_sysfw_inner_cert) hashval_sysfw_inner_cert = hashlib.sha512(indata_sysfw_inner_cert).hexdigest() imagesize_sysfw_inner_cert = len(indata_sysfw_inner_cert) self.num_comps += 1 self.sysfw_inner_cert_ext_boot_sequence_string = "sysfw_inner_cert=SEQUENCE:sysfw_inner_cert" self.sysfw_inner_cert_ext_boot_block = f"""[sysfw_inner_cert] compType = INTEGER:3 bootCore = INTEGER:0 compOpts = INTEGER:0 destAddr = FORMAT:HEX,OCT:00000000 compSize = INTEGER:{imagesize_sysfw_inner_cert} shaType = OID:{self.sha_type} shaValue = FORMAT:HEX,OCT:{hashval_sysfw_inner_cert}""" # dm data self.dm_data_ext_boot_sequence_string = "" self.dm_data_ext_boot_block = "" imagesize_dm_data = 0 if self.dm_data: self.content = fdt_util.GetPhandleList(self._node, 'content-dm-data') input_data_dm_data = self.GetContents(required) input_fname_dm_data = tools.get_output_filename('input.%s' % uniq) tools.write_file(input_fname_dm_data, input_data_dm_data) indata_dm_data = tools.read_file(input_fname_dm_data) hashval_dm_data = hashlib.sha512(indata_dm_data).hexdigest() imagesize_dm_data = len(indata_dm_data) self.num_comps += 1 self.dm_data_ext_boot_sequence_string = "dm_data=SEQUENCE:dm_data" self.dm_data_ext_boot_block = f"""[dm_data] compType = INTEGER:17 bootCore = INTEGER:16 compOpts = INTEGER:0 destAddr = FORMAT:HEX,OCT:{self.load_addr_dm_data:08x} compSize = INTEGER:{imagesize_dm_data} shaType = OID:{self.sha_type} shaValue = FORMAT:HEX,OCT:{hashval_dm_data}""" self.total_size = self.imagesize_sbl + self.imagesize_sysfw + self.imagesize_sysfw_data + imagesize_sysfw_inner_cert + imagesize_dm_data return super().GetCertificate(required=required, type='rom-combined') def GetCertificate(self, required): """Get the contents of this entry Args: required: True if the data must be present, False if it is OK to return None Returns: bytes content of the entry, which is the certificate binary for the provided data """ if self.combined: return self.CombinedGetCertificate(required) else: return self.NonCombinedGetCertificate(required) def ObtainContents(self): data = self.data if data is None: data = self.GetCertificate(False) if data is None: return False self.SetContents(data) return True def ProcessContents(self): # The blob may have changed due to WriteSymbols() data = self.data if data is None: data = self.GetCertificate(True) return self.ProcessContentsUpdate(data) def AddBintools(self, btools): super().AddBintools(btools) self.openssl = self.AddBintool(btools, 'openssl')