package/uboot-tools: fix host FIT signature support

U-Boot host tools use the tools configuration namespace when code calls
CONFIG_IS_ENABLED(). With USE_HOSTCC, CONFIG_IS_ENABLED(FIT_SIGNATURE)
resolves to CONFIG_TOOLS_FIT_SIGNATURE, while
CONFIG_VAL(FIT_SIGNATURE_MAX_SIZE) resolves to
CONFIG_TOOLS_FIT_SIGNATURE_MAX_SIZE.

The host-uboot-tools package only generates the old CONFIG_FIT_SIGNATURE
define. This makes tools/Makefile include fit_check_sign, but the host code
sees FIT signature support as disabled and the OpenSSL-backed signing and
verification objects are omitted. As a result, mkimage accepts a signature
node without writing its value or injecting a required public key.
fit_check_sign then has no required key and reports success without
checking the configuration signature.

A FIT-support-only configuration never exercises this path, which is why
the existing hash-only runtime test still passes.

Generate the tools FIT signature options needed by the host code and pass
CONFIG_TOOLS_LIBCRYPTO=y so the OpenSSL-backed signing, verification and
cipher objects are selected. Keep CONFIG_FIT_SIGNATURE=y in the make
options because U-Boot tools/Makefile still uses it to build
fit_check_sign.

Extend TestHostUbootTools to create an RSA-signed FIT, require a 256-byte
configuration signature and a required public key, verify the FIT, corrupt
the signature, and require verification to fail.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
[Julien: add comments in runtime test]
Signed-off-by: Julien Olivain <ju.o@free.fr>
This commit is contained in:
James Hilliard
2026-07-28 13:45:41 -06:00
committed by Julien Olivain
parent df6f3ad43f
commit 928cc5dc5c
4 changed files with 121 additions and 2 deletions

View File

@@ -126,7 +126,10 @@ define HOST_UBOOT_TOOLS_CONFIGURE_CMDS
mkdir -p $(@D)/include/generated
$(if $(BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT),$(UBOOT_TOOLS_ENABLE_HASH_ALGOS))
$(if $(BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT),echo '#define CONFIG_TOOLS_FIT_PRINT 1' >> $(@D)/include/generated/autoconf.h)
echo $(if $(BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT),'#define CONFIG_FIT_SIGNATURE 1') >> $(@D)/include/generated/autoconf.h
$(if $(BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT),echo '#define CONFIG_TOOLS_IMAGE_PRE_LOAD 1' >> $(@D)/include/generated/autoconf.h)
$(if $(BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT),echo '#define CONFIG_TOOLS_RSASSA_PSS 1' >> $(@D)/include/generated/autoconf.h)
$(if $(BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT),echo '#define CONFIG_TOOLS_FIT_SIGNATURE 1' >> $(@D)/include/generated/autoconf.h)
$(if $(BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT),echo '#define CONFIG_TOOLS_FIT_SIGNATURE_MAX_SIZE 0x10000000' >> $(@D)/include/generated/autoconf.h)
mkdir -p $(@D)/include/asm
touch $(@D)/include/asm/linkage.h
endef
@@ -142,7 +145,7 @@ HOST_UBOOT_TOOLS_DEPENDENCIES += host-dtc
endif
ifeq ($(BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT),y)
HOST_UBOOT_TOOLS_MAKE_OPTS += CONFIG_FIT_SIGNATURE=y CONFIG_FIT_SIGNATURE_MAX_SIZE=0x10000000
HOST_UBOOT_TOOLS_MAKE_OPTS += CONFIG_TOOLS_LIBCRYPTO=y CONFIG_FIT_SIGNATURE=y
HOST_UBOOT_TOOLS_DEPENDENCIES += host-openssl
define HOST_UBOOT_TOOLS_INSTALL_FIT_CHECK_SIGN
$(INSTALL) -m 0755 -D $(@D)/tools/fit_check_sign $(HOST_DIR)/bin/fit_check_sign

View File

