diff mbox series

[v2,1/4] binman: Add nxp_imx8mcst etype for i.MX8M flash.bin signing

Message ID 20240503010518.263458-1-marex@denx.de
State Changes Requested
Delegated to: Fabio Estevam
Headers show
Series [v2,1/4] binman: Add nxp_imx8mcst etype for i.MX8M flash.bin signing | expand

Commit Message

Marek Vasut May 3, 2024, 1:05 a.m. UTC
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.

Signed-off-by: Marek Vasut <marex@denx.de>
---
Cc: "NXP i.MX U-Boot Team" <uboot-imx@nxp.com>
Cc: Adam Ford <aford173@gmail.com>
Cc: Alper Nebi Yasak <alpernebiyasak@gmail.com>
Cc: Andrejs Cainikovs <andrejs.cainikovs@toradex.com>
Cc: Angus Ainslie <angus@akkea.ca>
Cc: Emanuele Ghidoli <emanuele.ghidoli@toradex.com>
Cc: Fabio Estevam <festevam@gmail.com>
Cc: Francesco Dolcini <francesco.dolcini@toradex.com>
Cc: Marcel Ziswiler <marcel.ziswiler@toradex.com>
Cc: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
Cc: Simon Glass <sjg@chromium.org>
Cc: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Cc: Stefano Babic <sbabic@denx.de>
Cc: Tim Harvey <tharvey@gateworks.com>
Cc: Tom Rini <trini@konsulko.com>
Cc: kernel@puri.sm
Cc: u-boot@dh-electronics.com
Cc: u-boot@lists.denx.de
---
V2: - Use configparser module for generating the configuration INI file
    - Use config template as an input, parse it, modify only keys of interest
    - Pull magic values into top level variables
    - Rename nxp,csf-key and nxp,img-key to nxp,csf-crt and nxp,img-crt
    - Return unmodified data if signing unrecognized (non-SPL/non-FIT) section
---
 tools/binman/btool/cst.py          |  48 +++++++++
 tools/binman/etype/nxp_imx8mcst.py | 163 +++++++++++++++++++++++++++++
 2 files changed, 211 insertions(+)
 create mode 100644 tools/binman/btool/cst.py
 create mode 100644 tools/binman/etype/nxp_imx8mcst.py

Comments

Francesco Dolcini May 6, 2024, 11:52 a.m. UTC | #1
Hello Marek,

On Fri, May 03, 2024 at 03:05:09AM +0200, Marek Vasut wrote:
> 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.
> 
> Signed-off-by: Marek Vasut <marex@denx.de>

I was not able to test or really look into your series [1], however I can
relate with a comment from Tim Harvey.

I think is important to keep in mind that that signing cannot be done
with key material that is in-tree, because well, that's private, and I
think we should not force people to branch to properly sign the
binaries.

I think that it would be valuable to share how do you foresee this used
in a real environment.

Francesco

[1] so feel free to reference me to any already agreed discussion on the
topic ...
Marek Vasut May 6, 2024, 3:21 p.m. UTC | #2
On 5/6/24 1:52 PM, Francesco Dolcini wrote:
> Hello Marek,
> 
> On Fri, May 03, 2024 at 03:05:09AM +0200, Marek Vasut wrote:
>> 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.
>>
>> Signed-off-by: Marek Vasut <marex@denx.de>
> 
> I was not able to test or really look into your series [1], however I can
> relate with a comment from Tim Harvey.
> 
> I think is important to keep in mind that that signing cannot be done
> with key material that is in-tree, because well, that's private, and I
> think we should not force people to branch to properly sign the
> binaries.
> 
> I think that it would be valuable to share how do you foresee this used
> in a real environment.

I am open to discussion, really.

Currently the most basic approach is implemented -- plug in key material 
either by copying it into build directory, or creating a symlink, or 
adjusting the DT to specify full path to key material.

I am sure this can be expanded to cover other use cases ?
diff mbox series

Patch

diff --git a/tools/binman/btool/cst.py b/tools/binman/btool/cst.py
new file mode 100644
index 00000000000..30e78bdbbd9
--- /dev/null
+++ b/tools/binman/btool/cst.py
@@ -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')
diff --git a/tools/binman/etype/nxp_imx8mcst.py b/tools/binman/etype/nxp_imx8mcst.py
new file mode 100644
index 00000000000..132127ad482
--- /dev/null
+++ b/tools/binman/etype/nxp_imx8mcst.py
@@ -0,0 +1,163 @@ 
+# 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 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 = fdt_util.GetString(self._node, 'nxp,srk-table', 'SRK_1_2_3_4_table.bin')
+        self.csf_crt = fdt_util.GetString(self._node, 'nxp,csf-crt', 'CSF1_1_sha256_4096_65537_v3_usr_crt.pem')
+        self.img_crt = 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')