binman: ti-secure: Add support for TI signing

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.

Reviewed-by: Simon Glass <sjg@chromium.org>
[vigneshr@ti.com: fixed inconsist cert generation by multiple packing]
Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
Signed-off-by: Neha Malcom Francis <n-francis@ti.com>
This commit is contained in:
Neha Malcom Francis 2023-07-22 00:14:25 +05:30 committed by Tom Rini
parent 6c66ccf26c
commit 78144826bb
13 changed files with 924 additions and 8 deletions

51
board/ti/keys/custMpk.pem Normal file
View file

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----

View file

@ -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-----

View file

@ -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

View file

@ -1712,6 +1712,71 @@ the included board config binaries. Example::
.. _etype_ti_secure:
Entry: ti-secure: Entry containing a TI x509 certificate binary
---------------------------------------------------------------
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.
.. _etype_ti_secure_rom:
Entry: ti-secure-rom: Entry containing a TI x509 certificate binary for images 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'
- content: phandle of SPL in case of legacy bootflow or phandles of component binaries
in case of combined bootflow
The following properties are only for generating a combined bootflow binary:
- 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.
.. _etype_u_boot:
Entry: u-boot: U-Boot flat binary

View file

@ -0,0 +1,78 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2022-2023 Texas Instruments Incorporated - https://www.ti.com/
# Written by Neha Malcom Francis <n-francis@ti.com>
#
# Support for generation of TI secured binary blobs
from binman.entry import EntryArg
from binman.etype.x509_cert import Entry_x509_cert
from dtoc import fdt_util
class Entry_ti_secure(Entry_x509_cert):
"""Entry containing a TI x509 certificate binary
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@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 certificate binary for the
provided data
"""
return super().GetCertificate(required=required, type='sysfw')
def ObtainContents(self):
data = self.data
if data is None:
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.data
return self.ProcessContentsUpdate(data)
def AddBintools(self, btools):
super().AddBintools(btools)
self.openssl = self.AddBintool(btools, 'openssl')

View file

@ -0,0 +1,249 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2022-2023 Texas Instruments Incorporated - https://www.ti.com/
# Written by Neha Malcom Francis <n-francis@ti.com>
#
# Support for generation of TI secured bootloaders booted by ROM
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):
"""Entry containing a TI x509 certificate binary for images 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'
- content: phandle of SPL in case of legacy bootflow or phandles of component binaries
in case of combined bootflow
The following properties are only for generating a combined bootflow binary:
- 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@ti.com'}
def NonCombinedGetCertificate(self, required):
"""Generate certificate for legacy boot flow
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 certificate binary for the
provided data
"""
if self.core == 'secure':
if self.countersign:
self.cert_type = 3
else:
self.cert_type = 2
self.bootcore = 0
self.bootcore_opts = 32
else:
self.cert_type = 1
self.bootcore = 16
self.bootcore_opts = 0
return super().GetCertificate(required=required, type='rom')
def CombinedGetCertificate(self, required):
"""Generate certificate for combined boot flow
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 certificate binary for the
provided data
"""
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)
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)
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
self.sysfw_inner_cert_ext_boot_block = ""
self.sysfw_inner_cert_ext_boot_sequence_string = ""
imagesize_sysfw_inner_cert = 0
if self.sysfw_inner_cert:
self.content = fdt_util.GetPhandleList(self._node, 'content-sysfw-inner-cert')
input_data_sysfw_inner_cert = self.GetContents(required)
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}"""
# dm data
self.dm_data_ext_boot_sequence_string = ""
self.dm_data_ext_boot_block = ""
imagesize_dm_data = 0
if self.dm_data:
self.content = fdt_util.GetPhandleList(self._node, 'content-dm-data')
input_data_dm_data = self.GetContents(required)
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}"""
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):
"""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 certificate binary for the
provided data
"""
if self.combined:
return self.CombinedGetCertificate(required)
else:
return self.NonCombinedGetCertificate(required)
def ObtainContents(self):
data = self.data
if data is None:
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.data
return self.ProcessContentsUpdate(data)
def AddBintools(self, btools):
super().AddBintools(btools)
self.openssl = self.AddBintool(btools, 'openssl')

View file

@ -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:

View file

@ -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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
TI_UNSECURE_DATA = b'unsecuredata'
# Subdirectory of the input dir to use to put test FDTs
TEST_FDT_SUBDIR = 'fdts'
@ -211,6 +212,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,
@ -6905,5 +6907,55 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
data = self._DoReadFile('279_ti_board_cfg_no_type.dts')
self.assertIn("Schema validation error", str(e.exception))
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]
data_a = self._DoReadFileDtb('288_ti_secure_rom_a.dts',
entry_args=entry_args)[0]
data_b = self._DoReadFileDtb('289_ti_secure_rom_b.dts',
entry_args=entry_args)[0]
self.assertGreater(len(data), len(TI_UNSECURE_DATA))
self.assertGreater(len(data_a), len(TI_UNSECURE_DATA))
self.assertGreater(len(data_b), 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()

View file

@ -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";
};
};
};

View file

@ -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";
};
};
};

View file

@ -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";
};
};
};

View file

@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
ti-secure-rom {
content = <&unsecure_binary>;
core = "secure";
countersign;
};
unsecure_binary: blob-ext {
filename = "ti_unsecure.bin";
};
};
};

View file

@ -0,0 +1,18 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
ti-secure-rom {
content = <&unsecure_binary>;
core = "public";
};
unsecure_binary: blob-ext {
filename = "ti_unsecure.bin";
};
};
};