K3 devices requires x509 certificate to be added as header of bootloader binaries that allows ROM to validate the integrity of the image. Etype that generates a TI x509 certificate is added.
Currently this etype is scaled for J721E. For J721E, tiboot3.bin requires the x509 certificate binary to be prepended to the R5 SPL. Signed-off-by: Neha Malcom Francis <n-fran...@ti.com> --- test/py/requirements.txt | 1 + tools/binman/entries.rst | 15 ++ tools/binman/etype/ti_x509_cert.py | 241 ++++++++++++++++++++++++++++ tools/binman/ftest.py | 6 + tools/binman/test/232_x509_cert.dts | 18 +++ 5 files changed, 281 insertions(+) create mode 100644 tools/binman/etype/ti_x509_cert.py create mode 100644 tools/binman/test/232_x509_cert.dts diff --git a/test/py/requirements.txt b/test/py/requirements.txt index a91ba64563..add264bdaf 100644 --- a/test/py/requirements.txt +++ b/test/py/requirements.txt @@ -11,6 +11,7 @@ packaging==19.2 pbr==5.4.3 pluggy==0.13.0 py==1.10.0 +pycryptodome==3.14.1 pycryptodomex==3.9.8 pyelftools==0.27 pygit2==0.28.2 diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index b6915ef12e..e7757b3e06 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -1890,6 +1890,21 @@ and kernel are genuine. +Entry: ti-x509-cert: Texas Instruments x509 certificate for K3 devices +------------------------------------------------ + +Properties / Entry arguments: + - content: Phandle of binary to sign + - output: Name of the final output file + - 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/ti_x509_cert.py b/tools/binman/etype/ti_x509_cert.py new file mode 100644 index 0000000000..b79946c0b9 --- /dev/null +++ b/tools/binman/etype/ti_x509_cert.py @@ -0,0 +1,241 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018 Google, Inc +# Written by Simon Glass <s...@chromium.org> +# + +# Support for a TI x509 certificate for signing K3 devices + +from subprocess import Popen, PIPE +from sys import stderr, stdout +import os +import tempfile + +from Crypto.PublicKey import RSA + +from binman.etype.collection import Entry_collection +from dtoc import fdt_util +from patman import tools + + +class Entry_ti_x509_cert(Entry_collection): + """An entry which contains an x509 certificate binary signed with 1024 bit RSA key + + Properties / Entry arguments: + - content: Phandle of binary to generate signature for + - key_file: File with key inside it. If not provided, script generates RSA degenrate 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) + self.cert = fdt_util.GetString(self._node, 'cert', 'certificate.bin') + + # temporary directory for intermediate files + self.outdir = tempfile.mkdtemp(prefix='binman.x509.') + + def ReadNode(self): + super().ReadNode() + if self.key_file == "": + self.key_int_file = os.path.join(self.outdir, 'eckey.pem') + self.GenerateDegenKey() + else: + self.key_int_file = self.key_file + + def ObtainContents(self): + self.image = self.GetContents(False) + if self.image is None: + return False + self.image_file = os.path.join(self.outdir, 'x509.image') + with open(self.image_file, 'wb') as f: + f.write(self.image) + self.cert_data = self._TICreateCertificateLegacy() + self.SetContents(self.cert_data) + return True + + def ProcessContents(self): + # The blob may have changed due to WriteSymbols() + return super().ProcessContentsUpdate(self.cert_data) + + def _TICreateCertificateLegacy(self): + """Create certificate for legacy boot flow""" + + sha_val = self.GetShaVal(self.image_file) + bin_size = self.GetFileSize(self.image_file) + addr = "%08x" % self.load_addr + if self.core == 16: + self.cert_type = 1 + else: + self.cert_type = 2 + self.debug_type = 0 + self.bootcore_opts = 0 + + self.GenerateTemplate() + self.GenerateCertificate(bin_size, sha_val, addr) + + return tools.read_file(self.cert_file) + + def GetShaVal(self, 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 GetFileSize(self, binary_file): + return os.path.getsize(binary_file) + + def ParseKey(self, 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 GenerateDegenKey(self): + """Generate a 4096 bit RSA key""" + # generates 1024 bit PEM encoded RSA key in PKCS#1 format + private_key = RSA.generate(1024) + self.key_pem_file = os.path.join(self.outdir, 'key.pem') + with open(self.key_pem_file, 'wb') as f: + f.write(private_key.exportKey('PEM')) + + self.key_text_file = os.path.join(self.outdir, 'key.txt') + process = Popen(['openssl', 'rsa', '-in', self.key_pem_file, + '-text', '-out', self.key_text_file], stdout=PIPE, stderr=PIPE) + stdout, stderr = process.communicate() + + DEGEN_MODULUS = self.ParseKey(self.key_text_file, "modulus") + DEGEN_P = self.ParseKey(self.key_text_file, "prime1") + DEGEN_Q = self.ParseKey(self.key_text_file, "prime2") + DEGEN_COEFF = self.ParseKey(self.key_text_file, "coefficient") + + self.GenerateDegenTemplate() + + self.degen_key = os.path.join(self.outdir, 'x509.degenerateKey.txt') + with open(self.degen_temp_file, 'r') as file_input: + with open(self.degen_key, '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) + + self.degen_key_der = os.path.join( + self.outdir, 'x509.degenerateKey.der') + process = Popen(['openssl', 'asn1parse', '-genconf', self.degen_key, + '-out', self.degen_key_der], stdout=PIPE, stderr=PIPE) + stdout, stderr = process.communicate() + + process = Popen(['openssl', 'rsa', '-in', self.degen_key_der, + '-inform', 'DER', '-outform', 'PEM', '-out', self.key_int_file]) + stdout, stderr = process.communicate() + + def GenerateCertificate(self, bin_size, sha_val, addr): + self.temp_x509 = os.path.join(self.outdir, 'x509.temp.cert') + self.cert_file = os.path.join(self.outdir, 'x509.certificate.bin') + + temp_dict = {} + temp_dict['TEST_IMAGE_LENGTH'] = str(bin_size) + temp_dict['TEST_IMAGE_SHA_VAL'] = sha_val.decode("utf-8") + temp_dict['TEST_CERT_TYPE'] = str(self.cert_type) + temp_dict['TEST_BOOT_CORE_OPTS'] = str(self.bootcore_opts) + temp_dict['TEST_BOOT_CORE'] = str(self.core) + temp_dict['TEST_BOOT_ADDR'] = str(addr) + temp_dict['TEST_DEBUG_TYPE'] = str(self.debug_type) + + with open(self.temp_x509, "w") as output_file: + with open(self.temp_file, "r") as input_file: + for line in input_file: + l = line + for key in temp_dict: + if key in line: + l = l.replace(key, temp_dict[key]) + output_file.write(l) + + process = Popen(['openssl', 'req', '-new', '-x509', '-key', self.key_int_file, '-nodes', '-outform', + 'DER', '-out', self.cert_file, '-config', self.temp_x509, '-sha512'], stdout=PIPE, stderr=PIPE) + stdout, stderr = process.communicate() + + def GenerateDegenTemplate(self): + self.degen_temp_file = os.path.join(self.outdir, 'x509.degen-template') + with open(self.degen_temp_file, '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 GenerateTemplate(self): + self.temp_file = os.path.join(self.outdir, 'x509.template') + with open(self.temp_file, '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 = supp...@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) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 3709b68297..70bda9738c 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, @@ -5716,6 +5718,10 @@ fdt fdtmap Extract the devicetree blob from the fdtmap dts='234_replace_section_simple.dts') self.assertEqual(new_data, data) + def testX509Cert(self): + """Test an image with the default x509 certificate header""" + data = self._DoReadFile('232_x509_cert.dts') + self.assertGreater(len(data), 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..3e68309de5 --- /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 { + ti-x509-cert { + content = <&image>; + }; + + image: blob-ext { + filename = "tosign.bin"; + }; + }; +}; -- 2.17.1