blob: fe81a1f51b1eba8a20126bcd802047cc05e9092e [file] [log] [blame]
# 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):
"""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
[ 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, bootcore_opts):
"""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
[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}
{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')