# SPDX-License-Identifier: GPL-2.0+ # Copyright 2022 Google LLC # """Bintool implementation for openssl openssl provides a number of features useful for signing images Documentation is at https://www.coreboot.org/CBFS Source code is at https://www.openssl.org/ """ import hashlib from binman import bintool 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 Bintoolopenssl(bintool.Bintool): """openssl tool This bintool supports creating new openssl certificates. It also supports fetching a binary openssl Documentation about openssl is at https://www.openssl.org/ """ def __init__(self, name): super().__init__( name, 'openssl cryptography toolkit', version_regex=r'OpenSSL (.*) \(', version_args='version') def x509_cert(self, cert_fname, input_fname, key_fname, cn, revision, config_fname): """Create a certificate Args: cert_fname (str): Filename of certificate to create input_fname (str): Filename containing data to sign key_fname (str): Filename of .pem file cn (str): Common name revision (int): Revision number config_fname (str): Filename to write fconfig into Returns: str: Tool output """ indata = tools.read_file(input_fname) hashval = hashlib.sha512(indata).hexdigest() with open(config_fname, 'w', encoding='utf-8') as outf: print(f'''[ req ] distinguished_name = req_distinguished_name x509_extensions = v3_ca prompt = no dirstring_type = nobmp [ req_distinguished_name ] CN = {cert_fname} [ v3_ca ] basicConstraints = CA:true 1.3.6.1.4.1.294.1.3 = ASN1:SEQUENCE:swrv 1.3.6.1.4.1.294.1.34 = ASN1:SEQUENCE:sysfw_image_integrity [ swrv ] swrv = INTEGER:{revision} [ sysfw_image_integrity ] shaType = OID:2.16.840.1.101.3.4.2.3 shaValue = FORMAT:HEX,OCT:{hashval} imageSize = INTEGER:{len(indata)} ''', file=outf) args = ['req', '-new', '-x509', '-key', key_fname, '-nodes', '-outform', 'DER', '-out', cert_fname, '-config', config_fname, '-sha512'] return self.run_cmd(*args) def x509_cert_sysfw(self, cert_fname, input_fname, key_fname, sw_rev, config_fname, req_dist_name_dict): """Create a certificate to be booted by system firmware Args: cert_fname (str): Filename of certificate to create input_fname (str): Filename containing data to sign key_fname (str): Filename of .pem file sw_rev (int): Software revision config_fname (str): Filename to write fconfig into req_dist_name_dict (dict): Dictionary containing key-value pairs of req_distinguished_name section extensions, must contain extensions for C, ST, L, O, OU, CN and emailAddress Returns: str: Tool output """ indata = tools.read_file(input_fname) hashval = hashlib.sha512(indata).hexdigest() with open(config_fname, 'w', encoding='utf-8') as outf: print(f'''[ req ] distinguished_name = req_distinguished_name x509_extensions = v3_ca prompt = no dirstring_type = nobmp [ req_distinguished_name ] C = {req_dist_name_dict['C']} ST = {req_dist_name_dict['ST']} L = {req_dist_name_dict['L']} O = {req_dist_name_dict['O']} OU = {req_dist_name_dict['OU']} CN = {req_dist_name_dict['CN']} emailAddress = {req_dist_name_dict['emailAddress']} [ v3_ca ] basicConstraints = CA:true 1.3.6.1.4.1.294.1.3 = ASN1:SEQUENCE:swrv 1.3.6.1.4.1.294.1.34 = ASN1:SEQUENCE:sysfw_image_integrity 1.3.6.1.4.1.294.1.35 = ASN1:SEQUENCE:sysfw_image_load [ swrv ] swrv = INTEGER:{sw_rev} [ sysfw_image_integrity ] shaType = OID:2.16.840.1.101.3.4.2.3 shaValue = FORMAT:HEX,OCT:{hashval} imageSize = INTEGER:{len(indata)} [ sysfw_image_load ] destAddr = FORMAT:HEX,OCT:00000000 authInPlace = INTEGER:2 ''', file=outf) args = ['req', '-new', '-x509', '-key', key_fname, '-nodes', '-outform', 'DER', '-out', cert_fname, '-config', config_fname, '-sha512'] return self.run_cmd(*args) def x509_cert_rom(self, cert_fname, input_fname, key_fname, sw_rev, config_fname, req_dist_name_dict, cert_type, bootcore, bootcore_opts, load_addr, sha): """Create a certificate Args: cert_fname (str): Filename of certificate to create input_fname (str): Filename containing data to sign key_fname (str): Filename of .pem file sw_rev (int): Software revision config_fname (str): Filename to write fconfig into req_dist_name_dict (dict): Dictionary containing key-value pairs of req_distinguished_name section extensions, must contain extensions for C, ST, L, O, OU, CN and emailAddress cert_type (int): Certification type bootcore (int): Booting core load_addr (int): Load address of image sha (int): Hash function Returns: str: Tool output """ indata = tools.read_file(input_fname) hashval = hashlib.sha512(indata).hexdigest() with open(config_fname, 'w', encoding='utf-8') as outf: print(f''' [ req ] distinguished_name = req_distinguished_name x509_extensions = v3_ca prompt = no dirstring_type = nobmp [ req_distinguished_name ] C = {req_dist_name_dict['C']} ST = {req_dist_name_dict['ST']} L = {req_dist_name_dict['L']} O = {req_dist_name_dict['O']} OU = {req_dist_name_dict['OU']} CN = {req_dist_name_dict['CN']} emailAddress = {req_dist_name_dict['emailAddress']} [ v3_ca ] basicConstraints = CA:true 1.3.6.1.4.1.294.1.1 = ASN1:SEQUENCE:boot_seq 1.3.6.1.4.1.294.1.2 = ASN1:SEQUENCE:image_integrity 1.3.6.1.4.1.294.1.3 = ASN1:SEQUENCE:swrv # 1.3.6.1.4.1.294.1.4 = ASN1:SEQUENCE:encryption 1.3.6.1.4.1.294.1.8 = ASN1:SEQUENCE:debug [ boot_seq ] certType = INTEGER:{cert_type} bootCore = INTEGER:{bootcore} bootCoreOpts = INTEGER:{bootcore_opts} destAddr = FORMAT:HEX,OCT:{load_addr:08x} imageSize = INTEGER:{len(indata)} [ image_integrity ] shaType = OID:{SHA_OIDS[sha]} shaValue = FORMAT:HEX,OCT:{hashval} [ swrv ] swrv = INTEGER:{sw_rev} # [ encryption ] # initalVector = FORMAT:HEX,OCT:TEST_IMAGE_ENC_IV # randomString = FORMAT:HEX,OCT:TEST_IMAGE_ENC_RS # iterationCnt = INTEGER:TEST_IMAGE_KEY_DERIVE_INDEX # salt = FORMAT:HEX,OCT:TEST_IMAGE_KEY_DERIVE_SALT [ debug ] debugUID = FORMAT:HEX,OCT:0000000000000000000000000000000000000000000000000000000000000000 debugType = INTEGER:4 coreDbgEn = INTEGER:0 coreDbgSecEn = INTEGER:0 ''', file=outf) args = ['req', '-new', '-x509', '-key', key_fname, '-nodes', '-outform', 'DER', '-out', cert_fname, '-config', config_fname, '-sha512'] return self.run_cmd(*args) def x509_cert_rom_combined(self, cert_fname, input_fname, key_fname, sw_rev, config_fname, req_dist_name_dict, load_addr, sha, total_size, num_comps, sysfw_inner_cert_ext_boot_sequence_string, dm_data_ext_boot_sequence_string, imagesize_sbl, hashval_sbl, load_addr_sysfw, imagesize_sysfw, hashval_sysfw, load_addr_sysfw_data, imagesize_sysfw_data, hashval_sysfw_data, sysfw_inner_cert_ext_boot_block, dm_data_ext_boot_block): """Create a certificate Args: cert_fname (str): Filename of certificate to create input_fname (str): Filename containing data to sign key_fname (str): Filename of .pem file sw_rev (int): Software revision config_fname (str): Filename to write fconfig into req_dist_name_dict (dict): Dictionary containing key-value pairs of req_distinguished_name section extensions, must contain extensions for C, ST, L, O, OU, CN and emailAddress cert_type (int): Certification type bootcore (int): Booting core load_addr (int): Load address of image sha (int): Hash function Returns: str: Tool output """ indata = tools.read_file(input_fname) hashval = hashlib.sha512(indata).hexdigest() sha_type = SHA_OIDS[sha] with open(config_fname, 'w', encoding='utf-8') as outf: print(f''' [ req ] distinguished_name = req_distinguished_name x509_extensions = v3_ca prompt = no dirstring_type = nobmp [ req_distinguished_name ] C = {req_dist_name_dict['C']} ST = {req_dist_name_dict['ST']} L = {req_dist_name_dict['L']} O = {req_dist_name_dict['O']} OU = {req_dist_name_dict['OU']} CN = {req_dist_name_dict['CN']} emailAddress = {req_dist_name_dict['emailAddress']} [ v3_ca ] basicConstraints = CA:true 1.3.6.1.4.1.294.1.3=ASN1:SEQUENCE:swrv 1.3.6.1.4.1.294.1.9=ASN1:SEQUENCE:ext_boot_info [swrv] swrv=INTEGER:{sw_rev} [ext_boot_info] extImgSize=INTEGER:{total_size} numComp=INTEGER:{num_comps} sbl=SEQUENCE:sbl sysfw=SEQUENCE:sysfw sysfw_data=SEQUENCE:sysfw_data {sysfw_inner_cert_ext_boot_sequence_string} {dm_data_ext_boot_sequence_string} [sbl] compType = INTEGER:1 bootCore = INTEGER:16 compOpts = INTEGER:0 destAddr = FORMAT:HEX,OCT:{load_addr:08x} compSize = INTEGER:{imagesize_sbl} shaType = OID:{sha_type} shaValue = FORMAT:HEX,OCT:{hashval_sbl} [sysfw] compType = INTEGER:2 bootCore = INTEGER:0 compOpts = INTEGER:0 destAddr = FORMAT:HEX,OCT:{load_addr_sysfw:08x} compSize = INTEGER:{imagesize_sysfw} shaType = OID:{sha_type} shaValue = FORMAT:HEX,OCT:{hashval_sysfw} [sysfw_data] compType = INTEGER:18 bootCore = INTEGER:0 compOpts = INTEGER:0 destAddr = FORMAT:HEX,OCT:{load_addr_sysfw_data:08x} compSize = INTEGER:{imagesize_sysfw_data} shaType = OID:{sha_type} shaValue = FORMAT:HEX,OCT:{hashval_sysfw_data} {sysfw_inner_cert_ext_boot_block} {dm_data_ext_boot_block} ''', file=outf) args = ['req', '-new', '-x509', '-key', key_fname, '-nodes', '-outform', 'DER', '-out', cert_fname, '-config', config_fname, '-sha512'] return self.run_cmd(*args) def fetch(self, method): """Fetch handler for openssl This installs the openssl package using the apt utility. Args: method (FETCH_...): Method to use Returns: True if the file was fetched and now installed, None if a method other than FETCH_BIN was requested Raises: Valuerror: Fetching could not be completed """ if method != bintool.FETCH_BIN: return None return self.apt_install('openssl')