[PATCH RFC v2 05/11] ti: etype: x509: Add etype for x509 certificate for K3 devices
Roger Quadros
rogerq at kernel.org
Tue May 31 11:20:37 CEST 2022
On 06/05/2022 07:37, Neha Malcom Francis wrote:
> K3 devices x509 certificate added to certain binaries that allows ROM to
what binaries?
> validate the integrity of the image. Etype that generates an x509
> certificate depending on boot flow added.
Could you please explain in more detail as to what exactly is happening here.
What do you mean by "depending on boot flow"?
>
> Signed-off-by: Neha Malcom Francis <n-francis at ti.com>
> ---
> tools/binman/entries.rst | 15 ++
> tools/binman/etype/x509_cert.py | 248 ++++++++++++++++++++++++++++
> tools/binman/ftest.py | 7 +
> tools/binman/test/232_x509_cert.dts | 18 ++
> tools/k3_gen_x509_cert.sh | 10 +-
> 5 files changed, 293 insertions(+), 5 deletions(-)
> create mode 100644 tools/binman/etype/x509_cert.py
> create mode 100644 tools/binman/test/232_x509_cert.dts
>
> diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst
> index 0c6d82fce8..dfa281e49f 100644
> --- a/tools/binman/entries.rst
> +++ b/tools/binman/entries.rst
> @@ -1890,6 +1890,21 @@ and kernel are genuine.
>
>
>
> +Entry: x509cert: x509 certificate for K3 devices
> +------------------------------------------------
> +
x509 is a generic standard. Can this be made usable by other vendors as well or
is it very specific to TI?
If this is TI specific then I'd suggest a "ti-" prefix to the entry name.
> +Properties / Entry arguments:
> + - content: Phandle of binary to sign
> + - output: Name of the final output file
why do you need output property?
> + - key_file: File with key inside it. If not provided, script generates RSA degenerate key
> + - core: Target core ID on which image would be running
> + - load: Target load address of the binary in hex
> +
> + Output files:
> + - certificate.bin: Signed certificate binary
> +
> +
> +
> Entry: x86-reset16: x86 16-bit reset code for U-Boot
> ----------------------------------------------------
>
> diff --git a/tools/binman/etype/x509_cert.py b/tools/binman/etype/x509_cert.py
> new file mode 100644
> index 0000000000..0009973155
> --- /dev/null
> +++ b/tools/binman/etype/x509_cert.py
> @@ -0,0 +1,248 @@
> +# SPDX-License-Identifier: GPL-2.0+
> +# Copyright (c) 2018 Google, Inc
> +# Written by Simon Glass <sjg at chromium.org>
> +#
> +
> +# Support for a x509 certificate for signing K3 devices
> +
> +import os
> +from collections import OrderedDict
> +from subprocess import Popen, PIPE
> +from sys import stderr, stdout
> +
> +import asn1
> +from Crypto.PublicKey import RSA
> +from cryptography.hazmat.backends import default_backend
> +from cryptography.hazmat.primitives import serialization
> +
> +from binman.etype.collection import Entry_collection
> +from dtoc import fdt_util
> +from patman import tools
> +
> +temp_x509 = "x509-temp.cert"
> +cert = "certificate.bin"
> +rand_key = "eckey.pem"
> +bootcore_opts = 0
> +bootcore = 0
> +debug_type = 0
> +
> +
> +class Entry_x509_cert(Entry_collection):
> + """ An entry which contains a x509 certificate
> +
> + Properties / Entry arguments:
> + - content: Phandle of binary to sign
> + - key_file: File with key inside it. If not provided, script generates RSA degenerate key
> + - core: Target core ID on which image would be running
> + - load: Target load address of the binary in hex
> +
> + Output files:
> + - certificate.bin: Signed certificate binary"""
> +
> + def __init__(self, section, etype, node):
> + super().__init__(section, etype, node)
> + self.key_file = fdt_util.GetString(self._node, 'key-file', "")
> + self.core = fdt_util.GetInt(self._node, 'core', 0)
> + self.load_addr = fdt_util.GetInt(self._node, 'load', 0x41c00000)
> +
> + def ReadNode(self):
> + super().ReadNode()
> + if self.key_file == "":
> + self.degen_key = True
> + else:
> + self.degen_key = False
> +
> + def _CreateCertificate(self):
> + """Create certificate for legacy boot flow"""
> + if self.degen_key == True:
> + gen_degen_key()
> + self.key_file = rand_key
> +
> + sha_val = get_sha_val("intermediate-sysfw.bin")
> + bin_size = get_file_size("intermediate-sysfw.bin")
> + addr = "%08x" % self.load_addr
> + if self.core == 0:
> + cert_type = 2
> + elif self.core == 16:
> + cert_type = 1
> + else:
> + cert_type = 2
> + debug_type = 0
> +
> + gen_template()
> + gen_cert(bin_size, sha_val, cert_type, bootcore_opts,
> + self.core, addr, debug_type, self.key_file)
> +
> + return tools.read_file("certificate.bin")
> +
> + def ObtainContents(self):
> + self.image = self.GetContents(False)
> + if self.image is None:
> + return False
> + f = open("intermediate-sysfw.bin", "wb")
> + f.write(self.image)
> + f.close()
> + self.SetContents(self._CreateCertificate())
> + return True
> +
> + def ProcessContents(self):
> + data = self._CreateCertificate()
> + return self.ProcessContentsUpdate(data)
Why do you need _CreateCertificate() and ProcessContents()?
Just have one ObtainContents() and try to get rid of all the intermediate files.
> +
> +
> +def get_sha_val(binary_file):
> + process = Popen(['openssl', 'dgst', '-sha512', '-hex',
> + binary_file], stdout=PIPE, stderr=PIPE)
> + stdout, stderr = process.communicate()
> + sha_val = stdout.split()[1]
> + return sha_val
> +
> +
> +def get_file_size(binary_file):
> + return os.path.getsize(binary_file)
> +
> +
> +def gen_degen_template():
> + with open("degen-template.txt", 'w+', encoding='utf-8') as f:
> + degen_temp = """
> +asn1=SEQUENCE:rsa_key
> +
> +[rsa_key]
> +version=INTEGER:0
> +modulus=INTEGER:0xDEGEN_MODULUS
> +pubExp=INTEGER:1
> +privExp=INTEGER:1
> +p=INTEGER:0xDEGEN_P
> +q=INTEGER:0xDEGEN_Q
> +e1=INTEGER:1
> +e2=INTEGER:1
> +coeff=INTEGER:0xDEGEN_COEFF"""
> + f.write(degen_temp)
> +
> +
> +def gen_template():
> + """Generate x509 Template"""
> + with open("x509-template.txt", "w+", encoding='utf-8') as f:
> + x509template = """
> +[ req ]
> +distinguished_name = req_distinguished_name
> +x509_extensions = v3_ca
> +prompt = no
> +dirstring_type = nobmp
> +
> +[ req_distinguished_name ]
> +C = US
> +ST = TX
> +L = Dallas
> +O = Texas Instruments Incorporated
> +OU = Processors
> +CN = TI support
> +emailAddress = support at ti.com
> +
> +[ 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:TEST_CERT_TYPE
> +bootCore = INTEGER:TEST_BOOT_CORE
> +bootCoreOpts = INTEGER:TEST_BOOT_CORE_OPTS
> +destAddr = FORMAT:HEX,OCT:TEST_BOOT_ADDR
> +imageSize = INTEGER:TEST_IMAGE_LENGTH
> +
> +[ image_integrity ]
> +shaType = OID:2.16.840.1.101.3.4.2.3
> +shaValue = FORMAT:HEX,OCT:TEST_IMAGE_SHA_VAL
> +
> +[ swrv ]
> +swrv = INTEGER:0
> +
> +# [ 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:TEST_DEBUG_TYPE
> +coreDbgEn = INTEGER:0
> +coreDbgSecEn = INTEGER:0"""
> + f.write(x509template)
> +
> +
> +def parse_key(inp_key, section):
> + parsed_key = ""
> + section_true = False
> + with open(inp_key, 'r') as file:
> + for line in file:
> + if section in line:
> + section_true = True
> + elif section_true:
> + if " " not in line:
> + break
> + else:
> + parsed_key += line.replace(":", "").replace(" ", "")
> + return parsed_key.replace("\n", "")
> +
> +
> +def gen_degen_key():
> + """Generate a 4096 bit RSA key"""
> + try:
> + # generates 1024 bit PEM encoded RSA key in PKCS#1 format
> + private_key = RSA.generate(1024)
> + f = open('key.pem', 'wb')
> + f.write(private_key.exportKey('PEM'))
> + f.close()
> + except:
> + raise(Exception)
> +
> + try:
> + process = Popen(['openssl', 'rsa', '-in', 'key.pem',
> + '-text', '-out', 'key.txt'], stdout=PIPE, stderr=PIPE)
> + stdout, stderr = process.communicate()
> + except:
> + raise(stderr)
> +
> + DEGEN_MODULUS = parse_key("key.txt", "modulus")
> + DEGEN_P = parse_key("key.txt", "prime1")
> + DEGEN_Q = parse_key("key.txt", "prime2")
> + DEGEN_COEFF = parse_key("key.txt", "coefficient")
> +
> + gen_degen_template()
> +
> + with open("degen-template.txt", 'r') as file_input:
> + with open("degenerateKey.txt", 'w') as file_output:
> + for line in file_input:
> + s = line.replace("DEGEN_MODULUS", DEGEN_MODULUS).replace(
> + "DEGEN_P", DEGEN_P).replace("DEGEN_Q", DEGEN_Q).replace("DEGEN_COEFF", DEGEN_COEFF)
> + file_output.write(s)
> +
> + try:
> + process = Popen(['openssl', 'asn1parse', '-genconf', 'degenerateKey.txt',
> + '-out', 'degenerateKey.der'], stdout=PIPE, stderr=PIPE)
> + stdout, stderr = process.communicate()
> + except:
> + raise(stderr)
> +
> + try:
> + process = Popen(['openssl', 'rsa', '-in', 'degenerateKey.der',
> + '-inform', 'DER', '-outform', 'PEM', '-out', rand_key])
> + stdout, stderr = process.communicate()
> + except:
> + raise(stderr)
> +
> +
> +def gen_cert(bin_size, sha_val, cert_type, bootcore_opts, bootcore, addr, debug_type, key):
> + with open(temp_x509, "w") as output_file:
> + with open("x509-template.txt", "r") as input_file:
> + for line in input_file:
> + output_file.write(line.replace("TEST_IMAGE_LENGTH", str(bin_size)).replace("TEST_IMAGE_SHA_VAL", sha_val.decode("utf-8")).replace("TEST_CERT_TYPE", str(cert_type)).replace(
> + "TEST_BOOT_CORE_OPTS", str(bootcore_opts)).replace("TEST_BOOT_CORE", str(bootcore)).replace("TEST_BOOT_ADDR", str(addr)).replace("TEST_DEBUG_TYPE", str(debug_type)))
> + process = Popen(['openssl', 'req', '-new', '-x509', '-key', key, '-nodes', '-outform',
> + 'DER', '-out', cert, '-config', temp_x509, '-sha512'], stdout=PIPE, stderr=PIPE)
> + stdout, stderr = process.communicate()
> diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
> index 5ff294a386..d8ee592250 100644
> --- a/tools/binman/ftest.py
> +++ b/tools/binman/ftest.py
> @@ -96,6 +96,7 @@ ENV_DATA = b'var1=1\nvar2="2"'
> PRE_LOAD_MAGIC = b'UBSH'
> PRE_LOAD_VERSION = 0x11223344.to_bytes(4, 'big')
> PRE_LOAD_HDR_SIZE = 0x00001000.to_bytes(4, 'big')
> +X509_DATA = b'filetobesigned'
>
> # Subdirectory of the input dir to use to put test FDTs
> TEST_FDT_SUBDIR = 'fdts'
> @@ -200,6 +201,7 @@ class TestFunctional(unittest.TestCase):
> TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA)
> TestFunctional._MakeInputFile('sysfw.bin', TI_SYSFW_DATA)
> TestFunctional._MakeInputFile('scp.bin', SCP_DATA)
> + TestFunctional._MakeInputFile('tosign.bin', X509_DATA)
>
> # Add a few .dtb files for testing
> TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR,
> @@ -5537,5 +5539,10 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
> data = self._DoReadFile('232_ti_sysfw.dts')
> self.assertEqual(TI_SYSFW_DATA, data[:len(TI_SYSFW_DATA)])
>
> + def testX509Cert(self):
> + """Test an image with the default x509 certificate header"""
> + data = self._DoReadFile('232_x509_cert.dts')
> + self.assertEqual(X509_DATA, data[938:938 + len(X509_DATA)])
what is 938?
Isn't it easier to just assert that _DoReadFile('232_x509_cert.dts') is greater than len(X509_DATA)?
> +
> if __name__ == "__main__":
> unittest.main()
> diff --git a/tools/binman/test/232_x509_cert.dts b/tools/binman/test/232_x509_cert.dts
> new file mode 100644
> index 0000000000..f768568ca7
> --- /dev/null
> +++ b/tools/binman/test/232_x509_cert.dts
> @@ -0,0 +1,18 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +
> +/dts-v1/;
> +
> +/ {
> + #address-cells = <1>;
> + #size-cells = <1>;
> +
> + binman {
> + x509-cert {
> + content = <&image>;
> + };
> +
> + image: blob-ext {
> + filename = "tosign.bin";
> + };
> + };
> +};
> diff --git a/tools/k3_gen_x509_cert.sh b/tools/k3_gen_x509_cert.sh
> index 298cec1313..b6ef5a2de3 100755
> --- a/tools/k3_gen_x509_cert.sh
> +++ b/tools/k3_gen_x509_cert.sh
> @@ -109,7 +109,7 @@ gen_degen_key() {
> openssl asn1parse -genconf degenerateKey.txt -out degenerateKey.der >>/dev/null 2>&1
> openssl rsa -in degenerateKey.der -inform DER -outform PEM -out $RAND_KEY >>/dev/null 2>&1
> KEY=$RAND_KEY
> - rm key.pem key.txt degen-template.txt degenerateKey.txt degenerateKey.der
> + #rm key.pem key.txt degen-template.txt degenerateKey.txt degenerateKey.der
> }
>
> declare -A options_help
> @@ -246,7 +246,7 @@ gen_cert
> cat $CERT $BIN > $OUTPUT
>
> # Remove all intermediate files
> -rm $TEMP_X509 $CERT x509-template.txt
> -if [ "$KEY" == "$RAND_KEY" ]; then
> - rm $RAND_KEY
> -fi
> +#rm $TEMP_X509 $CERT x509-template.txt
> +#if [ "$KEY" == "$RAND_KEY" ]; then
> +# rm $RAND_KEY
> +#fi
Why these changes?
Maybe you should include them within
"ifndef CONFIG_BINMAN ... endif" to avoid breaking platforms not using BINMAN.
cheers,
-roger
More information about the U-Boot
mailing list