| # SPDX-License-Identifier: GPL-2.0+ |
| # Copyright 2023 Google LLC |
| # Written by Simon Glass <sjg@chromium.org> |
| # |
| |
| # Support for an X509 certificate, used to sign a set of entries |
| |
| from collections import OrderedDict |
| import os |
| |
| from binman.entry import EntryArg |
| from binman.etype.collection import Entry_collection |
| |
| from dtoc import fdt_util |
| from u_boot_pylib import tools |
| |
| class Entry_x509_cert(Entry_collection): |
| """An entry which contains an X509 certificate |
| |
| Properties / Entry arguments: |
| - content: List of phandles to entries to sign |
| |
| Output files: |
| - input.<unique_name> - input file passed to openssl |
| - cert.<unique_name> - output file generated by openssl (which is |
| used as the entry contents) |
| |
| openssl signs the provided data, writing 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 |
| self.req_dist_name = None |
| self.cert_type = None |
| self.bootcore = None |
| self.bootcore_opts = None |
| self.load_addr = None |
| self.sha = None |
| self.total_size = None |
| self.num_comps = None |
| self.sysfw_inner_cert_ext_boot_sequence_string = None |
| self.dm_data_ext_boot_sequence_string = None |
| self.imagesize_sbl = None |
| self.hashval_sbl = None |
| self.load_addr_sysfw = None |
| self.imagesize_sysfw = None |
| self.hashval_sysfw = None |
| self.load_addr_sysfw_data = None |
| self.imagesize_sysfw_data = None |
| self.hashval_sysfw_data = None |
| self.sysfw_inner_cert_ext_boot_block = None |
| self.dm_data_ext_boot_block = None |
| self.firewall_cert_data = None |
| |
| def ReadNode(self): |
| super().ReadNode() |
| self._cert_ca = fdt_util.GetString(self._node, 'cert-ca') |
| self._cert_rev = fdt_util.GetInt(self._node, 'cert-revision-int', 0) |
| self.key_fname = self.GetEntryArgsOrProps([ |
| EntryArg('keyfile', str)], required=True)[0] |
| self.sw_rev = fdt_util.GetInt(self._node, 'sw-rev', 1) |
| |
| def GetCertificate(self, required, type='generic'): |
| """Get the contents of this entry |
| |
| Args: |
| required: True if the data must be present, False if it is OK to |
| return None |
| type: Type of x509 certificate to generate, current supported ones are |
| 'generic', 'sysfw', 'rom' |
| |
| Returns: |
| bytes content of the entry, which is the signed vblock for the |
| provided data |
| """ |
| # Join up the data files to be signed |
| input_data = self.GetContents(required) |
| if input_data is None: |
| return None |
| |
| uniq = self.GetUniqueName() |
| output_fname = tools.get_output_filename('cert.%s' % uniq) |
| input_fname = tools.get_output_filename('input.%s' % uniq) |
| config_fname = tools.get_output_filename('config.%s' % uniq) |
| tools.write_file(input_fname, input_data) |
| if type == 'generic': |
| stdout = self.openssl.x509_cert( |
| cert_fname=output_fname, |
| input_fname=input_fname, |
| key_fname=self.key_fname, |
| cn=self._cert_ca, |
| revision=self._cert_rev, |
| config_fname=config_fname) |
| elif type == 'sysfw': |
| stdout = self.openssl.x509_cert_sysfw( |
| cert_fname=output_fname, |
| input_fname=input_fname, |
| key_fname=self.key_fname, |
| config_fname=config_fname, |
| sw_rev=self.sw_rev, |
| req_dist_name_dict=self.req_dist_name, |
| firewall_cert_data=self.firewall_cert_data) |
| elif type == 'rom': |
| stdout = self.openssl.x509_cert_rom( |
| cert_fname=output_fname, |
| input_fname=input_fname, |
| key_fname=self.key_fname, |
| config_fname=config_fname, |
| sw_rev=self.sw_rev, |
| req_dist_name_dict=self.req_dist_name, |
| cert_type=self.cert_type, |
| bootcore=self.bootcore, |
| bootcore_opts=self.bootcore_opts, |
| load_addr=self.load_addr, |
| sha=self.sha |
| ) |
| elif type == 'rom-combined': |
| stdout = self.openssl.x509_cert_rom_combined( |
| cert_fname=output_fname, |
| input_fname=input_fname, |
| key_fname=self.key_fname, |
| config_fname=config_fname, |
| sw_rev=self.sw_rev, |
| req_dist_name_dict=self.req_dist_name, |
| load_addr=self.load_addr, |
| sha=self.sha, |
| total_size=self.total_size, |
| num_comps=self.num_comps, |
| sysfw_inner_cert_ext_boot_sequence_string=self.sysfw_inner_cert_ext_boot_sequence_string, |
| dm_data_ext_boot_sequence_string=self.dm_data_ext_boot_sequence_string, |
| imagesize_sbl=self.imagesize_sbl, |
| hashval_sbl=self.hashval_sbl, |
| load_addr_sysfw=self.load_addr_sysfw, |
| imagesize_sysfw=self.imagesize_sysfw, |
| hashval_sysfw=self.hashval_sysfw, |
| load_addr_sysfw_data=self.load_addr_sysfw_data, |
| imagesize_sysfw_data=self.imagesize_sysfw_data, |
| hashval_sysfw_data=self.hashval_sysfw_data, |
| sysfw_inner_cert_ext_boot_block=self.sysfw_inner_cert_ext_boot_block, |
| dm_data_ext_boot_block=self.dm_data_ext_boot_block, |
| bootcore_opts=self.bootcore_opts |
| ) |
| if stdout is not None: |
| data = tools.read_file(output_fname) |
| else: |
| # Bintool is missing; just use 4KB of zero data |
| self.record_missing_bintool(self.openssl) |
| data = tools.get_bytes(0, 4096) |
| return data |
| |
| def ObtainContents(self): |
| 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.GetCertificate(True) |
| return self.ProcessContentsUpdate(data) |
| |
| def AddBintools(self, btools): |
| super().AddBintools(btools) |
| self.openssl = self.AddBintool(btools, 'openssl') |