[PATCH v2 02/18] binman: ti-secure: Add support for TI signing

Neha Malcom Francis n-francis at ti.com
Tue Apr 4 14:13:26 CEST 2023


The ti-secure entry contains certificate for binaries that will be
loaded or booted by system firmware whereas the ti-secure-rom entry
contains certificate for binaries that will be booted by ROM. Support
for both these types of certificates is necessary for booting of K3
devices.

Signed-off-by: Neha Malcom Francis <n-francis at ti.com>
---
 board/ti/keys/custMpk.pem                     |  51 ++++
 board/ti/keys/ti-degenerate-key.pem           |  10 +
 tools/binman/btool/openssl.py                 | 244 ++++++++++++++++++
 tools/binman/entries.rst                      |  25 ++
 tools/binman/etype/ti_secure.py               |  83 ++++++
 tools/binman/etype/ti_secure_rom.py           | 233 +++++++++++++++++
 tools/binman/etype/x509_cert.py               |  87 ++++++-
 tools/binman/ftest.py                         |  46 ++++
 tools/binman/test/279_ti_secure.dts           |  17 ++
 tools/binman/test/280_ti_secure_rom.dts       |  17 ++
 .../test/281_ti_secure_rom_combined.dts       |  25 ++
 11 files changed, 830 insertions(+), 8 deletions(-)
 create mode 100644 board/ti/keys/custMpk.pem
 create mode 100644 board/ti/keys/ti-degenerate-key.pem
 create mode 100644 tools/binman/etype/ti_secure.py
 create mode 100644 tools/binman/etype/ti_secure_rom.py
 create mode 100644 tools/binman/test/279_ti_secure.dts
 create mode 100644 tools/binman/test/280_ti_secure_rom.dts
 create mode 100644 tools/binman/test/281_ti_secure_rom_combined.dts

diff --git a/board/ti/keys/custMpk.pem b/board/ti/keys/custMpk.pem
new file mode 100644
index 0000000000..adba378c80
--- /dev/null
+++ b/board/ti/keys/custMpk.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/board/ti/keys/ti-degenerate-key.pem b/board/ti/keys/ti-degenerate-key.pem
new file mode 100644
index 0000000000..bd7d3745ad
--- /dev/null
+++ b/board/ti/keys/ti-degenerate-key.pem
@@ -0,0 +1,10 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIBWwIBAAKBgQDRfrnXQaP0k6vRK/gZ+bDflSU6y1JagGeQ/b+QYuiDz14japog
+8fRSu5WBsAxaSaySAUwS3L9Ppw+hGMecmyIJ494aMfZTtk1g49gU58joduiRnu7e
+QSZHMnehhuNlfD7A2tAAKnxIYuabs8zHYM/SS9Ne7t3kIQMbKfUSzNy6qQIBAQIB
+AQJBAOelUA376o6w3HkShXfN+shaOZYqFuTJ9exLMwsLp7DZKXB5F9I4JJ+Vkvho
+k6QWs7vkhleLSYUZknXHYm26ZE0CQQDnhTtd4PTBoZPjPXOeYMJFtEdMNy0XP6ey
+bcce389ugoY7BEkvASrd8PHgJQHziepgWOG4DGp33c64Hfq4zI3NAgEBAgEBAkA0
+RbK4uqoLciQluesTPU6lBy7Se3Dw0F9xBqlF5SR4KI6q+zQrHpBKyFOofMHZgizR
+iCrL55cxEM146zMw3AnF
+-----END RSA PRIVATE KEY-----
diff --git a/tools/binman/btool/openssl.py b/tools/binman/btool/openssl.py
index 3a4dbdd6d7..aad3b61ae2 100644
--- a/tools/binman/btool/openssl.py
+++ b/tools/binman/btool/openssl.py
@@ -15,6 +15,13 @@ import hashlib
 from binman import bintool
 from u_boot_pylib import tools
 
