binman: Add nxp_imx8mcst etype for i.MX8M flash.bin signing

Add new binman etype which allows signing both the SPL and fitImage sections
of i.MX8M flash.bin using CST. There are multiple DT properties which govern
the signing process, nxp,loader-address is the only mandatory one which sets
the SPL signature start address without the imx8mimage header, this should be
SPL text base. The key material can be configured using optional DT properties
nxp,srk-table, nxp,csf-crt, nxp,img-crt, all of which default the key material
names generated by CST tool scripts. The nxp,unlock property can be used to
unlock CAAM access in SPL section.

Reviewed-by: Tim Harvey <tharvey@gateworks.com>
Signed-off-by: Marek Vasut <marex@denx.de>
This commit is contained in:
Marek Vasut 2024-05-21 12:48:23 +02:00 committed by Fabio Estevam
parent 377e91c162
commit bc6beae7c5
4 changed files with 215 additions and 1 deletions

2
.gitignore vendored
View File

@ -73,6 +73,8 @@ fit-dtb.blob*
/capsule.*.efi-capsule
/capsule*.map
/keep-syms-lto.*
/*imx8mimage*
/*imx8mcst*
#
# Generated include files

View File

@ -2213,7 +2213,7 @@ MRPROPER_DIRS += include/config include/generated spl tpl vpl \
# Remove include/asm symlink created by U-Boot before v2014.01
MRPROPER_FILES += .config .config.old include/autoconf.mk* include/config.h \
ctags etags tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS \
drivers/video/fonts/*.S include/asm
drivers/video/fonts/*.S include/asm *imx8mimage* *imx8mcst*
# clean - Delete most, but leave enough to build external modules
#

48
tools/binman/btool/cst.py Normal file
View File

@ -0,0 +1,48 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright 2024 Marek Vasut <marex@denx.de>
#
"""Bintool implementation for cst"""
import re
from binman import bintool
class Bintoolcst(bintool.Bintool):
"""Image generation for U-Boot
This bintool supports running `cst` with some basic parameters as
needed by binman.
"""
def __init__(self, name):
super().__init__(name, 'Sign NXP i.MX image')
# pylint: disable=R0913
def run(self, output_fname=None):
"""Run cst
Args:
output_fname: Output filename to write to
"""
args = []
if output_fname:
args += ['-o', output_fname]
return self.run_cmd(*args)
def fetch(self, method):
"""Fetch handler for cst
This installs cst using the apt utility.
Args:
method (FETCH_...): Method to use
Returns:
True if the file was fetched and now installed, None if a method
other than FETCH_BIN was requested
Raises:
Valuerror: Fetching could not be completed
"""
if method != bintool.FETCH_BIN:
return None
return self.apt_install('imx-code-signing-tool')

View File

@ -0,0 +1,164 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright 2023-2024 Marek Vasut <marex@denx.de>
# Written with much help from Simon Glass <sjg@chromium.org>
#
# Entry-type module for generating the i.MX8M code signing tool
# input configuration file and invocation of cst on generated
# input configuration file and input data to be signed.
#
import configparser
import os
import struct
from collections import OrderedDict
from binman.entry import Entry
from binman.etype.mkimage import Entry_mkimage
from binman.etype.section import Entry_section
from binman import elf
from dtoc import fdt_util
from u_boot_pylib import tools
MAGIC_NXP_IMX_IVT = 0x412000d1
MAGIC_FITIMAGE = 0xedfe0dd0
csf_config_template = """
[Header]
Version = 4.3
Hash Algorithm = sha256
Engine = CAAM
Engine Configuration = 0
Certificate Format = X509
Signature Format = CMS
[Install SRK]
File = "SRK_1_2_3_4_table.bin"
Source index = 0
[Install CSFK]
File = "CSF1_1_sha256_4096_65537_v3_usr_crt.pem"
[Authenticate CSF]
[Unlock]
Engine = CAAM
Features = MID
[Install Key]
Verification index = 0
Target Index = 2
File = "IMG1_1_sha256_4096_65537_v3_usr_crt.pem"
[Authenticate Data]
Verification index = 2
Blocks = 0x1234 0x78 0xabcd "data.bin"
"""
class Entry_nxp_imx8mcst(Entry_mkimage):
"""NXP i.MX8M CST .cfg file generator and cst invoker
Properties / Entry arguments:
- nxp,loader-address - loader address (SPL text base)
"""
def __init__(self, section, etype, node):
super().__init__(section, etype, node)
self.required_props = ['nxp,loader-address']
def ReadNode(self):
super().ReadNode()
self.loader_address = fdt_util.GetInt(self._node, 'nxp,loader-address')
self.srk_table = os.getenv('SRK_TABLE', fdt_util.GetString(self._node, 'nxp,srk-table', 'SRK_1_2_3_4_table.bin'))
self.csf_crt = os.getenv('CSF_KEY', fdt_util.GetString(self._node, 'nxp,csf-crt', 'CSF1_1_sha256_4096_65537_v3_usr_crt.pem'))
self.img_crt = os.getenv('IMG_KEY', fdt_util.GetString(self._node, 'nxp,img-crt', 'IMG1_1_sha256_4096_65537_v3_usr_crt.pem'))
self.unlock = fdt_util.GetBool(self._node, 'nxp,unlock')
self.ReadEntries()
def BuildSectionData(self, required):
data, input_fname, uniq = self.collect_contents_to_file(
self._entries.values(), 'input')
# Parse the input data and figure out what it is that is being signed.
# - If it is mkimage'd imx8mimage, then extract to be signed data size
# from imx8mimage header, and calculate CSF blob offset right past
# the SPL from this information.
# - If it is fitImage, then pad the image to 4k, add generated IVT and
# sign the whole payload, then append CSF blob at the end right past
# the IVT.
signtype = struct.unpack('<I', data[:4])[0]
signbase = self.loader_address
signsize = 0
if signtype == MAGIC_NXP_IMX_IVT: # SPL/imx8mimage
# Sign the payload including imx8mimage header
# (extra 0x40 bytes before the payload)
signbase -= 0x40
signsize = struct.unpack('<I', data[24:28])[0] - signbase
# Remove mkimage generated padding from the end of data
data = data[:signsize]
elif signtype == MAGIC_FITIMAGE: # fitImage
# Align fitImage to 4k
signsize = tools.align(len(data), 0x1000)
data += tools.get_bytes(0, signsize - len(data))
# Add generated IVT
data += struct.pack('<I', MAGIC_NXP_IMX_IVT)
data += struct.pack('<I', signbase + signsize) # IVT base
data += struct.pack('<I', 0)
data += struct.pack('<I', 0)
data += struct.pack('<I', 0)
data += struct.pack('<I', signbase + signsize) # IVT base
data += struct.pack('<I', signbase + signsize + 0x20) # CSF base
data += struct.pack('<I', 0)
else:
# Unknown section type, pass input data through.
return data
# Write out customized data to be signed
output_dname = tools.get_output_filename(f'nxp.cst-input-data.{uniq}')
tools.write_file(output_dname, data)
# Generate CST configuration file used to sign payload
cfg_fname = tools.get_output_filename('nxp.csf-config-txt.%s' % uniq)
config = configparser.ConfigParser()
# Do not make key names lowercase
config.optionxform = str
# Load configuration template and modify keys of interest
config.read_string(csf_config_template)
config['Install SRK']['File'] = '"' + self.srk_table + '"'
config['Install CSFK']['File'] = '"' + self.csf_crt + '"'
config['Install Key']['File'] = '"' + self.img_crt + '"'
config['Authenticate Data']['Blocks'] = hex(signbase) + ' 0 ' + hex(len(data)) + ' "' + str(output_dname) + '"'
if not self.unlock:
config.remove_section('Unlock')
with open(cfg_fname, 'w') as cfgf:
config.write(cfgf)
output_fname = tools.get_output_filename(f'nxp.csf-output-blob.{uniq}')
args = ['-i', cfg_fname, '-o', output_fname]
if self.cst.run_cmd(*args) is not None:
outdata = tools.read_file(output_fname)
return data + outdata
else:
# Bintool is missing; just use the input data as the output
self.record_missing_bintool(self.cst)
return data
def SetImagePos(self, image_pos):
# Customized SoC specific SetImagePos which skips the mkimage etype
# implementation and removes the 0x48 offset introduced there. That
# offset is only used for uImage/fitImage, which is not the case in
# here.
upto = 0x00
for entry in super().GetEntries().values():
entry.SetOffsetSize(upto, None)
# Give up if any entries lack a size
if entry.size is None:
return
upto += entry.size
Entry_section.SetImagePos(self, image_pos)
def AddBintools(self, btools):
super().AddBintools(btools)
self.cst = self.AddBintool(btools, 'cst')