@@ -1,11 +1,14 @@
import hashlib
import re
import subprocess
import zlib
from pathlib import Path
import infra.basetest
EXAMPLE_ITS = Path(__file__).parent / "test_uboot_tools/example.its"
KEY_DTS = Path(__file__).parent / "test_uboot_tools/key.dts"
SIGNED_ITS = Path(__file__).parent / "test_uboot_tools/signed.its"
def get_hashes(output: str) -> dict[str, str]:
@@ -72,6 +75,7 @@ class TestHostUbootTools(infra.basetest.BRHostPkgTest):
"""
BR2_PACKAGE_HOST_UBOOT_TOOLS=y
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
"""
def test_run(self):
@@ -86,3 +90,70 @@ class TestHostUbootTools(infra.basetest.BRHostPkgTest):
self.assertEqual(expected[h], reported[h])
# Python does not have built-in CRC16 support, just check it is present
self.assertIn("crc16-ccitt", reported)
# See U-Boot FIT Signature Verification documentation:
# https://source.denx.de/u-boot/u-boot/-/blob/v2026.07/doc/usage/fit/signature.rst
keydir = Path(self.builddir) / "keys"
keydir.mkdir(exist_ok=True)
# We generate a prublic/private key pair to sign the image.
cmd = [
"host/bin/openssl", "req", "-batch", "-new", "-x509", "-nodes",
"-newkey", "rsa:2048", "-keyout", str(keydir / "dev.key"),
"-out", str(keydir / "dev.crt"), "-subj", "/CN=Buildroot FIT test",
]
infra.run_cmd_on_host(self.builddir, cmd)
# We compile the empty dtb that will be used to store the
# public key.
cmd = [
"host/bin/dtc", "-I", "dts", "-O", "dtb", "-p", "0x1000",
"-o", "test-key.dtb", str(KEY_DTS),
]
infra.run_cmd_on_host(self.builddir, cmd)
# We sign the image and write the public key in our key
# storage dtb.
cmd = [
"host/bin/mkimage", "-f", str(SIGNED_ITS), "-k", str(keydir),
"-K", "test-key.dtb", "-r", "signed.fit",
]
infra.run_cmd_on_host(self.builddir, cmd)
# We check there is a signature present in the FIT image.
cmd = [
"host/bin/fdtget", "-t", "bx", "signed.fit",
"/configurations/config-1/signature-1", "value",
]
signature = infra.run_cmd_on_host(self.builddir, cmd).split()
self.assertEqual(len(signature), 256)
# We check the key is marked as required for the configuration.
cmd = [
"host/bin/fdtget", "-t", "s", "test-key.dtb",
"/signature/key-dev", "required",
]
required = infra.run_cmd_on_host(self.builddir, cmd).strip()
self.assertEqual(required, "conf")
# We actually check the signature is valid.
cmd = [
"host/bin/fit_check_sign", "-f", "signed.fit",
"-k", "test-key.dtb",
]
infra.run_cmd_on_host(self.builddir, cmd)
# We "corrupt" the signature, by setting it to zero.
cmd = [
"host/bin/fdtput", "-t", "bx", "signed.fit",
"/configurations/config-1/signature-1", "value", "00",
]
infra.run_cmd_on_host(self.builddir, cmd)
# We check again the siganute, and expect a failure.
cmd = [
"host/bin/fit_check_sign", "-f", "signed.fit",
"-k", "test-key.dtb",
]
with self.assertRaises(subprocess.CalledProcessError):
infra.run_cmd_on_host(self.builddir, cmd)

View File

@@ -0,0 +1,7 @@
/dts-v1/;
/* This empty dts will be used for storing the public key.
* The dtb is populated later by the "mkimage -K" command. */
/ {
};

View File

@@ -0,0 +1,38 @@
/dts-v1/;
/ {
description = "Signed test FIT";
#address-cells = <1>;
images {
kernel {
description = "This file, pretending to be a kernel";
data = /incbin/("example.its");
type = "kernel";
arch = "arm64";
os = "linux";
compression = "none";
load = <0x40400000>;
entry = <0x40400000>;
hash-1 {
algo = "sha256";
};
};
};
configurations {
default = "config-1";
config-1 {
description = "Signed test entry";
kernel = "kernel";
signature-1 {
algo = "sha256,rsa2048";
key-name-hint = "dev";
sign-images = "kernel";
};
};
};
};