| # SPDX-License-Identifier: GPL-2.0+ |
| # Copyright (c) 2022-2023 Texas Instruments Incorporated - https://www.ti.com/ |
| # Written by Neha Malcom Francis <n-francis@ti.com> |
| # |
| |
| # 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): lockstep (0) or split (2) mode 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.<unique_name> - input file passed to openssl |
| - config.<unique_name> - input file generated for openssl (which is |
| used as the config file) |
| - cert.<unique_name> - 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.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 |
| return self.ProcessContentsUpdate(data) |
| |
| def AddBintools(self, btools): |
| super().AddBintools(btools) |
| self.openssl = self.AddBintool(btools, 'openssl') |