[PATCH] tools: binman: add build key directory option
James Hilliard
james.hilliard1 at gmail.com
Fri Jul 3 23:33:03 CEST 2026
U-Boot's Makefile passes KEYDIR to direct mkimage invocations, but
internal binman builds only received the generic BINMAN_INDIRS search
path. FIT entries with fit,sign or fit,encrypt therefore had to infer
the key directory from include directories before invoking mkimage -k.
Add a binman build -k/--keydir option and pass KEYDIR through with
that option from the top-level Makefile. FIT entries prefer this key
directory over include-dir discovery and then pass it to mkimage -k.
Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
---
Makefile | 1 +
tools/binman/binman.rst | 10 +++++++-
tools/binman/cmdline.py | 2 ++
tools/binman/control.py | 1 +
tools/binman/etype/fit.py | 24 +++++++++++-------
tools/binman/ftest.py | 51 ++++++++++++++++++++++++++++++++++++---
tools/binman/state.py | 21 ++++++++++++++++
7 files changed, 97 insertions(+), 13 deletions(-)
diff --git a/Makefile b/Makefile
index d8287323c43..27be2a81e83 100644
--- a/Makefile
+++ b/Makefile
@@ -1689,6 +1689,7 @@ cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \
-I . -I $(srctree)/board/$(BOARDDIR) -I $(srctree) \
$(foreach f,$(of_list_dirs),-I $(f)) -a of-list=$(of_list) \
$(foreach f,$(BINMAN_INDIRS),-I $(f)) \
+ $(if $(KEYDIR),-k $(KEYDIR)) \
-a atf-bl1-path=${BL1} \
-a atf-bl31-path=${BL31} \
-a tee-os-path=${TEE} \
diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst
index 366491089ad..8770b279ff8 100644
--- a/tools/binman/binman.rst
+++ b/tools/binman/binman.rst
@@ -1711,7 +1711,7 @@ Usage::
binman build [-h] [-a ENTRY_ARG] [-b BOARD] [-d DT] [--fake-dtb]
[--fake-ext-blobs] [--force-missing-bintools FORCE_MISSING_BINTOOLS]
- [-i IMAGE] [-I INDIR] [-m] [-M] [-n] [-O OUTDIR] [-p] [-u]
+ [-i IMAGE] [-I INDIR] [-k KEYDIR] [-m] [-M] [-n] [-O OUTDIR] [-p] [-u]
[--update-fdt-in-elf UPDATE_FDT_IN_ELF] [-W]
Options:
@@ -1738,6 +1738,9 @@ Options:
Add a path to the list of directories to use for input files. This can be
specified multiple times to add more than one path.
+-k KEYDIR, --keydir KEYDIR
+ Directory containing keys for signing/encryption.
+
-m, --map
Output a map file for each image. See `Map files`_.
@@ -2161,6 +2164,11 @@ BINMAN_INDIRS
Sets the search path for input files used by binman by adding one or more
`-I` arguments. See :ref:`External blobs`.
+KEYDIR
+ Sets the key directory passed to FIT entries by adding a `-k` argument.
+ FIT entries use this directory for mkimage's `-k` argument when `fit,sign`
+ or `fit,encrypt` is enabled.
+
BINMAN_TOOLPATHS
Sets the search path for external tool used by binman by adding one or more
`--toolpath` arguments. See :ref:`External tools`.
diff --git a/tools/binman/cmdline.py b/tools/binman/cmdline.py
index 9632ec115e5..cd4d05dfb69 100644
--- a/tools/binman/cmdline.py
+++ b/tools/binman/cmdline.py
@@ -128,6 +128,8 @@ controlled by a description in the board device tree.'''
help='Image filename to build (if not specified, build all)')
build_parser.add_argument('-I', '--indir', action='append',
help='Add a path to the list of directories to use for input files')
+ build_parser.add_argument('-k', '--keydir', type=str,
+ help='Directory containing keys for signing/encryption')
build_parser.add_argument('-m', '--map', action='store_true',
default=False, help='Output a map file for each image')
build_parser.add_argument('-M', '--allow-missing', action='store_true',
diff --git a/tools/binman/control.py b/tools/binman/control.py
index 816f7c1eba2..84586d1baf1 100644
--- a/tools/binman/control.py
+++ b/tools/binman/control.py
@@ -923,6 +923,7 @@ def Binman(args):
tools.set_input_dirs(args.indir)
tools.prepare_output_dir(args.outdir, args.preserve)
state.SetEntryArgs(args.entry_arg)
+ state.SetKeydir(args.keydir)
state.SetThreads(args.threads)
images = PrepareImagesAndDtbs(dtb_fname, args.image,
diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py
index f28b1e6b4cb..404e33d3022 100644
--- a/tools/binman/etype/fit.py
+++ b/tools/binman/etype/fit.py
@@ -14,6 +14,7 @@ import os
from binman.entry import Entry, EntryArg
from binman.etype.section import Entry_section
from binman import elf
+from binman import state
from dtoc import fdt_util
from dtoc.fdt import Fdt
from u_boot_pylib import tools
@@ -105,18 +106,20 @@ class Entry_fit(Entry_section):
fit,sign
Enable signing FIT images via mkimage as described in
verified-boot.rst.
- If the property is found and fit,engine is not set, the private
- keys path is detected among binman include directories and passed to
- mkimage via -k flag. All the keys required for signing FIT must be
- available at time of signing and must be located in single include
- directory.
+ If the property is found and fit,engine is not set, the key
+ directory passed with binman build -k is passed to mkimage via the
+ -k flag. If no key directory is provided, the private keys path is
+ detected among binman include directories. All the keys required for
+ signing FIT must be available at time of signing and must be
+ located in a single directory.
fit,encrypt
Enable data encryption in FIT images via mkimage. If the property
- is found, the keys path is detected among binman include
- directories and passed to mkimage via -k flag. All the keys
+ is found, the key directory passed with binman build -k is passed
+ to mkimage via the -k flag. If no key directory is provided, the
+ keys path is detected among binman include directories. All the keys
required for encrypting the FIT must be available at the time of
- encrypting and must be located in a single include directory.
+ encrypting and must be located in a single directory.
Incompatible with fit,engine.
@@ -485,6 +488,8 @@ class Entry_fit(Entry_section):
includes 'generator' entries which are used to create the FIT,
but should not be processed as real entries. This is set up once
we have the entries
+ _keydir (str): Key directory from the binman build -k option, if
+ provided
_loadables (list of str): List of generated split-elf nodes, each
a node name
_remove_props (list of str): Value of of-spl-remove-props EntryArg,
@@ -500,6 +505,7 @@ class Entry_fit(Entry_section):
self._fit_list_prop = None
self._fit_default_dt = None
self._priv_entries = {}
+ self._keydir = state.GetKeydir()
self._loadables = []
self._remove_props = []
props = self.GetEntryArgsOrProps(
@@ -701,7 +707,7 @@ class Entry_fit(Entry_section):
args.update({'engine': engine})
# If no engine, keys must exist locally, find them
if engine is None:
- keydir = self._get_keys_dir(data)
+ keydir = self._keydir or self._get_keys_dir(data)
elif self._fit_props.get('fit,encrypt') is not None:
self.Raise('fit,engine currently does not support encryption')
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index bf98b268ac1..0fc00fade3a 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -385,7 +385,8 @@ class TestFunctional(unittest.TestCase):
use_expanded=False, verbosity=None, allow_missing=False,
allow_fake_blobs=False, extra_indirs=None, threads=None,
test_section_timeout=False, update_fdt_in_elf=None,
- force_missing_bintools='', ignore_missing=False, output_dir=None):
+ force_missing_bintools='', ignore_missing=False,
+ output_dir=None, keydir=None):
"""Run binman with a given test file
Args:
@@ -419,6 +420,7 @@ class TestFunctional(unittest.TestCase):
ignore_missing (bool): True to return success even if there are
missing blobs or bintools
output_dir: Specific output directory to use for image using -O
+ keydir: Directory containing keys for signing/encryption
Returns:
int return code, 0 on success
@@ -438,6 +440,8 @@ class TestFunctional(unittest.TestCase):
if test_section_timeout:
args.append('--test-section-timeout')
args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)]
+ if keydir:
+ args += ['-k', keydir]
if map:
args.append('-m')
if update_dtb:
@@ -518,7 +522,7 @@ class TestFunctional(unittest.TestCase):
def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False,
verbosity=None, allow_fake_blobs=True, map=False,
update_dtb=False, entry_args=None, reset_dtbs=True,
- extra_indirs=None, threads=None):
+ extra_indirs=None, threads=None, keydir=None):
"""Run binman and return the resulting image
This runs binman with a given test file and then reads the resulting
@@ -549,6 +553,7 @@ class TestFunctional(unittest.TestCase):
extra_indirs: Extra input directories to add using -I
threads: Number of threads to use (None for default, 0 for
single-threaded)
+ keydir: Directory containing keys for signing/encryption
Returns:
Tuple:
@@ -575,7 +580,7 @@ class TestFunctional(unittest.TestCase):
entry_args=entry_args, use_real_dtb=use_real_dtb,
use_expanded=use_expanded, verbosity=verbosity,
allow_fake_blobs=allow_fake_blobs, extra_indirs=extra_indirs,
- threads=threads)
+ threads=threads, keydir=keydir)
self.assertEqual(0, retcode)
out_dtb_fname = tools.get_output_filename('u-boot.dtb.out')
@@ -8167,6 +8172,31 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
self.assertIsNotNone(signature)
self.assertIsNotNone(signature.props.get('value'))
+ def testFitSignKeydir(self):
+ """Test that binman build -k is passed to mkimage"""
+ if not elf.ELF_TOOLS:
+ self.skipTest('Python elftools not available')
+ data = tools.read_file(self.TestFile("fit/rsa2048.key"))
+ self._MakeInputFile("keys/rsa2048.key", data)
+
+ test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
+ keys_subdir = os.path.join(self._indir, "keys")
+ entry_args = {
+ 'of-list': 'test-fdt1',
+ 'default-dt': 'test-fdt1',
+ 'atf-bl31-path': 'bl31.elf',
+ }
+ data = self._DoReadFileDtb(
+ 'fit/signature.dts',
+ entry_args=entry_args,
+ extra_indirs=[test_subdir],
+ keydir=keys_subdir)[0]
+
+ dtb = fdt.Fdt.FromData(data)
+ dtb.Scan()
+ signature = dtb.GetNode('/configurations/conf-uboot-1/signature')
+ self.assertIsNotNone(signature.props.get('value'))
+
def testFitSignEngineSimple(self):
"""Test that image with FIT and signature nodes can be signed with an
OpenSSL Engine"""
@@ -8507,6 +8537,21 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
dec_data = file.read()
self.assertEqual(U_BOOT_NODTB_DATA, dec_data.encode('ascii'))
+ def testSimpleFitEncryptedDataKeydir(self):
+ """Test that encrypted FIT data uses binman build -k"""
+ 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(
+ 'fit/encrypt_data.dts',
+ keydir=keys_subdir)[0]
+
+ fit = fdt.Fdt.FromData(data)
+ fit.Scan()
+ node = fit.GetNode('/images/u-boot')
+ self.assertIn('data-size-unciphered', fit.GetProps(node))
+
def testSimpleFitEncryptedDataMissingKey(self):
"""Test an image with a FIT containing data to be encrypted but with a missing key"""
with self.assertRaises(ValueError) as e:
diff --git a/tools/binman/state.py b/tools/binman/state.py
index f4d885c772a..15e97f68893 100644
--- a/tools/binman/state.py
+++ b/tools/binman/state.py
@@ -41,6 +41,9 @@ fdt_path_prefix = ''
# Arguments passed to binman to provide arguments to entries
entry_args = {}
+# Directory containing keys for FIT signing/encryption
+keydir = None
+
# True to use fake device-tree files for testing (see U_BOOT_DTB_DATA in
# ftest.py)
use_fake_dtb = False
@@ -206,6 +209,24 @@ def GetEntryArgBool(name):
val = GetEntryArg(name)
return val and val not in ['n', '0']
+def SetKeydir(pathname):
+ """Set the key directory to use for signing/encryption
+
+ Args:
+ pathname: Key directory, or None if not provided
+ """
+ global keydir
+
+ keydir = pathname
+
+def GetKeydir():
+ """Get the key directory to use for signing/encryption
+
+ Returns:
+ Key directory, or None if not provided
+ """
+ return keydir
+
def Prepare(images, dtb):
"""Get device tree files ready for use
--
2.53.0
More information about the U-Boot
mailing list