1# SPDX-License-Identifier: GPL-2.0+ 2# Copyright (c) 2022-2023 Texas Instruments Incorporated - https://www.ti.com/ 3# Written by Neha Malcom Francis <n-francis@ti.com> 4# 5 6# Support for generation of TI secured bootloaders booted by ROM 7 8from binman.entry import EntryArg 9from binman.etype.x509_cert import Entry_x509_cert 10 11import hashlib 12 13from dtoc import fdt_util 14from u_boot_pylib import tools 15 16VALID_SHAS = [256, 384, 512, 224] 17SHA_OIDS = {256:'2.16.840.1.101.3.4.2.1', 18 384:'2.16.840.1.101.3.4.2.2', 19 512:'2.16.840.1.101.3.4.2.3', 20 224:'2.16.840.1.101.3.4.2.4'} 21 22class Entry_ti_secure_rom(Entry_x509_cert): 23 """Entry containing a TI x509 certificate binary for images booted by ROM 24 25 Properties / Entry arguments: 26 - keyfile: Filename of file containing key to sign binary with 27 - combined: boolean if device follows combined boot flow 28 - countersign: boolean if device contains countersigned system firmware 29 - load: load address of SPL 30 - sw-rev: software revision 31 - sha: Hash function to be used for signing 32 - core: core on which bootloader runs, valid cores are 'secure' and 'public' 33 - content: phandle of SPL in case of legacy bootflow or phandles of component binaries 34 in case of combined bootflow 35 - core-opts (optional): lockstep (0) or split (2) mode set to 0 by default 36 37 The following properties are only for generating a combined bootflow binary: 38 - sysfw-inner-cert: boolean if binary contains sysfw inner certificate 39 - dm-data: boolean if binary contains dm-data binary 40 - content-sbl: phandle of SPL binary 41 - content-sysfw: phandle of sysfw binary 42 - content-sysfw-data: phandle of sysfw-data or tifs-data binary 43 - content-sysfw-inner-cert (optional): phandle of sysfw inner certificate binary 44 - content-dm-data (optional): phandle of dm-data binary 45 - load-sysfw: load address of sysfw binary 46 - load-sysfw-data: load address of sysfw-data or tifs-data binary 47 - load-sysfw-inner-cert (optional): load address of sysfw inner certificate binary 48 - load-dm-data (optional): load address of dm-data binary 49 50 Output files: 51 - input.<unique_name> - input file passed to openssl 52 - config.<unique_name> - input file generated for openssl (which is 53 used as the config file) 54 - cert.<unique_name> - output file generated by openssl (which is 55 used as the entry contents) 56 57 openssl signs the provided data, using the TI templated config file and 58 writes the signature in this entry. This allows verification that the 59 data is genuine. 60 """ 61 def __init__(self, section, etype, node): 62 super().__init__(section, etype, node) 63 self.openssl = None 64 65 def ReadNode(self): 66 super().ReadNode() 67 self.combined = fdt_util.GetBool(self._node, 'combined', False) 68 self.countersign = fdt_util.GetBool(self._node, 'countersign', False) 69 self.load_addr = fdt_util.GetInt(self._node, 'load', 0x00000000) 70 self.sw_rev = fdt_util.GetInt(self._node, 'sw-rev', 1) 71 self.sha = fdt_util.GetInt(self._node, 'sha', 512) 72 self.core = fdt_util.GetString(self._node, 'core', 'secure') 73 self.bootcore_opts = fdt_util.GetInt(self._node, 'core-opts') 74 self.key_fname = self.GetEntryArgsOrProps([ 75 EntryArg('keyfile', str)], required=True)[0] 76 if self.combined: 77 self.sysfw_inner_cert = fdt_util.GetBool(self._node, 'sysfw-inner-cert', False) 78 self.load_addr_sysfw = fdt_util.GetInt(self._node, 'load-sysfw', 0x00000000) 79 self.load_addr_sysfw_data = fdt_util.GetInt(self._node, 'load-sysfw-data', 0x00000000) 80 self.dm_data = fdt_util.GetBool(self._node, 'dm-data', False) 81 if self.dm_data: 82 self.load_addr_dm_data = fdt_util.GetInt(self._node, 'load-dm-data', 0x00000000) 83 self.req_dist_name = {'C': 'US', 84 'ST': 'TX', 85 'L': 'Dallas', 86 'O': 'Texas Instruments Incorporated', 87 'OU': 'Processors', 88 'CN': 'TI Support', 89 'emailAddress': 'support@ti.com'} 90 self.debug = fdt_util.GetBool(self._node, 'debug', False) 91 92 def NonCombinedGetCertificate(self, required): 93 """Generate certificate for legacy boot flow 94 95 Args: 96 required: True if the data must be present, False if it is OK to 97 return None 98 99 Returns: 100 bytes content of the entry, which is the certificate binary for the 101 provided data 102 """ 103 if self.bootcore_opts is None: 104 self.bootcore_opts = 0 105 106 if self.core == 'secure': 107 if self.countersign: 108 self.cert_type = 3 109 else: 110 self.cert_type = 2 111 self.bootcore = 0 112 else: 113 self.cert_type = 1 114 self.bootcore = 16 115 116 return super().GetCertificate(required=required, type='rom') 117 118 def CombinedGetCertificate(self, required): 119 """Generate certificate for combined boot flow 120 121 Args: 122 required: True if the data must be present, False if it is OK to 123 return None 124 125 Returns: 126 bytes content of the entry, which is the certificate binary for the 127 provided data 128 """ 129 uniq = self.GetUniqueName() 130 131 self.num_comps = 3 132 self.sha_type = SHA_OIDS[self.sha] 133 134 if self.bootcore_opts is None: 135 self.bootcore_opts = 0 136 137 # sbl 138 self.content = fdt_util.GetPhandleList(self._node, 'content-sbl') 139 input_data_sbl = self.GetContents(required) 140 if input_data_sbl is None: 141 return None 142 143 input_fname_sbl = tools.get_output_filename('input.%s' % uniq) 144 tools.write_file(input_fname_sbl, input_data_sbl) 145 146 indata_sbl = tools.read_file(input_fname_sbl) 147 self.hashval_sbl = hashlib.sha512(indata_sbl).hexdigest() 148 self.imagesize_sbl = len(indata_sbl) 149 150 # sysfw 151 self.content = fdt_util.GetPhandleList(self._node, 'content-sysfw') 152 input_data_sysfw = self.GetContents(required) 153 154 input_fname_sysfw = tools.get_output_filename('input.%s' % uniq) 155 tools.write_file(input_fname_sysfw, input_data_sysfw) 156 157 indata_sysfw = tools.read_file(input_fname_sysfw) 158 self.hashval_sysfw = hashlib.sha512(indata_sysfw).hexdigest() 159 self.imagesize_sysfw = len(indata_sysfw) 160 161 # sysfw data 162 self.content = fdt_util.GetPhandleList(self._node, 'content-sysfw-data') 163 input_data_sysfw_data = self.GetContents(required) 164 165 input_fname_sysfw_data = tools.get_output_filename('input.%s' % uniq) 166 tools.write_file(input_fname_sysfw_data, input_data_sysfw_data) 167 168 indata_sysfw_data = tools.read_file(input_fname_sysfw_data) 169 self.hashval_sysfw_data = hashlib.sha512(indata_sysfw_data).hexdigest() 170 self.imagesize_sysfw_data = len(indata_sysfw_data) 171 172 # sysfw inner cert 173 self.sysfw_inner_cert_ext_boot_block = "" 174 self.sysfw_inner_cert_ext_boot_sequence_string = "" 175 imagesize_sysfw_inner_cert = 0 176 if self.sysfw_inner_cert: 177 self.content = fdt_util.GetPhandleList(self._node, 'content-sysfw-inner-cert') 178 input_data_sysfw_inner_cert = self.GetContents(required) 179 180 input_fname_sysfw_inner_cert = tools.get_output_filename('input.%s' % uniq) 181 tools.write_file(input_fname_sysfw_inner_cert, input_data_sysfw_inner_cert) 182 183 indata_sysfw_inner_cert = tools.read_file(input_fname_sysfw_inner_cert) 184 hashval_sysfw_inner_cert = hashlib.sha512(indata_sysfw_inner_cert).hexdigest() 185 imagesize_sysfw_inner_cert = len(indata_sysfw_inner_cert) 186 self.num_comps += 1 187 self.sysfw_inner_cert_ext_boot_sequence_string = "sysfw_inner_cert=SEQUENCE:sysfw_inner_cert" 188 self.sysfw_inner_cert_ext_boot_block = f"""[sysfw_inner_cert] 189compType = INTEGER:3 190bootCore = INTEGER:0 191compOpts = INTEGER:0 192destAddr = FORMAT:HEX,OCT:00000000 193compSize = INTEGER:{imagesize_sysfw_inner_cert} 194shaType = OID:{self.sha_type} 195shaValue = FORMAT:HEX,OCT:{hashval_sysfw_inner_cert}""" 196 197 # dm data 198 self.dm_data_ext_boot_sequence_string = "" 199 self.dm_data_ext_boot_block = "" 200 imagesize_dm_data = 0 201 if self.dm_data: 202 self.content = fdt_util.GetPhandleList(self._node, 'content-dm-data') 203 input_data_dm_data = self.GetContents(required) 204 205 input_fname_dm_data = tools.get_output_filename('input.%s' % uniq) 206 tools.write_file(input_fname_dm_data, input_data_dm_data) 207 208 indata_dm_data = tools.read_file(input_fname_dm_data) 209 hashval_dm_data = hashlib.sha512(indata_dm_data).hexdigest() 210 imagesize_dm_data = len(indata_dm_data) 211 self.num_comps += 1 212 self.dm_data_ext_boot_sequence_string = "dm_data=SEQUENCE:dm_data" 213 self.dm_data_ext_boot_block = f"""[dm_data] 214compType = INTEGER:17 215bootCore = INTEGER:16 216compOpts = INTEGER:0 217destAddr = FORMAT:HEX,OCT:{self.load_addr_dm_data:08x} 218compSize = INTEGER:{imagesize_dm_data} 219shaType = OID:{self.sha_type} 220shaValue = FORMAT:HEX,OCT:{hashval_dm_data}""" 221 222 self.total_size = self.imagesize_sbl + self.imagesize_sysfw + self.imagesize_sysfw_data + imagesize_sysfw_inner_cert + imagesize_dm_data 223 return super().GetCertificate(required=required, type='rom-combined') 224 225 def GetCertificate(self, required): 226 """Get the contents of this entry 227 228 Args: 229 required: True if the data must be present, False if it is OK to 230 return None 231 232 Returns: 233 bytes content of the entry, which is the certificate binary for the 234 provided data 235 """ 236 if self.combined: 237 return self.CombinedGetCertificate(required) 238 else: 239 return self.NonCombinedGetCertificate(required) 240 241 def ObtainContents(self): 242 data = self.data 243 if data is None: 244 data = self.GetCertificate(False) 245 if data is None: 246 return False 247 self.SetContents(data) 248 return True 249 250 def ProcessContents(self): 251 # The blob may have changed due to WriteSymbols() 252 data = self.data 253 return self.ProcessContentsUpdate(data) 254 255 def AddBintools(self, btools): 256 super().AddBintools(btools) 257 self.openssl = self.AddBintool(btools, 'openssl') 258