[PATCH RFC v2 05/11] ti: etype: x509: Add etype for x509 certificate for K3 devices

Roger Quadros rogerq at kernel.org
Wed Jun 1 12:48:10 CEST 2022


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-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?
>>>>
>>>
>>> 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 <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.
>>>>
>>>
>>> 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           = 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?
> 
> 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


More information about the U-Boot mailing list