diff options
Diffstat (limited to 'tools/binman/btool/openssl.py')
-rw-r--r-- | tools/binman/btool/openssl.py | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/tools/binman/btool/openssl.py b/tools/binman/btool/openssl.py new file mode 100644 index 00000000000..b26f087c447 --- /dev/null +++ b/tools/binman/btool/openssl.py @@ -0,0 +1,367 @@ +# 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, firewall_cert_data): + """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 + firewall_cert_data (dict): + - auth_in_place (int): The Priv ID for copying as the + specific host in firewall protected region + - num_firewalls (int): The number of firewalls in the + extended certificate + - certificate (str): Extended firewall certificate with + the information for the firewall configurations. + + 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 +1.3.6.1.4.1.294.1.37 = ASN1:SEQUENCE:firewall + +[ 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:{hex(firewall_cert_data['auth_in_place'])} + +[ firewall ] +numFirewallRegions = INTEGER:{firewall_cert_data['num_firewalls']} +{firewall_cert_data['certificate']} +''', 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, debug): + """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 + bootcore_opts(int): Booting core option, lockstep (0) or split (2) mode + 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 + + # When debugging low level boot firmware it can be useful to have ROM or TIFS + # unlock JTAG access to the misbehaving CPUs. However in a production setting + # this can lead to code modification by outside parties after it's been + # authenticated. To gain JTAG access add the 'debug' flag to the binman config + [ debug ] + debugUID = FORMAT:HEX,OCT:0000000000000000000000000000000000000000000000000000000000000000 + debugType = INTEGER:{ "4" if debug else "0" } + 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, bootcore_opts, debug): + """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 + bootcore_opts (int): Booting core option, lockstep (0) or split (2) mode + + 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 +1.3.6.1.4.1.294.1.8=ASN1:SEQUENCE:debug + +[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:{bootcore_opts} +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} + +# When debugging low level boot firmware it can be useful to have ROM or TIFS +# unlock JTAG access to the misbehaving CPUs. However in a production setting +# this can lead to code modification by outside parties after it's been +# authenticated. To gain JTAG access add the 'debug' flag to the binman config +[ debug ] +debugUID = FORMAT:HEX,OCT:0000000000000000000000000000000000000000000000000000000000000000 +debugType = INTEGER:{ "4" if debug else "0" } +coreDbgEn = INTEGER:0 +coreDbgSecEn = INTEGER:0 + +{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') |