1# SPDX-License-Identifier: GPL-2.0+ 2# Copyright 2022 Google LLC 3# 4"""Bintool implementation for openssl 5 6openssl provides a number of features useful for signing images 7 8Documentation is at https://www.coreboot.org/CBFS 9 10Source code is at https://www.openssl.org/ 11""" 12 13import hashlib 14 15from binman import bintool 16from u_boot_pylib import tools 17 18 19VALID_SHAS = [256, 384, 512, 224] 20SHA_OIDS = {256:'2.16.840.1.101.3.4.2.1', 21 384:'2.16.840.1.101.3.4.2.2', 22 512:'2.16.840.1.101.3.4.2.3', 23 224:'2.16.840.1.101.3.4.2.4'} 24 25class Bintoolopenssl(bintool.Bintool): 26 """openssl tool 27 28 This bintool supports creating new openssl certificates. 29 30 It also supports fetching a binary openssl 31 32 Documentation about openssl is at https://www.openssl.org/ 33 """ 34 def __init__(self, name): 35 super().__init__( 36 name, 'openssl cryptography toolkit', 37 version_regex=r'OpenSSL (.*) \(', version_args='version') 38 39 def x509_cert(self, cert_fname, input_fname, key_fname, cn, revision, 40 config_fname): 41 """Create a certificate 42 43 Args: 44 cert_fname (str): Filename of certificate to create 45 input_fname (str): Filename containing data to sign 46 key_fname (str): Filename of .pem file 47 cn (str): Common name 48 revision (int): Revision number 49 config_fname (str): Filename to write fconfig into 50 51 Returns: 52 str: Tool output 53 """ 54 indata = tools.read_file(input_fname) 55 hashval = hashlib.sha512(indata).hexdigest() 56 with open(config_fname, 'w', encoding='utf-8') as outf: 57 print(f'''[ req ] 58distinguished_name = req_distinguished_name 59x509_extensions = v3_ca 60prompt = no 61dirstring_type = nobmp 62 63[ req_distinguished_name ] 64CN = {cert_fname} 65 66[ v3_ca ] 67basicConstraints = CA:true 681.3.6.1.4.1.294.1.3 = ASN1:SEQUENCE:swrv 691.3.6.1.4.1.294.1.34 = ASN1:SEQUENCE:sysfw_image_integrity 70 71[ swrv ] 72swrv = INTEGER:{revision} 73 74[ sysfw_image_integrity ] 75shaType = OID:2.16.840.1.101.3.4.2.3 76shaValue = FORMAT:HEX,OCT:{hashval} 77imageSize = INTEGER:{len(indata)} 78''', file=outf) 79 args = ['req', '-new', '-x509', '-key', key_fname, '-nodes', 80 '-outform', 'DER', '-out', cert_fname, '-config', config_fname, 81 '-sha512'] 82 return self.run_cmd(*args) 83 84 def x509_cert_sysfw(self, cert_fname, input_fname, key_fname, sw_rev, 85 config_fname, req_dist_name_dict, firewall_cert_data): 86 """Create a certificate to be booted by system firmware 87 88 Args: 89 cert_fname (str): Filename of certificate to create 90 input_fname (str): Filename containing data to sign 91 key_fname (str): Filename of .pem file 92 sw_rev (int): Software revision 93 config_fname (str): Filename to write fconfig into 94 req_dist_name_dict (dict): Dictionary containing key-value pairs of 95 req_distinguished_name section extensions, must contain extensions for 96 C, ST, L, O, OU, CN and emailAddress 97 firewall_cert_data (dict): 98 - auth_in_place (int): The Priv ID for copying as the 99 specific host in firewall protected region 100 - num_firewalls (int): The number of firewalls in the 101 extended certificate 102 - certificate (str): Extended firewall certificate with 103 the information for the firewall configurations. 104 105 Returns: 106 str: Tool output 107 """ 108 indata = tools.read_file(input_fname) 109 hashval = hashlib.sha512(indata).hexdigest() 110 with open(config_fname, 'w', encoding='utf-8') as outf: 111 print(f'''[ req ] 112distinguished_name = req_distinguished_name 113x509_extensions = v3_ca 114prompt = no 115dirstring_type = nobmp 116 117[ req_distinguished_name ] 118C = {req_dist_name_dict['C']} 119ST = {req_dist_name_dict['ST']} 120L = {req_dist_name_dict['L']} 121O = {req_dist_name_dict['O']} 122OU = {req_dist_name_dict['OU']} 123CN = {req_dist_name_dict['CN']} 124emailAddress = {req_dist_name_dict['emailAddress']} 125 126[ v3_ca ] 127basicConstraints = CA:true 1281.3.6.1.4.1.294.1.3 = ASN1:SEQUENCE:swrv 1291.3.6.1.4.1.294.1.34 = ASN1:SEQUENCE:sysfw_image_integrity 1301.3.6.1.4.1.294.1.35 = ASN1:SEQUENCE:sysfw_image_load 1311.3.6.1.4.1.294.1.37 = ASN1:SEQUENCE:firewall 132 133[ swrv ] 134swrv = INTEGER:{sw_rev} 135 136[ sysfw_image_integrity ] 137shaType = OID:2.16.840.1.101.3.4.2.3 138shaValue = FORMAT:HEX,OCT:{hashval} 139imageSize = INTEGER:{len(indata)} 140 141[ sysfw_image_load ] 142destAddr = FORMAT:HEX,OCT:00000000 143authInPlace = INTEGER:{hex(firewall_cert_data['auth_in_place'])} 144 145[ firewall ] 146numFirewallRegions = INTEGER:{firewall_cert_data['num_firewalls']} 147{firewall_cert_data['certificate']} 148''', file=outf) 149 args = ['req', '-new', '-x509', '-key', key_fname, '-nodes', 150 '-outform', 'DER', '-out', cert_fname, '-config', config_fname, 151 '-sha512'] 152 return self.run_cmd(*args) 153 154 def x509_cert_rom(self, cert_fname, input_fname, key_fname, sw_rev, 155 config_fname, req_dist_name_dict, cert_type, bootcore, 156 bootcore_opts, load_addr, sha): 157 """Create a certificate 158 159 Args: 160 cert_fname (str): Filename of certificate to create 161 input_fname (str): Filename containing data to sign 162 key_fname (str): Filename of .pem file 163 sw_rev (int): Software revision 164 config_fname (str): Filename to write fconfig into 165 req_dist_name_dict (dict): Dictionary containing key-value pairs of 166 req_distinguished_name section extensions, must contain extensions for 167 C, ST, L, O, OU, CN and emailAddress 168 cert_type (int): Certification type 169 bootcore (int): Booting core 170 bootcore_opts(int): Booting core option, lockstep (0) or split (2) mode 171 load_addr (int): Load address of image 172 sha (int): Hash function 173 174 Returns: 175 str: Tool output 176 """ 177 indata = tools.read_file(input_fname) 178 hashval = hashlib.sha512(indata).hexdigest() 179 with open(config_fname, 'w', encoding='utf-8') as outf: 180 print(f''' 181[ req ] 182 distinguished_name = req_distinguished_name 183 x509_extensions = v3_ca 184 prompt = no 185 dirstring_type = nobmp 186 187 [ req_distinguished_name ] 188C = {req_dist_name_dict['C']} 189ST = {req_dist_name_dict['ST']} 190L = {req_dist_name_dict['L']} 191O = {req_dist_name_dict['O']} 192OU = {req_dist_name_dict['OU']} 193CN = {req_dist_name_dict['CN']} 194emailAddress = {req_dist_name_dict['emailAddress']} 195 196 [ v3_ca ] 197 basicConstraints = CA:true 198 1.3.6.1.4.1.294.1.1 = ASN1:SEQUENCE:boot_seq 199 1.3.6.1.4.1.294.1.2 = ASN1:SEQUENCE:image_integrity 200 1.3.6.1.4.1.294.1.3 = ASN1:SEQUENCE:swrv 201# 1.3.6.1.4.1.294.1.4 = ASN1:SEQUENCE:encryption 202 1.3.6.1.4.1.294.1.8 = ASN1:SEQUENCE:debug 203 204 [ boot_seq ] 205 certType = INTEGER:{cert_type} 206 bootCore = INTEGER:{bootcore} 207 bootCoreOpts = INTEGER:{bootcore_opts} 208 destAddr = FORMAT:HEX,OCT:{load_addr:08x} 209 imageSize = INTEGER:{len(indata)} 210 211 [ image_integrity ] 212 shaType = OID:{SHA_OIDS[sha]} 213 shaValue = FORMAT:HEX,OCT:{hashval} 214 215 [ swrv ] 216 swrv = INTEGER:{sw_rev} 217 218# [ encryption ] 219# initalVector = FORMAT:HEX,OCT:TEST_IMAGE_ENC_IV 220# randomString = FORMAT:HEX,OCT:TEST_IMAGE_ENC_RS 221# iterationCnt = INTEGER:TEST_IMAGE_KEY_DERIVE_INDEX 222# salt = FORMAT:HEX,OCT:TEST_IMAGE_KEY_DERIVE_SALT 223 224 [ debug ] 225 debugUID = FORMAT:HEX,OCT:0000000000000000000000000000000000000000000000000000000000000000 226 debugType = INTEGER:4 227 coreDbgEn = INTEGER:0 228 coreDbgSecEn = INTEGER:0 229''', file=outf) 230 args = ['req', '-new', '-x509', '-key', key_fname, '-nodes', 231 '-outform', 'DER', '-out', cert_fname, '-config', config_fname, 232 '-sha512'] 233 return self.run_cmd(*args) 234 235 def x509_cert_rom_combined(self, cert_fname, input_fname, key_fname, sw_rev, 236 config_fname, req_dist_name_dict, load_addr, sha, total_size, num_comps, 237 sysfw_inner_cert_ext_boot_sequence_string, dm_data_ext_boot_sequence_string, 238 imagesize_sbl, hashval_sbl, load_addr_sysfw, imagesize_sysfw, 239 hashval_sysfw, load_addr_sysfw_data, imagesize_sysfw_data, 240 hashval_sysfw_data, sysfw_inner_cert_ext_boot_block, 241 dm_data_ext_boot_block, bootcore_opts): 242 """Create a certificate 243 244 Args: 245 cert_fname (str): Filename of certificate to create 246 input_fname (str): Filename containing data to sign 247 key_fname (str): Filename of .pem file 248 sw_rev (int): Software revision 249 config_fname (str): Filename to write fconfig into 250 req_dist_name_dict (dict): Dictionary containing key-value pairs of 251 req_distinguished_name section extensions, must contain extensions for 252 C, ST, L, O, OU, CN and emailAddress 253 cert_type (int): Certification type 254 bootcore (int): Booting core 255 load_addr (int): Load address of image 256 sha (int): Hash function 257 bootcore_opts (int): Booting core option, lockstep (0) or split (2) mode 258 259 Returns: 260 str: Tool output 261 """ 262 indata = tools.read_file(input_fname) 263 hashval = hashlib.sha512(indata).hexdigest() 264 sha_type = SHA_OIDS[sha] 265 with open(config_fname, 'w', encoding='utf-8') as outf: 266 print(f''' 267[ req ] 268distinguished_name = req_distinguished_name 269x509_extensions = v3_ca 270prompt = no 271dirstring_type = nobmp 272 273[ req_distinguished_name ] 274C = {req_dist_name_dict['C']} 275ST = {req_dist_name_dict['ST']} 276L = {req_dist_name_dict['L']} 277O = {req_dist_name_dict['O']} 278OU = {req_dist_name_dict['OU']} 279CN = {req_dist_name_dict['CN']} 280emailAddress = {req_dist_name_dict['emailAddress']} 281 282[ v3_ca ] 283basicConstraints = CA:true 2841.3.6.1.4.1.294.1.3=ASN1:SEQUENCE:swrv 2851.3.6.1.4.1.294.1.9=ASN1:SEQUENCE:ext_boot_info 2861.3.6.1.4.1.294.1.8=ASN1:SEQUENCE:debug 287 288[swrv] 289swrv=INTEGER:{sw_rev} 290 291[ext_boot_info] 292extImgSize=INTEGER:{total_size} 293numComp=INTEGER:{num_comps} 294sbl=SEQUENCE:sbl 295sysfw=SEQUENCE:sysfw 296sysfw_data=SEQUENCE:sysfw_data 297{sysfw_inner_cert_ext_boot_sequence_string} 298{dm_data_ext_boot_sequence_string} 299 300[sbl] 301compType = INTEGER:1 302bootCore = INTEGER:16 303compOpts = INTEGER:{bootcore_opts} 304destAddr = FORMAT:HEX,OCT:{load_addr:08x} 305compSize = INTEGER:{imagesize_sbl} 306shaType = OID:{sha_type} 307shaValue = FORMAT:HEX,OCT:{hashval_sbl} 308 309[sysfw] 310compType = INTEGER:2 311bootCore = INTEGER:0 312compOpts = INTEGER:0 313destAddr = FORMAT:HEX,OCT:{load_addr_sysfw:08x} 314compSize = INTEGER:{imagesize_sysfw} 315shaType = OID:{sha_type} 316shaValue = FORMAT:HEX,OCT:{hashval_sysfw} 317 318[sysfw_data] 319compType = INTEGER:18 320bootCore = INTEGER:0 321compOpts = INTEGER:0 322destAddr = FORMAT:HEX,OCT:{load_addr_sysfw_data:08x} 323compSize = INTEGER:{imagesize_sysfw_data} 324shaType = OID:{sha_type} 325shaValue = FORMAT:HEX,OCT:{hashval_sysfw_data} 326 327[ debug ] 328debugUID = FORMAT:HEX,OCT:0000000000000000000000000000000000000000000000000000000000000000 329debugType = INTEGER:4 330coreDbgEn = INTEGER:0 331coreDbgSecEn = INTEGER:0 332 333{sysfw_inner_cert_ext_boot_block} 334 335{dm_data_ext_boot_block} 336 ''', file=outf) 337 args = ['req', '-new', '-x509', '-key', key_fname, '-nodes', 338 '-outform', 'DER', '-out', cert_fname, '-config', config_fname, 339 '-sha512'] 340 return self.run_cmd(*args) 341 342 def fetch(self, method): 343 """Fetch handler for openssl 344 345 This installs the openssl package using the apt utility. 346 347 Args: 348 method (FETCH_...): Method to use 349 350 Returns: 351 True if the file was fetched and now installed, None if a method 352 other than FETCH_BIN was requested 353 354 Raises: 355 Valuerror: Fetching could not be completed 356 """ 357 if method != bintool.FETCH_BIN: 358 return None 359 return self.apt_install('openssl') 360