binman: Support generation of x509 certificates
And a new entry type which supports generation of x509 certificates. This uses a new 'openssl' btool with just one operation so far. Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
7caa372a5e
commit
953d4177af
7 changed files with 333 additions and 0 deletions
94
tools/binman/btool/openssl.py
Normal file
94
tools/binman/btool/openssl.py
Normal file
|
@ -0,0 +1,94 @@
|
|||
# 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
|
||||
|
||||
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 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')
|
|
@ -2276,6 +2276,24 @@ and kernel are genuine.
|
|||
|
||||
|
||||
|
||||
.. _etype_x509_cert:
|
||||
|
||||
Entry: x509-cert: An entry which contains an X509 certificate
|
||||
-------------------------------------------------------------
|
||||
|
||||
Properties / Entry arguments:
|
||||
- content: List of phandles to entries to sign
|
||||
|
||||
Output files:
|
||||
- input.<unique_name> - input file passed to openssl
|
||||
- cert.<unique_name> - output file generated by openssl (which is
|
||||
used as the entry contents)
|
||||
|
||||
openssl signs the provided data, writing the signature in this entry. This
|
||||
allows verification that the data is genuine
|
||||
|
||||
|
||||
|
||||
.. _etype_x86_reset16:
|
||||
|
||||
Entry: x86-reset16: x86 16-bit reset code for U-Boot
|
||||
|
|
92
tools/binman/etype/x509_cert.py
Normal file
92
tools/binman/etype/x509_cert.py
Normal file
|
@ -0,0 +1,92 @@
|
|||
# SPDX-License-Identifier: GPL-2.0+
|
||||
# Copyright 2023 Google LLC
|
||||
# Written by Simon Glass <sjg@chromium.org>
|
||||
#
|
||||
|
||||
# Support for an X509 certificate, used to sign a set of entries
|
||||
|
||||
from collections import OrderedDict
|
||||
import os
|
||||
|
||||
from binman.entry import EntryArg
|
||||
from binman.etype.collection import Entry_collection
|
||||
|
||||
from dtoc import fdt_util
|
||||
from u_boot_pylib import tools
|
||||
|
||||
class Entry_x509_cert(Entry_collection):
|
||||
"""An entry which contains an X509 certificate
|
||||
|
||||
Properties / Entry arguments:
|
||||
- content: List of phandles to entries to sign
|
||||
|
||||
Output files:
|
||||
- input.<unique_name> - input file passed to openssl
|
||||
- cert.<unique_name> - output file generated by openssl (which is
|
||||
used as the entry contents)
|
||||
|
||||
openssl signs the provided data, writing the signature in this entry. This
|
||||
allows verification that the data is genuine
|
||||
"""
|
||||
def __init__(self, section, etype, node):
|
||||
super().__init__(section, etype, node)
|
||||
self.openssl = None
|
||||
|
||||
def ReadNode(self):
|
||||
super().ReadNode()
|
||||
self._cert_ca = fdt_util.GetString(self._node, 'cert-ca')
|
||||
self._cert_rev = fdt_util.GetInt(self._node, 'cert-revision-int', 0)
|
||||
self.key_fname = self.GetEntryArgsOrProps([
|
||||
EntryArg('keyfile', str)], required=True)[0]
|
||||
|
||||
def GetCertificate(self, required):
|
||||
"""Get the contents of this entry
|
||||
|
||||
Args:
|
||||
required: True if the data must be present, False if it is OK to
|
||||
return None
|
||||
|
||||
Returns:
|
||||
bytes content of the entry, which is the signed vblock for the
|
||||
provided data
|
||||
"""
|
||||
# Join up the data files to be signed
|
||||
input_data = self.GetContents(required)
|
||||
if input_data is None:
|
||||
return None
|
||||
|
||||
uniq = self.GetUniqueName()
|
||||
output_fname = tools.get_output_filename('cert.%s' % uniq)
|
||||
input_fname = tools.get_output_filename('input.%s' % uniq)
|
||||
config_fname = tools.get_output_filename('config.%s' % uniq)
|
||||
tools.write_file(input_fname, input_data)
|
||||
stdout = self.openssl.x509_cert(
|
||||
cert_fname=output_fname,
|
||||
input_fname=input_fname,
|
||||
key_fname=self.key_fname,
|
||||
cn=self._cert_ca,
|
||||
revision=self._cert_rev,
|
||||
config_fname=config_fname)
|
||||
if stdout is not None:
|
||||
data = tools.read_file(output_fname)
|
||||
else:
|
||||
# Bintool is missing; just use 4KB of zero data
|
||||
self.record_missing_bintool(self.openssl)
|
||||
data = tools.get_bytes(0, 4096)
|
||||
return data
|
||||
|
||||
def ObtainContents(self):
|
||||
data = self.GetCertificate(False)
|
||||
if data is None:
|
||||
return False
|
||||
self.SetContents(data)
|
||||
return True
|
||||
|
||||
def ProcessContents(self):
|
||||
# The blob may have changed due to WriteSymbols()
|
||||
data = self.GetCertificate(True)
|
||||
return self.ProcessContentsUpdate(data)
|
||||
|
||||
def AddBintools(self, btools):
|
||||
super().AddBintools(btools)
|
||||
self.openssl = self.AddBintool(btools, 'openssl')
|
|
@ -6539,6 +6539,32 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
|
|||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
def testX509Cert(self):
|
||||
"""Test creating an X509 certificate"""
|
||||
keyfile = self.TestFile('key.key')
|
||||
entry_args = {
|
||||
'keyfile': keyfile,
|
||||
}
|
||||
data = self._DoReadFileDtb('279_x509_cert.dts',
|
||||
entry_args=entry_args)[0]
|
||||
cert = data[:-4]
|
||||
self.assertEqual(U_BOOT_DATA, data[-4:])
|
||||
|
||||
# TODO: verify the signature
|
||||
|
||||
def testX509CertMissing(self):
|
||||
"""Test that binman still produces an image if openssl is missing"""
|
||||
keyfile = self.TestFile('key.key')
|
||||
entry_args = {
|
||||
'keyfile': 'keyfile',
|
||||
}
|
||||
with test_util.capture_sys_output() as (_, stderr):
|
||||
self._DoTestFile('279_x509_cert.dts',
|
||||
force_missing_bintools='openssl',
|
||||
entry_args=entry_args)
|
||||
err = stderr.getvalue()
|
||||
self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
19
tools/binman/test/279_x509_cert.dts
Normal file
19
tools/binman/test/279_x509_cert.dts
Normal file
|
@ -0,0 +1,19 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
|
||||
/dts-v1/;
|
||||
|
||||
/ {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
binman {
|
||||
x509-cert {
|
||||
cert-ca = "IOT2050 Firmware Signature";
|
||||
cert-revision-int = <0>;
|
||||
content = <&u_boot>;
|
||||
};
|
||||
|
||||
u_boot: u-boot {
|
||||
};
|
||||
};
|
||||
};
|
52
tools/binman/test/key.key
Normal file
52
tools/binman/test/key.key
Normal file
|
@ -0,0 +1,52 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
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
|
||||
wR0+2mDpx+HDyu76q3M+KxXG2U8sJg==
|
||||
-----END PRIVATE KEY-----
|
32
tools/binman/test/key.pem
Normal file
32
tools/binman/test/key.pem
Normal file
|
@ -0,0 +1,32 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
mCKa5Tw=
|
||||
-----END CERTIFICATE-----
|
Loading…
Reference in a new issue