+
+VALID_SHAS = [256, 384, 512, 224]
+SHA_OIDS = {256:'2.16.840.1.101.3.4.2.1',
+            384:'2.16.840.1.101.3.4.2.2',
+            512:'2.16.840.1.101.3.4.2.3',
+            224:'2.16.840.1.101.3.4.2.4'}
+
 class Bintoolopenssl(bintool.Bintool):
     """openssl tool
 
@@ -74,6 +81,243 @@ imageSize              = INTEGER:{len(indata)}
                 '-sha512']
         return self.run_cmd(*args)
 
+    def x509_cert_sysfw(self, cert_fname, input_fname, key_fname, sw_rev,
+                  config_fname, req_dist_name_dict):
+        """Create a certificate to be booted by system firmware
+
+        Args:
+            cert_fname (str): Filename of certificate to create
+            input_fname (str): Filename containing data to sign
+            key_fname (str): Filename of .pem file
+            sw_rev (int): Software revision
+            config_fname (str): Filename to write fconfig into
+            req_dist_name_dict (dict): Dictionary containing key-value pairs of
+            req_distinguished_name section extensions, must contain extensions for
+            C, ST, L, O, OU, CN and emailAddress
+
+        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 ]
+C                      = {req_dist_name_dict['C']}
+ST                     = {req_dist_name_dict['ST']}
+L                      = {req_dist_name_dict['L']}
+O                      = {req_dist_name_dict['O']}
+OU                     = {req_dist_name_dict['OU']}
+CN                     = {req_dist_name_dict['CN']}
+emailAddress           = {req_dist_name_dict['emailAddress']}
+
+[ 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
+1.3.6.1.4.1.294.1.35   = ASN1:SEQUENCE:sysfw_image_load
+
+[ swrv ]
+swrv = INTEGER:{sw_rev}
+
+[ sysfw_image_integrity ]
+shaType                = OID:2.16.840.1.101.3.4.2.3
+shaValue               = FORMAT:HEX,OCT:{hashval}
+imageSize              = INTEGER:{len(indata)}
+
+[ sysfw_image_load ]
+destAddr = FORMAT:HEX,OCT:00000000
+authInPlace = INTEGER:2
+''', 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 x509_cert_rom(self, cert_fname, input_fname, key_fname, sw_rev,
+                  config_fname, req_dist_name_dict, cert_type, bootcore,
+                  bootcore_opts, load_addr, sha):
+        """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
+            sw_rev (int): Software revision
+            config_fname (str): Filename to write fconfig into
+            req_dist_name_dict (dict): Dictionary containing key-value pairs of
+            req_distinguished_name section extensions, must contain extensions for
+            C, ST, L, O, OU, CN and emailAddress
+            cert_type (int): Certification type
+            bootcore (int): Booting core
+            load_addr (int): Load address of image
+            sha (int): Hash function
+
+        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 ]
+C                      = {req_dist_name_dict['C']}
+ST                     = {req_dist_name_dict['ST']}
+L                      = {req_dist_name_dict['L']}
+O                      = {req_dist_name_dict['O']}
+OU                     = {req_dist_name_dict['OU']}
+CN                     = {req_dist_name_dict['CN']}
+emailAddress           = {req_dist_name_dict['emailAddress']}
+
+ [ 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:{cert_type}
+ bootCore = INTEGER:{bootcore}
+ bootCoreOpts = INTEGER:{bootcore_opts}
+ destAddr = FORMAT:HEX,OCT:{load_addr:08x}
+ imageSize = INTEGER:{len(indata)}
+
+ [ image_integrity ]
+ shaType = OID:{SHA_OIDS[sha]}
+ shaValue = FORMAT:HEX,OCT:{hashval}
+
+ [ swrv ]
+ swrv = INTEGER:{sw_rev}
+
+# [ 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:4
+ coreDbgEn = INTEGER:0
+ coreDbgSecEn = INTEGER:0
+''', 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 x509_cert_rom_combined(self, cert_fname, input_fname, key_fname, sw_rev,
+                  config_fname, req_dist_name_dict, load_addr, sha, total_size, num_comps,
+                  sysfw_inner_cert_ext_boot_sequence_string, dm_data_ext_boot_sequence_string,
+                  imagesize_sbl, hashval_sbl, load_addr_sysfw, imagesize_sysfw,
+                  hashval_sysfw, load_addr_sysfw_data, imagesize_sysfw_data,
+                  hashval_sysfw_data, sysfw_inner_cert_ext_boot_block,
+                  dm_data_ext_boot_block):
+        """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
+            sw_rev (int): Software revision
+            config_fname (str): Filename to write fconfig into
+            req_dist_name_dict (dict): Dictionary containing key-value pairs of
+            req_distinguished_name section extensions, must contain extensions for
+            C, ST, L, O, OU, CN and emailAddress
+            cert_type (int): Certification type
+            bootcore (int): Booting core
+            load_addr (int): Load address of image
+            sha (int): Hash function
+
+        Returns:
+            str: Tool output
+        """
+        indata = tools.read_file(input_fname)
+        hashval = hashlib.sha512(indata).hexdigest()
+        sha_type = SHA_OIDS[sha]
+        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 ]
+C                      = {req_dist_name_dict['C']}
+ST                     = {req_dist_name_dict['ST']}
+L                      = {req_dist_name_dict['L']}
+O                      = {req_dist_name_dict['O']}
+OU                     = {req_dist_name_dict['OU']}
+CN                     = {req_dist_name_dict['CN']}
+emailAddress           = {req_dist_name_dict['emailAddress']}
+
+[ 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.9=ASN1:SEQUENCE:ext_boot_info
+
+[swrv]
+swrv=INTEGER:{sw_rev}
+
+[ext_boot_info]
+extImgSize=INTEGER:{total_size}
+numComp=INTEGER:{num_comps}
+sbl=SEQUENCE:sbl
+sysfw=SEQUENCE:sysfw
+sysfw_data=SEQUENCE:sysfw_data
+{sysfw_inner_cert_ext_boot_sequence_string}
+{dm_data_ext_boot_sequence_string}
+
+[sbl]
+compType = INTEGER:1
+bootCore = INTEGER:16
+compOpts = INTEGER:0
+destAddr = FORMAT:HEX,OCT:{load_addr:08x}
+compSize = INTEGER:{imagesize_sbl}
+shaType  = OID:{sha_type}
+shaValue = FORMAT:HEX,OCT:{hashval_sbl}
+
+[sysfw]
+compType = INTEGER:2
+bootCore = INTEGER:0
+compOpts = INTEGER:0
+destAddr = FORMAT:HEX,OCT:{load_addr_sysfw:08x}
+compSize = INTEGER:{imagesize_sysfw}
+shaType  = OID:{sha_type}
+shaValue = FORMAT:HEX,OCT:{hashval_sysfw}
+
+[sysfw_data]
+compType = INTEGER:18
+bootCore = INTEGER:0
+compOpts = INTEGER:0
+destAddr = FORMAT:HEX,OCT:{load_addr_sysfw_data:08x}
+compSize = INTEGER:{imagesize_sysfw_data}
+shaType  = OID:{sha_type}
+shaValue = FORMAT:HEX,OCT:{hashval_sysfw_data}
+
+{sysfw_inner_cert_ext_boot_block}
+
+{dm_data_ext_boot_block}
+        ''', 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
 
diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst
index 7cfe61dd09..e240d5ce62 100644
--- a/tools/binman/entries.rst
+++ b/tools/binman/entries.rst
@@ -2473,3 +2473,28 @@ my-combined-ti-board-config {
 };
 
 
+
+.. _etype_ti_secure:
+
+Entry: ti-secure: Texas Instruments secured binary
+-------------------------------------------------------------
+
+Support for generation of TI secured binary blobs
+
+Properties / Entry arguments:
+    - content: List of phandles to entries to sign
+    - keyfile: Filename of file containing key to sign binary with
+    - sha: Hash function to be used for signing
+
+Output files:
+    - input.<unique_name> - input file passed to openssl
+    - config.<unique_name> - input file generated for openssl (which is
+        used as the config file)
+    - cert.<unique_name> - output file generated by openssl (which is
+        used as the entry contents)
+
+openssl signs the provided data, using the TI templated config file and
+writes the signature in this entry. This allows verification that the
+data is genuine.
+
+
diff --git a/tools/binman/etype/ti_secure.py b/tools/binman/etype/ti_secure.py
new file mode 100644
index 0000000000..bf58d359b8
--- /dev/null
+++ b/tools/binman/etype/ti_secure.py
@@ -0,0 +1,83 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 20
+# Written by Neha Malcom Francis <n-francis at ti.com>
+#
+
+# Support for generation of TI secured binary blobs
+
+from collections import OrderedDict
+import os
+
+from binman.entry import EntryArg
+from binman.etype.x509_cert import Entry_x509_cert
+
+import hashlib
+
+from dtoc import fdt_util
+from u_boot_pylib  import tools
+
+class Entry_ti_secure(Entry_x509_cert):
+    """
+    Support for generation of TI secured binary blobs
+
+    Properties / Entry arguments:
+        - content: List of phandles to entries to sign
+        - keyfile: Filename of file containing key to sign binary with
+        - sha: Hash function to be used for signing
+
+    Output files:
+        - input.<unique_name> - input file passed to openssl
+        - config.<unique_name> - input file generated for openssl (which is
+            used as the config file)
+        - cert.<unique_name> - output file generated by openssl (which is
+            used as the entry contents)
+
+    openssl signs the provided data, using the TI templated config file and
+    writes 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.key_fname = self.GetEntryArgsOrProps([
+            EntryArg('keyfile', str)], required=True)[0]
+        self.sha = fdt_util.GetInt(self._node, 'sha', 512)
+        self.req_dist_name = {'C': 'US',
+                'ST': 'TX',
+                'L': 'Dallas',
+                'O': 'Texas Instruments Incorporated',
+                'OU': 'Processors',
+                'CN': 'TI Support',
+                'emailAddress': 'support at ti.com'}
+
+    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
+        """
+        return super().GetCertificate(required=required, type='sysfw')
+
+    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')
diff --git a/tools/binman/etype/ti_secure_rom.py b/tools/binman/etype/ti_secure_rom.py
new file mode 100644
index 0000000000..1e7bb84aef
--- /dev/null
+++ b/tools/binman/etype/ti_secure_rom.py
@@ -0,0 +1,233 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 20
+# Written by Neha Malcom Francis <n-francis at ti.com>
+#
+
+# Support for generation of TI secured bootloaders booted by ROM
+
+from collections import OrderedDict
+import os
+
+from binman.entry import EntryArg
+from binman.etype.x509_cert import Entry_x509_cert
+
+import hashlib
+
+from dtoc import fdt_util
+from u_boot_pylib  import tools
+
+VALID_SHAS = [256, 384, 512, 224]
+SHA_OIDS = {256:'2.16.840.1.101.3.4.2.1',
+            384:'2.16.840.1.101.3.4.2.2',
+            512:'2.16.840.1.101.3.4.2.3',
+            224:'2.16.840.1.101.3.4.2.4'}
+
+class Entry_ti_secure_rom(Entry_x509_cert):
+    """
+    Support for generation of TI secured bootloaders booted by ROM
+
+    Properties / Entry arguments:
+        - keyfile: Filename of file containing key to sign binary with
+        - combined: boolean if device follows combined boot flow
+        - countersign: boolean if device contains countersigned system firmware
+        - load: load address of SPL
+        - sw-rev: software revision
+        - sha: Hash function to be used for signing
+        - core: core on which bootloader runs, valid cores are 'secure' and 'public'
+        In case of legacy bootflow:
+            - content: phandle of SPL
+        In case of combined bootflow:
+            - sysfw-inner-cert: boolean if binary contains sysfw inner certificate
+            - dm-data: boolean if binary contains dm-data binary
+            - content-sbl: phandle of SPL binary
+            - content-sysfw: phandle of sysfw binary
+            - content-sysfw-data: phandle of sysfw-data or tifs-data binary
+            - content-sysfw-inner-cert (optional): phandle of sysfw inner certificate binary
+            - content-dm-data (optional): phandle of dm-data binary
+            - load-sysfw: load address of sysfw binary
+            - load-sysfw-data: load address of sysfw-data or tifs-data binary
+            - load-sysfw-inner-cert (optional): load address of sysfw inner certificate binary
+            - load-dm-data (optional): load address of dm-data binary
+
+
+    Output files:
+        - input.<unique_name> - input file passed to openssl
+        - config.<unique_name> - input file generated for openssl (which is
+            used as the config file)
+        - cert.<unique_name> - output file generated by openssl (which is
+            used as the entry contents)
+
+    openssl signs the provided data, using the TI templated config file and
+    writes 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.combined = fdt_util.GetBool(self._node, 'combined', False)
+        self.countersign = fdt_util.GetBool(self._node, 'countersign', False)
+        self.load_addr = fdt_util.GetInt(self._node, 'load', 0x00000000)
+        self.sw_rev = fdt_util.GetInt(self._node, 'sw-rev', 1)
+        self.sha = fdt_util.GetInt(self._node, 'sha', 512)
+        self.core = fdt_util.GetString(self._node, 'core', 'secure')
+        self.key_fname = self.GetEntryArgsOrProps([
+            EntryArg('keyfile', str)], required=True)[0]
+        if self.combined:
+            self.sysfw_inner_cert = fdt_util.GetBool(self._node, 'sysfw-inner-cert', False)
+            self.load_addr_sysfw = fdt_util.GetInt(self._node, 'load-sysfw', 0x00000000)
+            self.load_addr_sysfw_data = fdt_util.GetInt(self._node, 'load-sysfw-data', 0x00000000)
+            self.dm_data = fdt_util.GetBool(self._node, 'dm-data', False)
+            if self.dm_data:
+                self.load_addr_dm_data = fdt_util.GetInt(self._node, 'load-dm-data', 0x00000000)
+        self.req_dist_name = {'C': 'US',
+                    'ST': 'TX',
+                    'L': 'Dallas',
+                    'O': 'Texas Instruments Incorporated',
+                    'OU': 'Processors',
+                    'CN': 'TI Support',
+                    'emailAddress': 'support at ti.com'}
+
+    def NonCombinedGetCertificate(self, required):
+        if self.core == 'secure':
+            if self.countersign:
+                self.cert_type = 2
+            else:
+                self.cert_type = 3
+            self.bootcore = 0
+            self.bootcore_opts = 32
+        else:
+            self.cert_type = 1
+            self.bootcore = 16
+            if self.combined:
+                self.bootcore_opts = 32
+            else:
+                self.bootcore_opts = 0
+        return super().GetCertificate(required=required, type='rom')
+
+    def CombinedGetCertificate(self, required):
+        uniq = self.GetUniqueName()
+
+        self.num_comps = 3
+        self.sha_type = SHA_OIDS[self.sha]
+
+        # sbl
+        self.content = fdt_util.GetPhandleList(self._node, 'content-sbl')
+        input_data_sbl = self.GetContents(required)
+        if input_data_sbl is None:
+            return None
+
+        input_fname_sbl = tools.get_output_filename('input.%s' % uniq)
+        tools.write_file(input_fname_sbl, input_data_sbl)
+
+        indata_sbl = tools.read_file(input_fname_sbl)
+        self.hashval_sbl = hashlib.sha512(indata_sbl).hexdigest()
+        self.imagesize_sbl = len(indata_sbl)
+
+        # sysfw
+        self.content = fdt_util.GetPhandleList(self._node, 'content-sysfw')
+        input_data_sysfw = self.GetContents(required)
+        if input_data_sysfw is None:
+            return None
+
+        input_fname_sysfw = tools.get_output_filename('input.%s' % uniq)
+        tools.write_file(input_fname_sysfw, input_data_sysfw)
+
+        indata_sysfw = tools.read_file(input_fname_sysfw)
+        self.hashval_sysfw = hashlib.sha512(indata_sysfw).hexdigest()
+        self.imagesize_sysfw = len(indata_sysfw)
+
+        # sysfw data
+        self.content = fdt_util.GetPhandleList(self._node, 'content-sysfw-data')
+        input_data_sysfw_data = self.GetContents(required)
+        if input_data_sysfw_data is None:
+            return None
+
+        input_fname_sysfw_data = tools.get_output_filename('input.%s' % uniq)
+        tools.write_file(input_fname_sysfw_data, input_data_sysfw_data)
+
+        indata_sysfw_data = tools.read_file(input_fname_sysfw_data)
+        self.hashval_sysfw_data = hashlib.sha512(indata_sysfw_data).hexdigest()
+        self.imagesize_sysfw_data = len(indata_sysfw_data)
+
+        # sysfw inner cert
+        if self.sysfw_inner_cert:
+            self.content = fdt_util.GetPhandleList(self._node, 'content-sysfw-inner-cert')
+            input_data_sysfw_inner_cert = self.GetContents(required)
+            if input_data_sysfw_inner_cert is None:
+                return None
+
+            input_fname_sysfw_inner_cert = tools.get_output_filename('input.%s' % uniq)
+            tools.write_file(input_fname_sysfw_inner_cert, input_data_sysfw_inner_cert)
+
+            indata_sysfw_inner_cert = tools.read_file(input_fname_sysfw_inner_cert)
+            hashval_sysfw_inner_cert = hashlib.sha512(indata_sysfw_inner_cert).hexdigest()
+            imagesize_sysfw_inner_cert = len(indata_sysfw_inner_cert)
+            self.num_comps += 1
+            self.sysfw_inner_cert_ext_boot_sequence_string = "sysfw_inner_cert=SEQUENCE:sysfw_inner_cert"
+            self.sysfw_inner_cert_ext_boot_block = f"""[sysfw_inner_cert]
+compType = INTEGER:3
+bootCore = INTEGER:0
+compOpts = INTEGER:0
+destAddr = FORMAT:HEX,OCT:00000000
+compSize = INTEGER:{imagesize_sysfw_inner_cert}
+shaType  = OID:{self.sha_type}
+shaValue = FORMAT:HEX,OCT:{hashval_sysfw_inner_cert}"""
+        else:
+            self.sysfw_inner_cert_ext_boot_block = ""
+            self.sysfw_inner_cert_ext_boot_sequence_string = ""
+            imagesize_sysfw_inner_cert = 0
+
+        # dm data
+        if self.dm_data:
+            self.content = fdt_util.GetPhandleList(self._node, 'content-dm-data')
+            input_data_dm_data = self.GetContents(required)
+            if input_data_dm_data is None:
+                return None
+
+            input_fname_dm_data = tools.get_output_filename('input.%s' % uniq)
+            tools.write_file(input_fname_dm_data, input_data_dm_data)
+
+            indata_dm_data = tools.read_file(input_fname_dm_data)
+            hashval_dm_data = hashlib.sha512(indata_dm_data).hexdigest()
+            imagesize_dm_data = len(indata_dm_data)
+            self.num_comps += 1
+            self.dm_data_ext_boot_sequence_string = "dm_data=SEQUENCE:dm_data"
+            self.dm_data_ext_boot_block = f"""[dm_data]
+compType = INTEGER:17
+bootCore = INTEGER:16
+compOpts = INTEGER:0
+destAddr = FORMAT:HEX,OCT:{self.load_addr_dm_data:08x}
+compSize = INTEGER:{imagesize_dm_data}
+shaType  = OID:{self.sha_type}
+shaValue = FORMAT:HEX,OCT:{hashval_dm_data}"""
+        else:
+            self.dm_data_ext_boot_sequence_string = ""
+            self.dm_data_ext_boot_block = ""
+            imagesize_dm_data = 0
+
+        self.total_size = self.imagesize_sbl +  self.imagesize_sysfw + self.imagesize_sysfw_data + imagesize_sysfw_inner_cert + imagesize_dm_data
+        return super().GetCertificate(required=required, type='rom-combined')
+
+    def GetCertificate(self, required):
+        if self.combined:
+            return self.CombinedGetCertificate(required)
+        else:
+            return self.NonCombinedGetCertificate(required)
+
+    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')
diff --git a/tools/binman/etype/x509_cert.py b/tools/binman/etype/x509_cert.py
index f80a6ec2d1..d028cfe38c 100644
--- a/tools/binman/etype/x509_cert.py
+++ b/tools/binman/etype/x509_cert.py
@@ -31,6 +31,26 @@ class Entry_x509_cert(Entry_collection):
     def __init__(self, section, etype, node):
         super().__init__(section, etype, node)
         self.openssl = None
+        self.req_dist_name =  None
+        self.cert_type = None
+        self.bootcore = None
+        self.bootcore_opts = None
+        self.load_addr = None
+        self.sha = None
+        self.total_size = None
+        self.num_comps = None
+        self.sysfw_inner_cert_ext_boot_sequence_string = None
+        self.dm_data_ext_boot_sequence_string = None
+        self.imagesize_sbl = None
+        self.hashval_sbl = None
+        self.load_addr_sysfw = None
+        self.imagesize_sysfw = None
+        self.hashval_sysfw = None
+        self.load_addr_sysfw_data = None
+        self.imagesize_sysfw_data = None
+        self.hashval_sysfw_data = None
+        self.sysfw_inner_cert_ext_boot_block = None
+        self.dm_data_ext_boot_block = None
 
     def ReadNode(self):
         super().ReadNode()
@@ -38,13 +58,16 @@ class Entry_x509_cert(Entry_collection):
         self._cert_rev = fdt_util.GetInt(self._node, 'cert-revision-int', 0)
         self.key_fname = self.GetEntryArgsOrProps([
             EntryArg('keyfile', str)], required=True)[0]
+        self.sw_rev = fdt_util.GetInt(self._node, 'sw-rev', 1)
 
-    def GetCertificate(self, required):
+    def GetCertificate(self, required, type='generic'):
         """Get the contents of this entry
 
         Args:
             required: True if the data must be present, False if it is OK to
                 return None
+            type: Type of x509 certificate to generate, current supported ones are
+            'generic', 'sysfw', 'rom'
 
         Returns:
             bytes content of the entry, which is the signed vblock for the
@@ -60,13 +83,61 @@ class Entry_x509_cert(Entry_collection):
         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 type == 'generic':
+            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)
+        elif type == 'sysfw':
+            stdout = self.openssl.x509_cert_sysfw(
+                cert_fname=output_fname,
+                input_fname=input_fname,
+                key_fname=self.key_fname,
+                config_fname=config_fname,
+                sw_rev=self.sw_rev,
+                req_dist_name_dict=self.req_dist_name)
+        elif type == 'rom':
+            stdout = self.openssl.x509_cert_rom(
+                cert_fname=output_fname,
+                input_fname=input_fname,
+                key_fname=self.key_fname,
+                config_fname=config_fname,
+                sw_rev=self.sw_rev,
+                req_dist_name_dict=self.req_dist_name,
+                cert_type=self.cert_type,
+                bootcore=self.bootcore,
+                bootcore_opts=self.bootcore_opts,
+                load_addr=self.load_addr,
+                sha=self.sha
+            )
+        elif type == 'rom-combined':
+            stdout = self.openssl.x509_cert_rom_combined(
+                cert_fname=output_fname,
+                input_fname=input_fname,
+                key_fname=self.key_fname,
+                config_fname=config_fname,
+                sw_rev=self.sw_rev,
+                req_dist_name_dict=self.req_dist_name,
+                load_addr=self.load_addr,
+                sha=self.sha,
+                total_size=self.total_size,
+                num_comps=self.num_comps,
+                sysfw_inner_cert_ext_boot_sequence_string=self.sysfw_inner_cert_ext_boot_sequence_string,
+                dm_data_ext_boot_sequence_string=self.dm_data_ext_boot_sequence_string,
+                imagesize_sbl=self.imagesize_sbl,
+                hashval_sbl=self.hashval_sbl,
+                load_addr_sysfw=self.load_addr_sysfw,
+                imagesize_sysfw=self.imagesize_sysfw,
+                hashval_sysfw=self.hashval_sysfw,
+                load_addr_sysfw_data=self.load_addr_sysfw_data,
+                imagesize_sysfw_data=self.imagesize_sysfw_data,
+                hashval_sysfw_data=self.hashval_sysfw_data,
+                sysfw_inner_cert_ext_boot_block=self.sysfw_inner_cert_ext_boot_block,
+                dm_data_ext_boot_block=self.dm_data_ext_boot_block
+            )
         if stdout is not None:
             data = tools.read_file(output_fname)
         else:
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index 28b5bd19d5..61b71aa87e 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -98,6 +98,7 @@ PRE_LOAD_MAGIC        = b'UBSH'
 PRE_LOAD_VERSION      = 0x11223344.to_bytes(4, 'big')
 PRE_LOAD_HDR_SIZE     = 0x00001000.to_bytes(4, 'big')
 TI_BOARD_CONFIG_DATA  = b'\x00\x01'
+TI_UNSECURE_DATA      = b'unsecuredata'
 
 # Subdirectory of the input dir to use to put test FDTs
 TEST_FDT_SUBDIR       = 'fdts'
@@ -208,6 +209,7 @@ class TestFunctional(unittest.TestCase):
         TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA)
         TestFunctional._MakeInputFile('scp.bin', SCP_DATA)
         TestFunctional._MakeInputFile('rockchip-tpl.bin', ROCKCHIP_TPL_DATA)
+        TestFunctional._MakeInputFile('ti_unsecure.bin', TI_UNSECURE_DATA)
 
         # Add a few .dtb files for testing
         TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR,
@@ -6688,5 +6690,49 @@ fdt         fdtmap                Extract the devicetree blob from the fdtmap
         configlen_noheader = TI_BOARD_CONFIG_DATA*4
         self.assertGreater(data, configlen_noheader)
 
+    def testPackTiSecure(self):
+        """Test that an image with a TI secured binary can be created"""
+        keyfile = self.TestFile('key.key')
+        entry_args = {
+            'keyfile': keyfile,
+        }
+        data = self._DoReadFileDtb('279_ti_secure.dts',
+                                   entry_args=entry_args)[0]
+        self.assertGreater(len(data), len(TI_UNSECURE_DATA))
+
+    def testPackTiSecureMissingTool(self):
+        """Test that an image with a TI secured binary (non-functional) can be created
+        when openssl is missing"""
+        keyfile = self.TestFile('key.key')
+        entry_args = {
+            'keyfile': keyfile,
+        }
+        with test_util.capture_sys_output() as (_, stderr):
+            self._DoTestFile('279_ti_secure.dts',
+                             force_missing_bintools='openssl',
+                             entry_args=entry_args)
+        err = stderr.getvalue()
+        self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
+
+    def testPackTiSecureROM(self):
+        """Test that a ROM image with a TI secured binary can be created"""
+        keyfile = self.TestFile('key.key')
+        entry_args = {
+            'keyfile': keyfile,
+        }
+        data = self._DoReadFileDtb('280_ti_secure_rom.dts',
+                                entry_args=entry_args)[0]
+        self.assertGreater(len(data), len(TI_UNSECURE_DATA))
+
+    def testPackTiSecureROMCombined(self):
+        """Test that a ROM image with a TI secured binary can be created"""
+        keyfile = self.TestFile('key.key')
+        entry_args = {
+            'keyfile': keyfile,
+        }
+        data = self._DoReadFileDtb('281_ti_secure_rom_combined.dts',
+                                entry_args=entry_args)[0]
+        self.assertGreater(len(data), len(TI_UNSECURE_DATA))
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/tools/binman/test/279_ti_secure.dts b/tools/binman/test/279_ti_secure.dts
new file mode 100644
index 0000000000..941d0ab4ca
--- /dev/null
+++ b/tools/binman/test/279_ti_secure.dts
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		ti-secure {
+			content = <&unsecure_binary>;
+		};
+		unsecure_binary: blob-ext {
+			filename = "ti_unsecure.bin";
+		};
+	};
+};
diff --git a/tools/binman/test/280_ti_secure_rom.dts b/tools/binman/test/280_ti_secure_rom.dts
new file mode 100644
index 0000000000..d1313769f4
--- /dev/null
+++ b/tools/binman/test/280_ti_secure_rom.dts
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		ti-secure-rom {
+			content = <&unsecure_binary>;
+		};
+		unsecure_binary: blob-ext {
+			filename = "ti_unsecure.bin";
+		};
+	};
+};
diff --git a/tools/binman/test/281_ti_secure_rom_combined.dts b/tools/binman/test/281_ti_secure_rom_combined.dts
new file mode 100644
index 0000000000..bf872739bc
--- /dev/null
+++ b/tools/binman/test/281_ti_secure_rom_combined.dts
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	binman {
+		ti-secure-rom {
+			content = <&unsecure_binary>;
+			content-sbl = <&unsecure_binary>;
+			content-sysfw = <&unsecure_binary>;
+			content-sysfw-data = <&unsecure_binary>;
+			content-sysfw-inner-cert = <&unsecure_binary>;
+			content-dm-data = <&unsecure_binary>;
+			combined;
+			sysfw-inner-cert;
+			dm-data;
+		};
+		unsecure_binary: blob-ext {
+			filename = "ti_unsecure.bin";
+		};
+	};
+};
-- 
2.34.1



More information about the U-Boot mailing list