summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2026-05-05 10:37:16 -0600
committerTom Rini <[email protected]>2026-05-05 10:37:16 -0600
commitae8987f4e690f397f507c4b1efabd68b3ed08db6 (patch)
treebb61ec3250f48048c4ef5516ecbcf5270d545642 /tools
parente6112ab2d7261dc606f2d25599904f9d20b237ac (diff)
parentb20d69e5f57bee299380bb30112856e51d8f26b0 (diff)
Merge patch series "binman: Fix preload signing with encrypted FIT"HEADmaster
Yan WANG <[email protected]> says: This series improves the reliability and efficiency of binman preload header generation and test it against an encrypted FIT image signed with a preload header. When a preload header references other entries (e.g. an encrypted FIT) through the collection etype, the referenced entries may be rebuilt multiple times during binman processing. This becomes problematic when the referenced entry produces non-deterministic output, such as FIT encryption using random IVs or timestamps, since rebuilding the entry changes the data. This series ensures that referenced entries are built only once and that preload signing is performed after all data is collected. It also avoids unnecessary repacking or repeated signing operations by the preload. The changes include: * generate preload header placeholders in ObtainContents() and sign data only once in ProcessContentsUpdate() * mark referenced entries as build_done in the collection etype to avoid rebuilding data * add a functional test for signing an encrypted FIT with a preload header Link: https://lore.kernel.org/r/[email protected]
Diffstat (limited to 'tools')
-rw-r--r--tools/binman/etype/collection.py9
-rw-r--r--tools/binman/etype/pre_load.py9
-rw-r--r--tools/binman/etype/section.py5
-rw-r--r--tools/binman/ftest.py21
-rw-r--r--tools/binman/test/security/pre_load_fit_encrypted.dts63
5 files changed, 97 insertions, 10 deletions
diff --git a/tools/binman/etype/collection.py b/tools/binman/etype/collection.py
index c532aafe3e7..866ea8dcc1e 100644
--- a/tools/binman/etype/collection.py
+++ b/tools/binman/etype/collection.py
@@ -45,12 +45,17 @@ class Entry_collection(Entry):
self.Info('Getting contents, required=%s' % required)
data = bytearray()
for entry_phandle in self.content:
- entry_data = self.section.GetContentsByPhandle(entry_phandle, self,
- required)
+ entry, entry_data = self.section.GetContentsByPhandle(
+ entry_phandle, self, required)
if not required and entry_data is None:
self.Info('Contents not available yet')
# Data not available yet
return None
+
+ # Mark referenced entries as build_done to avoid rebuilding
+ if required:
+ entry.mark_build_done()
+
data += entry_data
self.Info('Returning contents size %x' % len(data))
diff --git a/tools/binman/etype/pre_load.py b/tools/binman/etype/pre_load.py
index 00f1a896767..0d953cb258e 100644
--- a/tools/binman/etype/pre_load.py
+++ b/tools/binman/etype/pre_load.py
@@ -152,14 +152,11 @@ class Entry_pre_load(Entry_collection):
return data + pad
def ObtainContents(self):
- """Obtain a placeholder for the header contents"""
- # wait that the image is available
- self.image = self.GetContents(False)
- if self.image is None:
- return False
- self.SetContents(self._CreateHeader())
+ """Create a placeholder for the header"""
+ self.SetContents(tools.get_bytes(0, self.header_size))
return True
def ProcessContents(self):
+ self.image = self.GetContents(True)
data = self._CreateHeader()
return self.ProcessContentsUpdate(data)
diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py
index 6a26d687056..8530b7ee17f 100644
--- a/tools/binman/etype/section.py
+++ b/tools/binman/etype/section.py
@@ -557,7 +557,8 @@ class Entry_section(Entry):
return None
Returns:
- data from associated entry (as a string), or None if not found
+ tuple: (entry, data) where entry is the Entry object and data is
+ from that entry (as a string), or (entry, None) if data not found
"""
node = self._node.GetFdt().LookupPhandle(phandle)
if not node:
@@ -565,7 +566,7 @@ class Entry_section(Entry):
entry = self.FindEntryByNode(node)
if not entry:
source_entry.Raise("Cannot find entry for node '%s'" % node.name)
- return entry.GetData(required)
+ return entry, entry.GetData(required)
def LookupEntry(self, entries, sym_name, msg):
"""Look up the entry for a binman symbol
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index ca5149ee654..da8325f820a 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -5895,6 +5895,27 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
data = self._DoReadFileDtb('security/pre_load_invalid_key.dts',
entry_args=entry_args)
+ def testPreLoadEncryptedFit(self):
+ """Test an encrypted FIT image with a pre-load header"""
+ entry_args = {
+ 'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
+ }
+ data = tools.read_file(self.TestFile("fit/aes256.bin"))
+ self._MakeInputFile("keys/aes256.bin", data)
+
+ keys_subdir = os.path.join(self._indir, "keys")
+ data = self._DoReadFileDtb(
+ 'security/pre_load_fit_encrypted.dts', entry_args=entry_args,
+ extra_indirs=[keys_subdir])[0]
+
+ image_fname = tools.get_output_filename('image.bin')
+ is_signed = self._CheckPreload(image_fname, self.TestFile("dev.key"))
+
+ self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
+ self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
+ self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
+ self.assertEqual(is_signed, True)
+
def _CheckSafeUniqueNames(self, *images):
"""Check all entries of given images for unsafe unique names"""
for image in images:
diff --git a/tools/binman/test/security/pre_load_fit_encrypted.dts b/tools/binman/test/security/pre_load_fit_encrypted.dts
new file mode 100644
index 00000000000..f5e9bf9426c
--- /dev/null
+++ b/tools/binman/test/security/pre_load_fit_encrypted.dts
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ binman {
+ pre-load {
+ content = <&image>;
+ algo-name = "sha256,rsa2048";
+ key-name = "dev.key";
+ header-size = <4096>;
+ version = <0x11223344>;
+ };
+
+ image: fit {
+ fit,encrypt;
+ description = "Test a FIT with encrypted data and signed with a preload";
+ #address-cells = <1>;
+
+ images {
+ u-boot {
+ description = "U-Boot";
+ type = "firmware";
+ arch = "arm64";
+ os = "U-Boot";
+ compression = "none";
+ load = <00000000>;
+ entry = <00000000>;
+ cipher {
+ algo = "aes256";
+ key-name-hint = "aes256";
+ };
+ u-boot-nodtb {
+ };
+ };
+ fdt-1 {
+ description = "Flattened Device Tree blob";
+ type = "flat_dt";
+ arch = "arm64";
+ compression = "none";
+ cipher {
+ algo = "aes256";
+ key-name-hint = "aes256";
+ };
+ u-boot-dtb {
+ };
+ };
+ };
+
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ description = "Boot U-Boot with FDT blob";
+ firmware = "u-boot";
+ fdt = "fdt-1";
+ };
+ };
+ };
+ };
+};