From 942c8c8e669739d2e8dec67a7ed90158defc93ed Mon Sep 17 00:00:00 2001 From: Paul HENRYS Date: Mon, 24 Feb 2025 22:20:50 +0100 Subject: [PATCH 1/6] rsa: Add rsa_verify_openssl() to use openssl for host builds rsa_verify_openssl() is used in lib/rsa/rsa-verify.c to authenticate data when building host tools. Signed-off-by: Paul HENRYS --- include/image.h | 18 ++++++ lib/rsa/rsa-verify.c | 5 ++ tools/image-host.c | 141 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+) diff --git a/include/image.h b/include/image.h index 07912606f33..c1db8383459 100644 --- a/include/image.h +++ b/include/image.h @@ -1688,6 +1688,24 @@ struct sig_header_s { */ int image_pre_load(ulong addr); +#if defined(USE_HOSTCC) +/** + * rsa_verify_openssl() - Verify a signature against some data with openssl API + * + * Verify a RSA PKCS1.5/PSS signature against an expected hash. + * + * @info: Specifies the key and algorithms + * @region: Pointer to the input data + * @region_count: Number of region + * @sig: Signature + * @sig_len: Number of bytes in the signature + * Return: 0 if verified, -ve on error + */ +int rsa_verify_openssl(struct image_sign_info *info, + const struct image_region region[], int region_count, + uint8_t *sig, uint sig_len); +#endif + /** * fit_image_verify_required_sigs() - Verify signatures marked as 'required' * diff --git a/lib/rsa/rsa-verify.c b/lib/rsa/rsa-verify.c index d3b4f71d6be..b74aaf86e6d 100644 --- a/lib/rsa/rsa-verify.c +++ b/lib/rsa/rsa-verify.c @@ -565,6 +565,11 @@ int rsa_verify(struct image_sign_info *info, uint8_t hash[info->crypto->key_len]; int ret; +#ifdef USE_HOSTCC + if (!info->fdt_blob) + return rsa_verify_openssl(info, region, region_count, sig, sig_len); +#endif + /* * Verify that the checksum-length does not exceed the * rsa-signature-length diff --git a/tools/image-host.c b/tools/image-host.c index e6de34fa059..e19166aeb18 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -19,6 +19,11 @@ #include #endif +#if CONFIG_IS_ENABLED(IMAGE_PRE_LOAD) +#include +#include +#endif + /** * fit_set_hash_value - set hash value in requested has node * @fit: pointer to the FIT format image header @@ -1401,3 +1406,139 @@ int fit_check_sign(const void *fit, const void *key, return ret; } #endif + +#if CONFIG_IS_ENABLED(IMAGE_PRE_LOAD) +/** + * rsa_verify_openssl() - Verify a signature against some data with openssl API + * + * Verify a RSA PKCS1.5/PSS signature against an expected hash. + * + * @info: Specifies the key and algorithms + * @region: Pointer to the input data + * @region_count: Number of region + * @sig: Signature + * @sig_len: Number of bytes in the signature + * Return: 0 if verified, -ve on error + */ +int rsa_verify_openssl(struct image_sign_info *info, + const struct image_region region[], int region_count, + uint8_t *sig, uint sig_len) +{ + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *ckey = NULL; + EVP_MD_CTX *ctx = NULL; + int pad; + int size; + int i; + int ret = 0; + + if (!info) { + fprintf(stderr, "No info provided\n"); + ret = -EINVAL; + goto out; + } + + if (!info->key) { + fprintf(stderr, "No key provided\n"); + ret = -EINVAL; + goto out; + } + + if (!info->checksum) { + fprintf(stderr, "No checksum information\n"); + ret = -EINVAL; + goto out; + } + + if (!info->padding) { + fprintf(stderr, "No padding information\n"); + ret = -EINVAL; + goto out; + } + + if (region_count < 1) { + fprintf(stderr, "Invalid value for region_count: %d\n", region_count); + ret = -EINVAL; + goto out; + } + + pkey = (EVP_PKEY *)info->key; + + ckey = EVP_PKEY_CTX_new(pkey, NULL); + if (!ckey) { + ret = -ENOMEM; + fprintf(stderr, "EVK key context setup failed: %s\n", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } + + size = EVP_PKEY_size(pkey); + if (size > sig_len) { + fprintf(stderr, "Invalid signature size (%d bytes)\n", + size); + ret = -EINVAL; + goto out; + } + + ctx = EVP_MD_CTX_new(); + if (!ctx) { + ret = -ENOMEM; + fprintf(stderr, "EVP context creation failed: %s\n", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } + EVP_MD_CTX_init(ctx); + + if (EVP_DigestVerifyInit(ctx, &ckey, + EVP_get_digestbyname(info->checksum->name), + NULL, pkey) <= 0) { + ret = -EINVAL; + fprintf(stderr, "Verifier setup failed: %s\n", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } + + if (!strcmp(info->padding->name, "pkcs-1.5")) { + pad = RSA_PKCS1_PADDING; + } else if (!strcmp(info->padding->name, "pss")) { + pad = RSA_PKCS1_PSS_PADDING; + } else { + ret = -ENOMSG; + fprintf(stderr, "Unsupported padding: %s\n", + info->padding->name); + goto out; + } + + if (EVP_PKEY_CTX_set_rsa_padding(ckey, pad) <= 0) { + ret = -EINVAL; + fprintf(stderr, "padding setup has failed: %s\n", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } + + for (i=0 ; i < region_count ; ++i) { + if (EVP_DigestVerifyUpdate(ctx, region[i].data, + region[i].size) <= 0) { + ret = -EINVAL; + fprintf(stderr, "Hashing data failed: %s\n", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } + } + + if (EVP_DigestVerifyFinal(ctx, sig, sig_len) <= 0) { + ret = -EINVAL; + fprintf(stderr, "Verifying digest failed: %s\n", + ERR_error_string(ERR_get_error(), NULL)); + goto out; + } +out: + if (ctx) + EVP_MD_CTX_free(ctx); + + if (ret) + fprintf(stderr, "Failed to verify signature\n"); + + return ret; +} +#endif From 47f9186a3e1cf8a18a94feda362025c40ce690cd Mon Sep 17 00:00:00 2001 From: Paul HENRYS Date: Mon, 24 Feb 2025 22:20:51 +0100 Subject: [PATCH 2/6] image: Add an inline declaration of unmap_sysmem() Add an empty inline declaration when compiling tools for a host where unmap_sysmem() is not defined. Signed-off-by: Paul HENRYS --- tools/mkimage.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/mkimage.h b/tools/mkimage.h index 15741f250fd..5d6bcc9301a 100644 --- a/tools/mkimage.h +++ b/tools/mkimage.h @@ -37,6 +37,10 @@ static inline void *map_sysmem(ulong paddr, unsigned long len) return (void *)(uintptr_t)paddr; } +static inline void unmap_sysmem(const void *vaddr) +{ +} + static inline ulong map_to_sysmem(const void *ptr) { return (ulong)(uintptr_t)ptr; From 3d17af85a4797da9d9887df6e6743a11ca12a807 Mon Sep 17 00:00:00 2001 From: Paul HENRYS Date: Mon, 24 Feb 2025 22:20:52 +0100 Subject: [PATCH 3/6] boot: Add support of the pre-load signature for host tools Signed-off-by: Paul HENRYS --- boot/image-pre-load.c | 57 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/boot/image-pre-load.c b/boot/image-pre-load.c index cc19017404c..adf3b341a20 100644 --- a/boot/image-pre-load.c +++ b/boot/image-pre-load.c @@ -3,13 +3,24 @@ * Copyright (C) 2021 Philippe Reynes */ +#ifdef USE_HOSTCC +#include "mkimage.h" +#else #include -DECLARE_GLOBAL_DATA_PTR; -#include #include +DECLARE_GLOBAL_DATA_PTR; +#endif /* !USE_HOSTCC*/ +#include #include +#ifdef USE_HOSTCC +/* Define compat stuff for use in tools. */ +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +#endif + /* * Offset of the image * @@ -17,6 +28,47 @@ DECLARE_GLOBAL_DATA_PTR; */ ulong image_load_offset; +#ifdef USE_HOSTCC +/* Host tools use these implementations to setup information related to the + * pre-load signatures + */ +static struct image_sig_info *host_info; + +#define log_info(fmt, args...) printf(fmt, ##args) +#define log_err(fmt, args...) printf(fmt, ##args) + +void image_pre_load_sig_set_info(struct image_sig_info *info) +{ + host_info = info; +} + +/* + * This function sets a pointer to information for the signature check. + * It expects that host_info has been initially provision by the host + * application. + * + * return: + * < 0 => an error has occurred + * 0 => OK + */ +static int image_pre_load_sig_setup(struct image_sig_info *info) +{ + if (!info) { + log_err("ERROR: info is NULL\n"); + return -EINVAL; + } + + if (!host_info) { + log_err("ERROR: host_info is NULL\n"); + log_err("ERROR: Set it with image_pre_load_sig_set_info()\n"); + return -EINVAL; + } + + memcpy(info, host_info, sizeof(struct image_sig_info)); + + return 0; +} +#else /* * This function gathers information about the signature check * that could be done before launching the image. @@ -106,6 +158,7 @@ static int image_pre_load_sig_setup(struct image_sig_info *info) out: return ret; } +#endif /* !USE_HOSTCC */ static int image_pre_load_sig_get_magic(ulong addr, u32 *magic) { From 76de7b061f6df1bd8ddc423ef1980489d72da873 Mon Sep 17 00:00:00 2001 From: Paul HENRYS Date: Mon, 24 Feb 2025 22:20:53 +0100 Subject: [PATCH 4/6] tools: Add preload_check_sign to authenticate images with a pre-load preload_check_sign is added so that it can be used to authenticate images signed with the pre-load signature supported by binman and U-Boot. It could also be used to test the signature in binman tests signing images with the pre-load. Signed-off-by: Paul HENRYS --- tools/.gitignore | 1 + tools/Kconfig | 5 ++ tools/Makefile | 5 ++ tools/preload_check_sign.c | 160 +++++++++++++++++++++++++++++++++++++ 4 files changed, 171 insertions(+) create mode 100644 tools/preload_check_sign.c diff --git a/tools/.gitignore b/tools/.gitignore index 0108c567309..6a5c613f772 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -29,6 +29,7 @@ /mxsboot /ncb /prelink-riscv +/preload_check_sign /printinitialenv /proftool /relocate-rela diff --git a/tools/Kconfig b/tools/Kconfig index 01ff0fcf748..8e272ee99a8 100644 --- a/tools/Kconfig +++ b/tools/Kconfig @@ -9,6 +9,11 @@ config MKIMAGE_DTC_PATH some cases the system dtc may not support all required features and the path to a different version should be given here. +config TOOLS_IMAGE_PRE_LOAD + def_bool y + help + Enable pre-load signature support in the tools builds. + config TOOLS_CRC16 def_bool y help diff --git a/tools/Makefile b/tools/Makefile index 237fa900a24..e5f5eea47c7 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -66,6 +66,7 @@ mkenvimage-objs := mkenvimage.o os_support.o generated/lib/crc32.o hostprogs-y += dumpimage mkimage hostprogs-$(CONFIG_TOOLS_LIBCRYPTO) += fit_info fit_check_sign hostprogs-$(CONFIG_TOOLS_LIBCRYPTO) += fdt_add_pubkey +hostprogs-$(CONFIG_TOOLS_LIBCRYPTO) += preload_check_sign ifneq ($(CONFIG_CMD_BOOTEFI_SELFTEST)$(CONFIG_FWU_MDATA_GPT_BLK),) hostprogs-y += file2include @@ -89,6 +90,8 @@ ECDSA_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix generated/lib/ecdsa/, ecdsa- AES_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix generated/lib/aes/, \ aes-encrypt.o aes-decrypt.o) +PRELOAD_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := generated/boot/image-pre-load.o + # Cryptographic helpers and image types that depend on openssl/libcrypto LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := \ generated/lib/fdt-libcrypto.o \ @@ -158,6 +161,7 @@ fit_info-objs := $(dumpimage-mkimage-objs) fit_info.o fit_check_sign-objs := $(dumpimage-mkimage-objs) fit_check_sign.o fdt_add_pubkey-objs := $(dumpimage-mkimage-objs) fdt_add_pubkey.o file2include-objs := file2include.o +preload_check_sign-objs := $(dumpimage-mkimage-objs) $(PRELOAD_OBJS-y) preload_check_sign.o ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_TOOLS_LIBCRYPTO),) # Add CFG_MXS into host CFLAGS, so we can check whether or not register @@ -195,6 +199,7 @@ HOSTLDLIBS_dumpimage := $(HOSTLDLIBS_mkimage) HOSTLDLIBS_fit_info := $(HOSTLDLIBS_mkimage) HOSTLDLIBS_fit_check_sign := $(HOSTLDLIBS_mkimage) HOSTLDLIBS_fdt_add_pubkey := $(HOSTLDLIBS_mkimage) +HOSTLDLIBS_preload_check_sign := $(HOSTLDLIBS_mkimage) hostprogs-$(CONFIG_EXYNOS5250) += mkexynosspl hostprogs-$(CONFIG_EXYNOS5420) += mkexynosspl diff --git a/tools/preload_check_sign.c b/tools/preload_check_sign.c new file mode 100644 index 00000000000..ebead459273 --- /dev/null +++ b/tools/preload_check_sign.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Check a file including a preload header including a signature + * + * Copyright (c) 2025 Paul HENRYS + * + * Binman makes it possible to generate a preload header signing part or the + * complete file. The tool preload_check_sign allows to verify and authenticate + * a file starting with a preload header. + */ +#include +#include +#include +#include +#include +#include + +extern void image_pre_load_sig_set_info(struct image_sig_info *info); +extern int image_pre_load_sig(ulong addr); + +static void usage(char *cmdname) +{ + fprintf(stderr, "Usage: %s -f file -k PEM key file\n" + " -f ==> set file which should be checked\n" + " -k ==> PEM key file\n" + " -a ==> algo (default: sha256,rsa2048)\n" + " -p ==> padding (default: pkcs-1.5)\n" + " -h ==> help\n", + cmdname); + exit(EXIT_FAILURE); +} + +int main(int argc, char **argv) +{ + int ret = 0; + char cmdname[256]; + char *file = NULL; + char *keyfile = NULL; + int c; + FILE *fp = NULL; + FILE *fp_key = NULL; + size_t bytes; + long filesize; + void *buffer = NULL; + EVP_PKEY *pkey = NULL; + char *algo = "sha256,rsa2048"; + char *padding = "pkcs-1.5"; + struct image_sig_info info = {0}; + + strncpy(cmdname, *argv, sizeof(cmdname) - 1); + cmdname[sizeof(cmdname) - 1] = '\0'; + while ((c = getopt(argc, argv, "f:k:a:p:h")) != -1) + switch (c) { + case 'f': + file = optarg; + break; + case 'k': + keyfile = optarg; + break; + case 'a': + algo = optarg; + break; + case 'p': + padding = optarg; + break; + default: + usage(cmdname); + break; + } + + if (!file) { + fprintf(stderr, "%s: Missing file\n", *argv); + usage(*argv); + } + + if (!keyfile) { + fprintf(stderr, "%s: Missing key file\n", *argv); + usage(*argv); + } + + fp = fopen(file, "r"); + if (!fp) { + fprintf(stderr, "Error opening file: %s\n", file); + ret = EXIT_FAILURE; + goto out; + } + + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + rewind(fp); + + buffer = malloc(filesize); + if (!buffer) { + fprintf(stderr, "Memory allocation failed"); + ret = EXIT_FAILURE; + goto out; + } + + bytes = fread(buffer, 1, filesize, fp); + if (bytes != filesize) { + fprintf(stderr, "Error reading file\n"); + ret = EXIT_FAILURE; + goto out; + } + + fp_key = fopen(keyfile, "r"); + if (!fp_key) { + fprintf(stderr, "Error opening file: %s\n", keyfile); + ret = EXIT_FAILURE; + goto out; + } + + /* Attempt to read the private key */ + pkey = PEM_read_PrivateKey(fp_key, NULL, NULL, NULL); + if (!pkey) { + /* If private key reading fails, try reading as a public key */ + fseek(fp_key, 0, SEEK_SET); + pkey = PEM_read_PUBKEY(fp_key, NULL, NULL, NULL); + } + if (!pkey) { + fprintf(stderr, "Unable to retrieve the public key: %s\n", + ERR_error_string(ERR_get_error(), NULL)); + ret = EXIT_FAILURE; + goto out; + } + + info.algo_name = algo; + info.padding_name = padding; + info.key = (uint8_t *)pkey; + info.mandatory = 1; + info.sig_size = EVP_PKEY_size(pkey); + if (info.sig_size < 0) { + fprintf(stderr, "Fail to retrieve the signature size: %s\n", + ERR_error_string(ERR_get_error(), NULL)); + ret = EXIT_FAILURE; + goto out; + } + + /* Compute signature information */ + info.sig_info.name = info.algo_name; + info.sig_info.padding = image_get_padding_algo(info.padding_name); + info.sig_info.checksum = image_get_checksum_algo(info.sig_info.name); + info.sig_info.crypto = image_get_crypto_algo(info.sig_info.name); + info.sig_info.key = info.key; + info.sig_info.keylen = info.key_len; + + /* Check the signature */ + image_pre_load_sig_set_info(&info); + ret = image_pre_load_sig((ulong)buffer); +out: + if (fp) + fclose(fp); + if (fp_key) + fclose(fp_key); + if (info.key) + EVP_PKEY_free(pkey); + free(buffer); + + exit(ret); +} From 6bb3687e2ac613d97712740d082094c4ea371230 Mon Sep 17 00:00:00 2001 From: Paul HENRYS Date: Mon, 24 Feb 2025 22:20:54 +0100 Subject: [PATCH 5/6] configs: Enable the pre-load signature in tools-only_defconfig pre-load related config options are enabled to have support of it in host tools. 'CONFIG_FIT_SIGNATURE=y' is being automatically removed since it is selected by CONFIG_IMAGE_PRE_LOAD_SIG. Signed-off-by: Paul HENRYS --- configs/tools-only_defconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configs/tools-only_defconfig b/configs/tools-only_defconfig index cecd26175d1..e64bb768440 100644 --- a/configs/tools-only_defconfig +++ b/configs/tools-only_defconfig @@ -9,10 +9,11 @@ CONFIG_EFI_LOADER=n CONFIG_ANDROID_BOOT_IMAGE=y CONFIG_TIMESTAMP=y CONFIG_FIT=y -CONFIG_FIT_SIGNATURE=y CONFIG_BOOTSTD_FULL=n CONFIG_BOOTMETH_CROS=n CONFIG_BOOTMETH_VBE=n +CONFIG_IMAGE_PRE_LOAD=y +CONFIG_IMAGE_PRE_LOAD_SIG=y CONFIG_USE_BOOTCOMMAND=y CONFIG_BOOTCOMMAND="run distro_bootcmd" CONFIG_CMD_BOOTD=n From b9b87d01efc496d18bbc17c58c552d54a06ef6ba Mon Sep 17 00:00:00 2001 From: Paul HENRYS Date: Mon, 24 Feb 2025 22:20:55 +0100 Subject: [PATCH 6/6] binman: Authenticate the image when testing the preload signature Use preload_check_sign to authenticate the generated image when testing the preload signature in testPreLoad(). Signed-off-by: Paul HENRYS --- tools/binman/ftest.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index a553ca9e564..8cf867fd3fe 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -762,6 +762,16 @@ class TestFunctional(unittest.TestCase): return False return True + def _CheckPreload(self, image, key, algo="sha256,rsa2048", + padding="pkcs-1.5"): + try: + tools.run('preload_check_sign', '-k', key, '-a', algo, '-p', + padding, '-f', image) + except: + self.fail('Expected image signed with a pre-load') + return False + return True + def testRun(self): """Test a basic run with valid args""" result = self._RunBinman('-h') @@ -5781,9 +5791,14 @@ fdt fdtmap Extract the devicetree blob from the fdtmap data = self._DoReadFileDtb( '230_pre_load.dts', entry_args=entry_args, extra_indirs=[os.path.join(self._binman_dir, 'test')])[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 testPreLoadNoKey(self): """Test an image with a pre-load heade0r with missing key"""