Neha, On 01/06/2022 12:48, Neha Malcom Francis wrote: > Hi Roger, > > On 01/06/22 14:54, Roger Quadros wrote: >> >> >> On 01/06/2022 09:02, Neha Malcom Francis wrote: >>> Hi Roger, >>> >>> On 31/05/22 14:50, Roger Quadros wrote: >>>> >>>> >>>> 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"? >>>> >>> >>> I will reformat the commit messages accordingly. >>>>> >>>>> Signed-off-by: Neha Malcom Francis <n-fran...@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? >>>> >>> >>> That is not required, I had later changed it to always using >>> certificate.bin. Will make the necessary changes. >>> >>>>> + - 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 <s...@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. >>>> >>> >>> I used etype/vblock.py as a reference. I will clean up this etype further. >>> >> >> There were some more comments below, in case you missed them. > Thanks! > >> >>>>> + >>>>> + >>>>> +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 = 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) >>>>> + >>>>> + >>>>> +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? > > 938 was the size of the expected x509 certificate to which the data is > appended.
And that is not exepected to change anytime in future? If not, then you can use a macro define for that. If yes, then this solution is not good. > >>>> >>>> Isn't it easier to just assert that _DoReadFile('232_x509_cert.dts') is >>>> greater than len(X509_DATA)? > Would that test ensure the proper creation of certificate.bin? > No but it would just tell you that something was appended or not and will not break if certificate size changes. Your above check was not checking certificate contents either. >>>> >>>>> + >>>>> 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. > > These file changes (k3_gen_x509_cert.sh) were not intended to be present in > the patch series, sorry for that. > cheers, -roger