diff options
author | Tom Rini <trini@konsulko.com> | 2022-03-31 14:12:30 -0400 |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2022-03-31 14:12:30 -0400 |
commit | 52d8100b1d60b656e3e311e3312fed43d388088a (patch) | |
tree | cb370025bcc386ef741cce94bda1b4dd58fe2d76 | |
parent | 23e354f82c04a1c070ca59907abc6b042761b0e7 (diff) | |
parent | 7bebc11c42351c8f0364f0e3eb922f5af7b6e826 (diff) | |
download | u-boot-WIP/31Mar2022-next.zip u-boot-WIP/31Mar2022-next.tar.gz u-boot-WIP/31Mar2022-next.tar.bz2 |
Merge branch '2022-03-31-image-add-a-stage-pre-load' into nextWIP/31Mar2022-next
To quote the author:
This series adds a stage pre-load before launching an image. This stage
is used to read a header before the image and this header contains the
signature of the full image. So u-boot may check the full image before
using any data of the image.
The support of this header is added to binman, and a command verify
checks the signature of a blob and set the u-boot env variable
"loadaddr_verified" to the beginning of the "real" image.
The support of this header is only added to binman, but it may also be
added to mkimage.
39 files changed, 1544 insertions, 29 deletions
@@ -1342,6 +1342,7 @@ cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \ -a tpl-bss-pad=$(if $(CONFIG_TPL_SEPARATE_BSS),,1) \ -a spl-dtb=$(CONFIG_SPL_OF_REAL) \ -a tpl-dtb=$(CONFIG_TPL_OF_REAL) \ + -a pre-load-key-path=${PRE_LOAD_KEY_PATH} \ $(BINMAN_$(@F)) OBJCOPYFLAGS_u-boot.ldr.hex := -I binary -O ihex diff --git a/arch/Kconfig b/arch/Kconfig index 1b35fda..8450fdb 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -205,6 +205,7 @@ config SANDBOX imply KEYBOARD imply PHYSMEM imply GENERATE_ACPI_TABLE + imply BINMAN config SH bool "SuperH architecture" diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 66b813f..826db26 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -7,6 +7,9 @@ #define USB_CLASS_HUB 9 / { + binman { + }; + chosen { stdout-path = "/serial"; }; diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 3d206fd..05c1cd5 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -61,6 +61,9 @@ osd0 = "/osd"; }; + binman { + }; + config { testing-bool; testing-int = <123>; diff --git a/boot/Kconfig b/boot/Kconfig index a395529..b3580bd 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -1023,6 +1023,61 @@ config RESET_TO_RETRY endmenu +menu "Image support" + +config IMAGE_PRE_LOAD + bool "Image pre-load support" + help + Enable an image pre-load stage in the SPL. + This pre-load stage allows to do some manipulation + or check (for example signature check) on an image + before launching it. + +config SPL_IMAGE_PRE_LOAD + bool "Image pre-load support within SPL" + depends on SPL && IMAGE_PRE_LOAD + help + Enable an image pre-load stage in the SPL. + This pre-load stage allows to do some manipulation + or check (for example signature check) on an image + before launching it. + +config IMAGE_PRE_LOAD_SIG + bool "Image pre-load signature support" + depends on IMAGE_PRE_LOAD + select FIT_SIGNATURE + select RSA + select RSA_VERIFY_WITH_PKEY + help + Enable signature check support in the pre-load stage. + For this feature a very simple header is added before + the image with few fields: + - a magic + - the image size + - the signature + All other information (header size, type of signature, + ...) are provided in the node /image/pre-load/sig of + u-boot. + +config SPL_IMAGE_PRE_LOAD_SIG + bool "Image pre-load signature support witin SPL" + depends on SPL_IMAGE_PRE_LOAD && IMAGE_PRE_LOAD_SIG + select SPL_FIT_SIGNATURE + select SPL_RSA + select SPL_RSA_VERIFY_WITH_PKEY + help + Enable signature check support in the pre-load stage in the SPL. + For this feature a very simple header is added before + the image with few fields: + - a magic + - the image size + - the signature + All other information (header size, type of signature, + ...) are provided in the node /image/pre-load/sig of + u-boot. + +endmenu + config USE_BOOTARGS bool "Enable boot arguments" help diff --git a/boot/Makefile b/boot/Makefile index 75366c8..1b99e6e 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += fdt_region.o obj-$(CONFIG_$(SPL_TPL_)FIT) += image-fit.o obj-$(CONFIG_$(SPL_)MULTI_DTB_FIT) += boot_fit.o common_fit.o +obj-$(CONFIG_$(SPL_TPL_)IMAGE_PRE_LOAD) += image-pre-load.o obj-$(CONFIG_$(SPL_TPL_)IMAGE_SIGN_INFO) += image-sig.o obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += image-fit-sig.o obj-$(CONFIG_$(SPL_TPL_)FIT_CIPHER) += image-cipher.o diff --git a/boot/bootm.c b/boot/bootm.c index 00c00ae..714406a 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -87,6 +87,33 @@ static int bootm_start(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static ulong bootm_data_addr(int argc, char *const argv[]) +{ + ulong addr; + + if (argc > 0) + addr = simple_strtoul(argv[0], NULL, 16); + else + addr = image_load_addr; + + return addr; +} + +static int bootm_pre_load(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong data_addr = bootm_data_addr(argc, argv); + int ret = 0; + + if (CONFIG_IS_ENABLED(CMD_BOOTM_PRE_LOAD)) + ret = image_pre_load(data_addr); + + if (ret) + ret = CMD_RET_FAILURE; + + return ret; +} + static int bootm_find_os(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -677,6 +704,9 @@ int do_bootm_states(struct cmd_tbl *cmdtp, int flag, int argc, if (states & BOOTM_STATE_START) ret = bootm_start(cmdtp, flag, argc, argv); + if (!ret && (states & BOOTM_STATE_PRE_LOAD)) + ret = bootm_pre_load(cmdtp, flag, argc, argv); + if (!ret && (states & BOOTM_STATE_FINDOS)) ret = bootm_find_os(cmdtp, flag, argc, argv); @@ -866,6 +896,9 @@ static const void *boot_get_kernel(struct cmd_tbl *cmdtp, int flag, int argc, &fit_uname_config, &fit_uname_kernel); + if (CONFIG_IS_ENABLED(CMD_BOOTM_PRE_LOAD)) + img_addr += image_load_offset; + bootstage_mark(BOOTSTAGE_ID_CHECK_MAGIC); /* check image type, for FIT images get FIT kernel node */ diff --git a/boot/image-pre-load.c b/boot/image-pre-load.c new file mode 100644 index 0000000..78d8906 --- /dev/null +++ b/boot/image-pre-load.c @@ -0,0 +1,416 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Philippe Reynes <philippe.reynes@softathome.com> + */ + +#include <common.h> +#include <asm/global_data.h> +DECLARE_GLOBAL_DATA_PTR; +#include <image.h> +#include <mapmem.h> + +#include <u-boot/sha256.h> + +#define IMAGE_PRE_LOAD_SIG_MAGIC 0x55425348 +#define IMAGE_PRE_LOAD_SIG_OFFSET_MAGIC 0 +#define IMAGE_PRE_LOAD_SIG_OFFSET_IMG_LEN 4 +#define IMAGE_PRE_LOAD_SIG_OFFSET_SIG 8 + +#define IMAGE_PRE_LOAD_PATH "/image/pre-load/sig" +#define IMAGE_PRE_LOAD_PROP_ALGO_NAME "algo-name" +#define IMAGE_PRE_LOAD_PROP_PADDING_NAME "padding-name" +#define IMAGE_PRE_LOAD_PROP_SIG_SIZE "signature-size" +#define IMAGE_PRE_LOAD_PROP_PUBLIC_KEY "public-key" +#define IMAGE_PRE_LOAD_PROP_MANDATORY "mandatory" + +#ifndef CONFIG_SYS_BOOTM_LEN +/* use 8MByte as default max gunzip size */ +#define CONFIG_SYS_BOOTM_LEN 0x800000 +#endif + +/* + * Information in the device-tree about the signature in the header + */ +struct image_sig_info { + char *algo_name; /* Name of the algo (eg: sha256,rsa2048) */ + char *padding_name; /* Name of the padding */ + u8 *key; /* Public signature key */ + int key_len; /* Length of the public key */ + u32 sig_size; /* size of the signature (in the header) */ + int mandatory; /* Set if the signature is mandatory */ + + struct image_sign_info sig_info; /* Signature info */ +}; + +/* + * Header of the signature header + */ +struct sig_header_s { + u32 magic; + u32 version; + u32 header_size; + u32 image_size; + u32 offset_img_sig; + u32 flags; + u32 reserved0; + u32 reserved1; + u8 sha256_img_sig[SHA256_SUM_LEN]; +}; + +#define SIG_HEADER_LEN (sizeof(struct sig_header_s)) + +/* + * Offset of the image + * + * This value is used to skip the header before really launching the image + */ +ulong image_load_offset; + +/* + * This function gathers information about the signature check + * that could be done before launching the image. + * + * return: + * < 0 => an error has occurred + * 0 => OK + * 1 => no setup + */ +static int image_pre_load_sig_setup(struct image_sig_info *info) +{ + const void *algo_name, *padding_name, *key, *mandatory; + const u32 *sig_size; + int key_len; + int node, ret = 0; + + if (!info) { + log_err("ERROR: info is NULL for image pre-load sig check\n"); + ret = -EINVAL; + goto out; + } + + memset(info, 0, sizeof(*info)); + + node = fdt_path_offset(gd_fdt_blob(), IMAGE_PRE_LOAD_PATH); + if (node < 0) { + log_info("INFO: no info for image pre-load sig check\n"); + ret = 1; + goto out; + } + + algo_name = fdt_getprop(gd_fdt_blob(), node, + IMAGE_PRE_LOAD_PROP_ALGO_NAME, NULL); + if (!algo_name) { + printf("ERROR: no algo_name for image pre-load sig check\n"); + ret = -EINVAL; + goto out; + } + + padding_name = fdt_getprop(gd_fdt_blob(), node, + IMAGE_PRE_LOAD_PROP_PADDING_NAME, NULL); + if (!padding_name) { + log_info("INFO: no padding_name provided, so using pkcs-1.5\n"); + padding_name = "pkcs-1.5"; + } + + sig_size = fdt_getprop(gd_fdt_blob(), node, + IMAGE_PRE_LOAD_PROP_SIG_SIZE, NULL); + if (!sig_size) { + log_err("ERROR: no signature-size for image pre-load sig check\n"); + ret = -EINVAL; + goto out; + } + + key = fdt_getprop(gd_fdt_blob(), node, + IMAGE_PRE_LOAD_PROP_PUBLIC_KEY, &key_len); + if (!key) { + log_err("ERROR: no key for image pre-load sig check\n"); + ret = -EINVAL; + goto out; + } + + info->algo_name = (char *)algo_name; + info->padding_name = (char *)padding_name; + info->key = (uint8_t *)key; + info->key_len = key_len; + info->sig_size = fdt32_to_cpu(*sig_size); + + mandatory = fdt_getprop(gd_fdt_blob(), node, + IMAGE_PRE_LOAD_PROP_MANDATORY, NULL); + if (mandatory && !strcmp((char *)mandatory, "yes")) + info->mandatory = 1; + + /* 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; + + out: + return ret; +} + +static int image_pre_load_sig_get_magic(ulong addr, u32 *magic) +{ + struct sig_header_s *sig_header; + int ret = 0; + + sig_header = (struct sig_header_s *)map_sysmem(addr, SIG_HEADER_LEN); + if (!sig_header) { + log_err("ERROR: can't map first header\n"); + ret = -EFAULT; + goto out; + } + + *magic = fdt32_to_cpu(sig_header->magic); + + unmap_sysmem(sig_header); + + out: + return ret; +} + +static int image_pre_load_sig_get_header_size(ulong addr, u32 *header_size) +{ + struct sig_header_s *sig_header; + int ret = 0; + + sig_header = (struct sig_header_s *)map_sysmem(addr, SIG_HEADER_LEN); + if (!sig_header) { + log_err("ERROR: can't map first header\n"); + ret = -EFAULT; + goto out; + } + + *header_size = fdt32_to_cpu(sig_header->header_size); + + unmap_sysmem(sig_header); + + out: + return ret; +} + +/* + * return: + * < 0 => no magic and magic mandatory (or error when reading magic) + * 0 => magic found + * 1 => magic NOT found + */ +static int image_pre_load_sig_check_magic(struct image_sig_info *info, ulong addr) +{ + u32 magic; + int ret = 1; + + ret = image_pre_load_sig_get_magic(addr, &magic); + if (ret < 0) + goto out; + + if (magic != IMAGE_PRE_LOAD_SIG_MAGIC) { + if (info->mandatory) { + log_err("ERROR: signature is mandatory\n"); + ret = -EINVAL; + goto out; + } + ret = 1; + goto out; + } + + ret = 0; /* magic found */ + + out: + return ret; +} + +static int image_pre_load_sig_check_header_sig(struct image_sig_info *info, ulong addr) +{ + void *header; + struct image_region reg; + u32 sig_len; + u8 *sig; + int ret = 0; + + /* Only map header of the header and its signature */ + header = (void *)map_sysmem(addr, SIG_HEADER_LEN + info->sig_size); + if (!header) { + log_err("ERROR: can't map header\n"); + ret = -EFAULT; + goto out; + } + + reg.data = header; + reg.size = SIG_HEADER_LEN; + + sig = (uint8_t *)header + SIG_HEADER_LEN; + sig_len = info->sig_size; + + ret = info->sig_info.crypto->verify(&info->sig_info, ®, 1, sig, sig_len); + if (ret) { + log_err("ERROR: header signature check has failed (err=%d)\n", ret); + ret = -EINVAL; + goto out_unmap; + } + + out_unmap: + unmap_sysmem(header); + + out: + return ret; +} + +static int image_pre_load_sig_check_img_sig_sha256(struct image_sig_info *info, ulong addr) +{ + struct sig_header_s *sig_header; + u32 header_size, offset_img_sig; + void *header; + u8 sha256_img_sig[SHA256_SUM_LEN]; + int ret = 0; + + sig_header = (struct sig_header_s *)map_sysmem(addr, SIG_HEADER_LEN); + if (!sig_header) { + log_err("ERROR: can't map first header\n"); + ret = -EFAULT; + goto out; + } + + header_size = fdt32_to_cpu(sig_header->header_size); + offset_img_sig = fdt32_to_cpu(sig_header->offset_img_sig); + + header = (void *)map_sysmem(addr, header_size); + if (!header) { + log_err("ERROR: can't map header\n"); + ret = -EFAULT; + goto out_sig_header; + } + + sha256_csum_wd(header + offset_img_sig, info->sig_size, + sha256_img_sig, CHUNKSZ_SHA256); + + ret = memcmp(sig_header->sha256_img_sig, sha256_img_sig, SHA256_SUM_LEN); + if (ret) { + log_err("ERROR: sha256 of image signature is invalid\n"); + ret = -EFAULT; + goto out_header; + } + + out_header: + unmap_sysmem(header); + out_sig_header: + unmap_sysmem(sig_header); + out: + return ret; +} + +static int image_pre_load_sig_check_img_sig(struct image_sig_info *info, ulong addr) +{ + struct sig_header_s *sig_header; + u32 header_size, image_size, offset_img_sig; + void *image; + struct image_region reg; + u32 sig_len; + u8 *sig; + int ret = 0; + + sig_header = (struct sig_header_s *)map_sysmem(addr, SIG_HEADER_LEN); + if (!sig_header) { + log_err("ERROR: can't map first header\n"); + ret = -EFAULT; + goto out; + } + + header_size = fdt32_to_cpu(sig_header->header_size); + image_size = fdt32_to_cpu(sig_header->image_size); + offset_img_sig = fdt32_to_cpu(sig_header->offset_img_sig); + + unmap_sysmem(sig_header); + + image = (void *)map_sysmem(addr, header_size + image_size); + if (!image) { + log_err("ERROR: can't map full image\n"); + ret = -EFAULT; + goto out; + } + + reg.data = image + header_size; + reg.size = image_size; + + sig = (uint8_t *)image + offset_img_sig; + sig_len = info->sig_size; + + ret = info->sig_info.crypto->verify(&info->sig_info, ®, 1, sig, sig_len); + if (ret) { + log_err("ERROR: signature check has failed (err=%d)\n", ret); + ret = -EINVAL; + goto out_unmap_image; + } + + log_info("INFO: signature check has succeed\n"); + + out_unmap_image: + unmap_sysmem(image); + + out: + return ret; +} + +int image_pre_load_sig(ulong addr) +{ + struct image_sig_info info; + int ret; + + ret = image_pre_load_sig_setup(&info); + if (ret < 0) + goto out; + if (ret > 0) { + ret = 0; + goto out; + } + + ret = image_pre_load_sig_check_magic(&info, addr); + if (ret < 0) + goto out; + if (ret > 0) { + ret = 0; + goto out; + } + + /* Check the signature of the signature header */ + ret = image_pre_load_sig_check_header_sig(&info, addr); + if (ret < 0) + goto out; + + /* Check sha256 of the image signature */ + ret = image_pre_load_sig_check_img_sig_sha256(&info, addr); + if (ret < 0) + goto out; + + /* Check the image signature */ + ret = image_pre_load_sig_check_img_sig(&info, addr); + if (!ret) { + u32 header_size; + + ret = image_pre_load_sig_get_header_size(addr, &header_size); + if (ret) { + log_err("%s: can't get header size\n", __func__); + ret = -EINVAL; + goto out; + } + + image_load_offset += header_size; + } + + out: + return ret; +} + +int image_pre_load(ulong addr) +{ + int ret = 0; + + image_load_offset = 0; + + if (CONFIG_IS_ENABLED(IMAGE_PRE_LOAD_SIG)) + ret = image_pre_load_sig(addr); + + return ret; +} diff --git a/cmd/Kconfig b/cmd/Kconfig index 1d84012..7bd9546 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -194,6 +194,16 @@ config CMD_BOOTM help Boot an application image from the memory. +config CMD_BOOTM_PRE_LOAD + bool "enable pre-load on bootm" + depends on CMD_BOOTM + depends on IMAGE_PRE_LOAD + default n + help + Enable support of stage pre-load for the bootm command. + This stage allow to check or modify the image provided + to the bootm command. + config BOOTM_EFI bool "Support booting UEFI FIT images" depends on CMD_BOOTEFI && CMD_BOOTM && FIT diff --git a/cmd/bootm.c b/cmd/bootm.c index e8b7066..1f70ee9 100644 --- a/cmd/bootm.c +++ b/cmd/bootm.c @@ -44,6 +44,9 @@ static int do_imls(struct cmd_tbl *cmdtp, int flag, int argc, static struct cmd_tbl cmd_bootm_sub[] = { U_BOOT_CMD_MKENT(start, 0, 1, (void *)BOOTM_STATE_START, "", ""), U_BOOT_CMD_MKENT(loados, 0, 1, (void *)BOOTM_STATE_LOADOS, "", ""), +#ifdef CONFIG_CMD_BOOTM_PRE_LOAD + U_BOOT_CMD_MKENT(preload, 0, 1, (void *)BOOTM_STATE_PRE_LOAD, "", ""), +#endif #ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH U_BOOT_CMD_MKENT(ramdisk, 0, 1, (void *)BOOTM_STATE_RAMDISK, "", ""), #endif @@ -57,6 +60,20 @@ static struct cmd_tbl cmd_bootm_sub[] = { U_BOOT_CMD_MKENT(go, 0, 1, (void *)BOOTM_STATE_OS_GO, "", ""), }; +#if defined(CONFIG_CMD_BOOTM_PRE_LOAD) +static ulong bootm_get_addr(int argc, char *const argv[]) +{ + ulong addr; + + if (argc > 0) + addr = hextoul(argv[0], NULL); + else + addr = image_load_addr; + + return addr; +} +#endif + static int do_bootm_subcommand(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -70,7 +87,12 @@ static int do_bootm_subcommand(struct cmd_tbl *cmdtp, int flag, int argc, if (c) { state = (long)c->cmd; if (state == BOOTM_STATE_START) - state |= BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER; + state |= BOOTM_STATE_PRE_LOAD | BOOTM_STATE_FINDOS | + BOOTM_STATE_FINDOTHER; +#if defined(CONFIG_CMD_BOOTM_PRE_LOAD) + if (state == BOOTM_STATE_PRE_LOAD) + state |= BOOTM_STATE_START; +#endif } else { /* Unrecognized command */ return CMD_RET_USAGE; @@ -84,6 +106,12 @@ static int do_bootm_subcommand(struct cmd_tbl *cmdtp, int flag, int argc, ret = do_bootm_states(cmdtp, flag, argc, argv, state, &images, 0); +#if defined(CONFIG_CMD_BOOTM_PRE_LOAD) + if (!ret && (state & BOOTM_STATE_PRE_LOAD)) + env_set_hex("loadaddr_verified", + bootm_get_addr(argc, argv) + image_load_offset); +#endif + return ret; } @@ -126,7 +154,7 @@ int do_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) } return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START | - BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER | + BOOTM_STATE_FINDOS | BOOTM_STATE_PRE_LOAD | BOOTM_STATE_FINDOTHER | BOOTM_STATE_LOADOS | #ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH BOOTM_STATE_RAMDISK | @@ -176,6 +204,9 @@ static char bootm_help_text[] = "must be\n" "issued in the order below (it's ok to not issue all sub-commands):\n" "\tstart [addr [arg ...]]\n" +#if defined(CONFIG_CMD_BOOTM_PRE_LOAD) + "\tpreload [addr [arg ..]] - run only the preload stage\n" +#endif "\tloados - load OS image\n" #if defined(CONFIG_SYS_BOOT_RAMDISK_HIGH) "\tramdisk - relocate initrd, set env initrd_start/initrd_end\n" diff --git a/common/spl/spl_ram.c b/common/spl/spl_ram.c index 3f7f7ac..8296459 100644 --- a/common/spl/spl_ram.c +++ b/common/spl/spl_ram.c @@ -24,9 +24,17 @@ static ulong spl_ram_load_read(struct spl_load_info *load, ulong sector, ulong count, void *buf) { + ulong addr; + debug("%s: sector %lx, count %lx, buf %lx\n", __func__, sector, count, (ulong)buf); - memcpy(buf, (void *)(CONFIG_SPL_LOAD_FIT_ADDRESS + sector), count); + + addr = (ulong)CONFIG_SPL_LOAD_FIT_ADDRESS + sector; + if (CONFIG_IS_ENABLED(IMAGE_PRE_LOAD)) + addr += image_load_offset; + + memcpy(buf, (void *)addr, count); + return count; } @@ -37,6 +45,17 @@ static int spl_ram_load_image(struct spl_image_info *spl_image, header = (struct image_header *)CONFIG_SPL_LOAD_FIT_ADDRESS; + if (CONFIG_IS_ENABLED(IMAGE_PRE_LOAD)) { + unsigned long addr = (unsigned long)header; + int ret = image_pre_load(addr); + + if (ret) + return ret; + + addr += image_load_offset; + header = (struct image_header *)addr; + } + #if CONFIG_IS_ENABLED(DFU) if (bootdev->boot_device == BOOT_DEVICE_DFU) spl_dfu_cmd(0, "dfu_alt_info_ram", "ram", "0"); diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 0f43101..2c9b37a 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -27,6 +27,8 @@ CONFIG_AUTOBOOT_SHA256_FALLBACK=y CONFIG_AUTOBOOT_NEVER_TIMEOUT=y CONFIG_AUTOBOOT_STOP_STR_ENABLE=y CONFIG_AUTOBOOT_STOP_STR_CRYPT="$5$rounds=640000$HrpE65IkB8CM5nCL$BKT3QdF98Bo8fJpTr9tjZLZQyzqPASBY20xuK5Rent9" +CONFIG_IMAGE_PRE_LOAD=y +CONFIG_IMAGE_PRE_LOAD_SIG=y CONFIG_CONSOLE_RECORD=y CONFIG_CONSOLE_RECORD_OUT_SIZE=0x1000 CONFIG_PRE_CONSOLE_BUFFER=y @@ -36,6 +38,7 @@ CONFIG_STACKPROTECTOR=y CONFIG_ANDROID_AB=y CONFIG_CMD_CPU=y CONFIG_CMD_LICENSE=y +CONFIG_CMD_BOOTM_PRE_LOAD=y CONFIG_CMD_BOOTZ=y CONFIG_CMD_BOOTEFI_HELLO=y CONFIG_CMD_ABOOTIMG=y diff --git a/include/image.h b/include/image.h index 97e5f2e..498eb7f 100644 --- a/include/image.h +++ b/include/image.h @@ -48,6 +48,7 @@ struct fdt_region; extern ulong image_load_addr; /* Default Load Address */ extern ulong image_save_addr; /* Default Save Address */ extern ulong image_save_size; /* Default Save Size */ +extern ulong image_load_offset; /* Default Load Address Offset */ /* An invalid size, meaning that the image size is not known */ #define IMAGE_SIZE_INVAL (-1UL) @@ -350,6 +351,7 @@ typedef struct bootm_headers { #define BOOTM_STATE_OS_PREP (0x00000100) #define BOOTM_STATE_OS_FAKE_GO (0x00000200) /* 'Almost' run the OS */ #define BOOTM_STATE_OS_GO (0x00000400) +#define BOOTM_STATE_PRE_LOAD 0x00000800 int state; #if defined(CONFIG_LMB) && !defined(USE_HOSTCC) @@ -1017,6 +1019,21 @@ int fit_image_hash_get_value(const void *fit, int noffset, uint8_t **value, int fit_set_timestamp(void *fit, int noffset, time_t timestamp); +/** + * fit_pre_load_data() - add public key to fdt blob + * + * Adds public key to the node pre load. + * + * @keydir: Directory containing keys + * @keydest: FDT blob to write public key + * @fit: Pointer to the FIT format image header + * + * returns: + * 0, on success + * < 0, on failure + */ +int fit_pre_load_data(const char *keydir, void *keydest, void *fit); + int fit_cipher_data(const char *keydir, void *keydest, void *fit, const char *comment, int require_keys, const char *engine_id, const char *cmdname); @@ -1324,6 +1341,19 @@ struct crypto_algo *image_get_crypto_algo(const char *full_name); struct padding_algo *image_get_padding_algo(const char *name); /** + * image_pre_load() - Manage pre load header + * + * Manage the pre-load header before launching the image. + * It checks the signature of the image. It also set the + * variable image_load_offset to skip this header before + * launching the image. + * + * @param addr Address of the image + * @return: 0 on success, -ve on error + */ +int image_pre_load(ulong addr); + +/** * fit_image_verify_required_sigs() - Verify signatures marked as 'required' * * @fit: FIT to check diff --git a/lib/Kconfig b/lib/Kconfig index 3c6fa99..effe735 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -791,17 +791,52 @@ endmenu config ASN1_COMPILER bool + help + ASN.1 (Abstract Syntax Notation One) is a standard interface + description language for defining data structures that can be + serialized and deserialized in a cross-platform way. It is + broadly used in telecommunications and computer networking, + and especially in cryptography (https://en.wikipedia.org/wiki/ASN.1). + This option enables the support of the asn1 compiler. config ASN1_DECODER bool help - Enable asn1 decoder library. + ASN.1 (Abstract Syntax Notation One) is a standard interface + description language for defining data structures that can be + serialized and deserialized in a cross-platform way. It is + broadly used in telecommunications and computer networking, + and especially in cryptography (https://en.wikipedia.org/wiki/ASN.1). + This option enables the support of the asn1 decoder. + +config SPL_ASN1_DECODER + bool + help + ASN.1 (Abstract Syntax Notation One) is a standard interface + description language for defining data structures that can be + serialized and deserialized in a cross-platform way. It is + broadly used in telecommunications and computer networking, + and especially in cryptography (https://en.wikipedia.org/wiki/ASN.1). + This option enables the support of the asn1 decoder in the SPL. config OID_REGISTRY bool help + In computing, object identifiers or OIDs are an identifier mechanism + standardized by the International Telecommunication Union (ITU) and + ISO/IEC for naming any object, concept, or "thing" with a globally + unambiguous persistent name (https://en.wikipedia.org/wiki/Object_identifier). Enable fast lookup object identifier registry. +config SPL_OID_REGISTRY + bool + help + In computing, object identifiers or OIDs are an identifier mechanism + standardized by the International Telecommunication Union (ITU) and + ISO/IEC for naming any object, concept, or "thing" with a globally + unambiguous persistent name (https://en.wikipedia.org/wiki/Object_identifier). + Enable fast lookup object identifier registry in the SPL. + config SMBIOS_PARSER bool "SMBIOS parser" help diff --git a/lib/Makefile b/lib/Makefile index 11b03d1..13fe5fb 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -17,8 +17,6 @@ obj-$(CONFIG_OF_LIVE) += of_live.o obj-$(CONFIG_CMD_DHRYSTONE) += dhry/ obj-$(CONFIG_ARCH_AT91) += at91/ obj-$(CONFIG_OPTEE_LIB) += optee/ -obj-$(CONFIG_ASN1_DECODER) += asn1_decoder.o -obj-y += crypto/ obj-$(CONFIG_AES) += aes.o obj-$(CONFIG_AES) += aes/ @@ -64,6 +62,8 @@ obj-$(CONFIG_TPM_V1) += tpm-v1.o obj-$(CONFIG_TPM_V2) += tpm-v2.o endif +obj-y += crypto/ + obj-$(CONFIG_$(SPL_TPL_)GENERATE_ACPI_TABLE) += acpi/ obj-$(CONFIG_$(SPL_)MD5) += md5.o obj-$(CONFIG_ECDSA) += ecdsa/ @@ -74,6 +74,7 @@ obj-$(CONFIG_SHA1) += sha1.o obj-$(CONFIG_SHA256) += sha256.o obj-$(CONFIG_SHA512) += sha512.o obj-$(CONFIG_CRYPT_PW) += crypt/ +obj-$(CONFIG_$(SPL_)ASN1_DECODER) += asn1_decoder.o obj-$(CONFIG_$(SPL_)ZLIB) += zlib/ obj-$(CONFIG_$(SPL_)ZSTD) += zstd/ @@ -135,9 +136,9 @@ obj-$(CONFIG_$(SPL_TPL_)STRTO) += strto.o else # Main U-Boot always uses the full printf support obj-y += vsprintf.o strto.o -obj-$(CONFIG_OID_REGISTRY) += oid_registry.o obj-$(CONFIG_SSCANF) += sscanf.o endif +obj-$(CONFIG_$(SPL_)OID_REGISTRY) += oid_registry.o obj-y += abuf.o obj-y += date.o diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig index 6369baf..509bc28 100644 --- a/lib/crypto/Kconfig +++ b/lib/crypto/Kconfig @@ -8,6 +8,15 @@ menuconfig ASYMMETRIC_KEY_TYPE if ASYMMETRIC_KEY_TYPE +config SPL_ASYMMETRIC_KEY_TYPE + bool "Asymmetric (public-key cryptographic) key Support within SPL" + depends on SPL + help + This option provides support for a key type that holds the data for + the asymmetric keys used for public key cryptographic operations such + as encryption, decryption, signature generation and signature + verification in the SPL. + config ASYMMETRIC_PUBLIC_KEY_SUBTYPE bool "Asymmetric public-key crypto algorithm subtype" help @@ -16,6 +25,15 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE appropriate hash algorithms (such as SHA-1) must be available. ENOPKG will be reported if the requisite algorithm is unavailable. +config SPL_ASYMMETRIC_PUBLIC_KEY_SUBTYPE + bool "Asymmetric public-key crypto algorithm subtype within SPL" + depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE + help + This option provides support for asymmetric public key type handling in the SPL. + If signature generation and/or verification are to be used, + appropriate hash algorithms (such as SHA-1) must be available. + ENOPKG will be reported if the requisite algorithm is unavailable. + config RSA_PUBLIC_KEY_PARSER bool "RSA public key parser" depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE @@ -27,6 +45,17 @@ config RSA_PUBLIC_KEY_PARSER public key data and provides the ability to instantiate a public key. +config SPL_RSA_PUBLIC_KEY_PARSER + bool "RSA public key parser within SPL" + depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select SPL_ASN1_DECODER + select ASN1_COMPILER + select SPL_OID_REGISTRY + help + This option provides support for parsing a blob containing RSA + public key data and provides the ability to instantiate a public + key in the SPL. + config X509_CERTIFICATE_PARSER bool "X.509 certificate parser" depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile index f3a4145..6792b1d 100644 --- a/lib/crypto/Makefile +++ b/lib/crypto/Makefile @@ -3,27 +3,34 @@ # Makefile for asymmetric cryptographic keys # -obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o +obj-$(CONFIG_$(SPL_)ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o asymmetric_keys-y := asymmetric_type.o -obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o +obj-$(CONFIG_$(SPL_)ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o # # RSA public key parser # -obj-$(CONFIG_RSA_PUBLIC_KEY_PARSER) += rsa_public_key.o +obj-$(CONFIG_$(SPL_)RSA_PUBLIC_KEY_PARSER) += rsa_public_key.o rsa_public_key-y := \ rsapubkey.asn1.o \ rsa_helper.o $(obj)/rsapubkey.asn1.o: $(obj)/rsapubkey.asn1.c $(obj)/rsapubkey.asn1.h +ifdef CONFIG_SPL_BUILD +CFLAGS_rsapubkey.asn1.o += -I$(obj) +endif + $(obj)/rsa_helper.o: $(obj)/rsapubkey.asn1.h +ifdef CONFIG_SPL_BUILD +CFLAGS_rsa_helper.o += -I$(obj) +endif # # X.509 Certificate handling # -obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o +obj-$(CONFIG_$(SPL_)X509_CERTIFICATE_PARSER) += x509_key_parser.o x509_key_parser-y := \ x509.asn1.o \ x509_akid.asn1.o \ @@ -40,11 +47,11 @@ $(obj)/x509_akid.asn1.o: $(obj)/x509_akid.asn1.c $(obj)/x509_akid.asn1.h # # PKCS#7 message handling # -obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o +obj-$(CONFIG_$(SPL_)PKCS7_MESSAGE_PARSER) += pkcs7_message.o pkcs7_message-y := \ pkcs7.asn1.o \ pkcs7_parser.o -obj-$(CONFIG_PKCS7_VERIFY) += pkcs7_verify.o +obj-$(CONFIG_$(SPL_)PKCS7_VERIFY) += pkcs7_verify.o $(obj)/pkcs7_parser.o: $(obj)/pkcs7.asn1.h $(obj)/pkcs7.asn1.o: $(obj)/pkcs7.asn1.c $(obj)/pkcs7.asn1.h diff --git a/lib/rsa/Kconfig b/lib/rsa/Kconfig index be9775b..b773f17 100644 --- a/lib/rsa/Kconfig +++ b/lib/rsa/Kconfig @@ -47,6 +47,25 @@ config RSA_VERIFY_WITH_PKEY directly specified in image_sign_info, where all the necessary key properties will be calculated on the fly in verification code. +config SPL_RSA_VERIFY_WITH_PKEY + bool "Execute RSA verification without key parameters from FDT within SPL" + depends on SPL + select SPL_RSA_VERIFY + select SPL_ASYMMETRIC_KEY_TYPE + select SPL_ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select SPL_RSA_PUBLIC_KEY_PARSER + help + The standard RSA-signature verification code (FIT_SIGNATURE) uses + pre-calculated key properties, that are stored in fdt blob, in + decrypting a signature. + This does not suit the use case where there is no way defined to + provide such additional key properties in standardized form, + particularly UEFI secure boot. + This options enables RSA signature verification with a public key + directly specified in image_sign_info, where all the necessary + key properties will be calculated on the fly in verification code + in the SPL. + config RSA_SOFTWARE_EXP bool "Enable driver for RSA Modular Exponentiation in software" depends on DM diff --git a/test/py/tests/test_fit.py b/test/py/tests/test_fit.py index 6d5b43c..5856960 100755 --- a/test/py/tests/test_fit.py +++ b/test/py/tests/test_fit.py @@ -89,6 +89,9 @@ base_fdt = ''' model = "Sandbox Verified Boot Test"; compatible = "sandbox"; + binman { + }; + reset@0 { compatible = "sandbox,reset"; reg = <0>; diff --git a/test/py/tests/test_vboot.py b/test/py/tests/test_vboot.py index ac8ed9f..040147d 100644 --- a/test/py/tests/test_vboot.py +++ b/test/py/tests/test_vboot.py @@ -21,6 +21,14 @@ For configuration verification: - Corrupt the signature - Check that image verification no-longer works +For pre-load header verification: +- Create FIT image with a pre-load header +- Check that signature verification succeeds +- Corrupt the FIT image +- Check that signature verification fails +- Launch an FIT image without a pre-load header +- Check that image verification fails + Tests run with both SHA1 and SHA256 hashing. """ @@ -35,19 +43,21 @@ import vboot_evil # Only run the full suite on a few combinations, since it doesn't add any more # test coverage. TESTDATA = [ - ['sha1-basic', 'sha1', '', None, False, True, False], - ['sha1-pad', 'sha1', '', '-E -p 0x10000', False, False, False], - ['sha1-pss', 'sha1', '-pss', None, False, False, False], - ['sha1-pss-pad', 'sha1', '-pss', '-E -p 0x10000', False, False, False], - ['sha256-basic', 'sha256', '', None, False, False, False], - ['sha256-pad', 'sha256', '', '-E -p 0x10000', False, False, False], - ['sha256-pss', 'sha256', '-pss', None, False, False, False], - ['sha256-pss-pad', 'sha256', '-pss', '-E -p 0x10000', False, False, False], - ['sha256-pss-required', 'sha256', '-pss', None, True, False, False], - ['sha256-pss-pad-required', 'sha256', '-pss', '-E -p 0x10000', True, True, False], - ['sha384-basic', 'sha384', '', None, False, False, False], - ['sha384-pad', 'sha384', '', '-E -p 0x10000', False, False, False], - ['algo-arg', 'algo-arg', '', '-o sha256,rsa2048', False, False, True], + ['sha1-basic', 'sha1', '', None, False, True, False, False], + ['sha1-pad', 'sha1', '', '-E -p 0x10000', False, False, False, False], + ['sha1-pss', 'sha1', '-pss', None, False, False, False, False], + ['sha1-pss-pad', 'sha1', '-pss', '-E -p 0x10000', False, False, False, False], + ['sha256-basic', 'sha256', '', None, False, False, False, False], + ['sha256-pad', 'sha256', '', '-E -p 0x10000', False, False, False, False], + ['sha256-pss', 'sha256', '-pss', None, False, False, False, False], + ['sha256-pss-pad', 'sha256', '-pss', '-E -p 0x10000', False, False, False, False], + ['sha256-pss-required', 'sha256', '-pss', None, True, False, False, False], + ['sha256-pss-pad-required', 'sha256', '-pss', '-E -p 0x10000', True, True, False, False], + ['sha384-basic', 'sha384', '', None, False, False, False, False], + ['sha384-pad', 'sha384', '', '-E -p 0x10000', False, False, False, False], + ['algo-arg', 'algo-arg', '', '-o sha256,rsa2048', False, False, True, False], + ['sha256-global-sign', 'sha256', '', '', False, False, False, True], + ['sha256-global-sign-pss', 'sha256', '-pss', '', False, False, False, True], ] @pytest.mark.boardspec('sandbox') @@ -56,10 +66,10 @@ TESTDATA = [ @pytest.mark.requiredtool('fdtget') @pytest.mark.requiredtool('fdtput') @pytest.mark.requiredtool('openssl') -@pytest.mark.parametrize("name,sha_algo,padding,sign_options,required,full_test,algo_arg", +@pytest.mark.parametrize("name,sha_algo,padding,sign_options,required,full_test,algo_arg,global_sign", TESTDATA) def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required, - full_test, algo_arg): + full_test, algo_arg, global_sign): """Test verified boot signing with mkimage and verification with 'bootm'. This works using sandbox only as it needs to update the device tree used @@ -81,6 +91,33 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required, util.run_and_log(cons, 'dtc %s %s%s -O dtb ' '-o %s%s' % (dtc_args, datadir, dts, tmpdir, dtb)) + def dtc_options(dts, options): + """Run the device tree compiler to compile a .dts file + + The output file will be the same as the input file but with a .dtb + extension. + + Args: + dts: Device tree file to compile. + options: Options provided to the compiler. + """ + dtb = dts.replace('.dts', '.dtb') + util.run_and_log(cons, 'dtc %s %s%s -O dtb ' + '-o %s%s %s' % (dtc_args, datadir, dts, tmpdir, dtb, options)) + + def run_binman(dtb): + """Run binman to build an image + + Args: + dtb: Device tree file used as input file. + """ + pythonpath = os.environ.get('PYTHONPATH', '') + os.environ['PYTHONPATH'] = pythonpath + ':' + '%s/../scripts/dtc/pylibfdt' % tmpdir + util.run_and_log(cons, [binman, 'build', '-d', "%s/%s" % (tmpdir,dtb), + '-a', "pre-load-key-path=%s" % tmpdir, '-O', + tmpdir, '-I', tmpdir]) + os.environ['PYTHONPATH'] = pythonpath + def run_bootm(sha_algo, test_type, expect_string, boots, fit=None): """Run a 'bootm' command U-Boot. @@ -139,6 +176,23 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required, cons.log.action('%s: Sign images' % sha_algo) util.run_and_log(cons, args) + def sign_fit_dtb(sha_algo, options, dtb): + """Sign the FIT + + Signs the FIT and writes the signature into it. It also writes the + public key into the dtb. + + Args: + sha_algo: Either 'sha1' or 'sha256', to select the algorithm to + use. + options: Options to provide to mkimage. + """ + args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, '-r', fit] + if options: + args += options.split(' ') + cons.log.action('%s: Sign images' % sha_algo) + util.run_and_log(cons, args) + def sign_fit_norequire(sha_algo, options): """Sign the FIT @@ -176,6 +230,20 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required, handle.write(struct.pack(">I", size)) return struct.unpack(">I", total_size)[0] + def corrupt_file(fit, offset, value): + """Corrupt a file + + To corrupt a file, a value is written at the specified offset + + Args: + fit: The file to corrupt + offset: Offset to write + value: Value written + """ + with open(fit, 'r+b') as handle: + handle.seek(offset) + handle.write(struct.pack(">I", value)) + def create_rsa_pair(name): """Generate a new RSA key paid and certificate @@ -374,6 +442,51 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required, (dtb)) run_bootm(sha_algo, 'multi required key', '', False) + def test_global_sign(sha_algo, padding, sign_options): + """Test global image signature with the given hash algorithm and padding. + + Args: + sha_algo: Either 'sha1' or 'sha256', to select the algorithm to use + padding: Either '' or '-pss', to select the padding to use for the + rsa signature algorithm. + """ + + dtb = '%ssandbox-u-boot-global%s.dtb' % (tmpdir, padding) + cons.config.dtb = dtb + + # Compile our device tree files for kernel and U-Boot. These are + # regenerated here since mkimage will modify them (by adding a + # public key) below. + dtc('sandbox-kernel.dts') + dtc_options('sandbox-u-boot-global%s.dts' % padding, '-p 1024') + + # Build the FIT with dev key (keys NOT required). This adds the + # signature into sandbox-u-boot.dtb, NOT marked 'required'. + make_fit('simple-images.its') + sign_fit_dtb(sha_algo, '', dtb) + + # Build the dtb for binman that define the pre-load header + # with the global sigature. + dtc('sandbox-binman%s.dts' % padding) + + # Run binman to create the final image with the not signed fit + # and the pre-load header that contains the global signature. + run_binman('sandbox-binman%s.dtb' % padding) + + # Check that the signature is correctly verified by u-boot + run_bootm(sha_algo, 'global image signature', + 'signature check has succeed', True, "%ssandbox.img" % tmpdir) + + # Corrupt the image (just one byte after the pre-load header) + corrupt_file("%ssandbox.img" % tmpdir, 4096, 255); + + # Check that the signature verification fails + run_bootm(sha_algo, 'global image signature', + 'signature check has failed', False, "%ssandbox.img" % tmpdir) + + # Check that the boot fails if the global signature is not provided + run_bootm(sha_algo, 'global image signature', 'signature is mandatory', False) + cons = u_boot_console tmpdir = os.path.join(cons.config.result_dir, name) + '/' if not os.path.exists(tmpdir): @@ -381,6 +494,7 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required, datadir = cons.config.source_dir + '/test/py/tests/vboot/' fit = '%stest.fit' % tmpdir mkimage = cons.config.build_dir + '/tools/mkimage' + binman = cons.config.source_dir + '/tools/binman/binman' fit_check_sign = cons.config.build_dir + '/tools/fit_check_sign' dtc_args = '-I dts -O dtb -i %s' % tmpdir dtb = '%ssandbox-u-boot.dtb' % tmpdir @@ -403,7 +517,9 @@ def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required, # afterwards. old_dtb = cons.config.dtb cons.config.dtb = dtb - if required: + if global_sign: + test_global_sign(sha_algo, padding, sign_options) + elif required: test_required_key(sha_algo, padding, sign_options) else: test_with_algo(sha_algo, padding, sign_options) diff --git a/test/py/tests/vboot/sandbox-binman-pss.dts b/test/py/tests/vboot/sandbox-binman-pss.dts new file mode 100644 index 0000000..56e3a42 --- /dev/null +++ b/test/py/tests/vboot/sandbox-binman-pss.dts @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + filename = "sandbox.img"; + + pre-load { + content = <&image>; + algo-name = "sha256,rsa2048"; + padding-name = "pss"; + key-name = "dev.key"; + header-size = <4096>; + version = <1>; + }; + + image: blob-ext { + filename = "test.fit"; + }; + }; +}; diff --git a/test/py/tests/vboot/sandbox-binman.dts b/test/py/tests/vboot/sandbox-binman.dts new file mode 100644 index 0000000..b24aeba --- /dev/null +++ b/test/py/tests/vboot/sandbox-binman.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + filename = "sandbox.img"; + + pre-load { + content = <&image>; + algo-name = "sha256,rsa2048"; + key-name = "dev.key"; + header-size = <4096>; + version = <1>; + }; + + image: blob-ext { + filename = "test.fit"; + }; + }; +}; diff --git a/test/py/tests/vboot/sandbox-u-boot-global-pss.dts b/test/py/tests/vboot/sandbox-u-boot-global-pss.dts new file mode 100644 index 0000000..c59a682 --- /dev/null +++ b/test/py/tests/vboot/sandbox-u-boot-global-pss.dts @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + model = "Sandbox Verified Boot Test"; + compatible = "sandbox"; + + binman { + }; + + reset@0 { + compatible = "sandbox,reset"; + }; + + image { + pre-load { + sig { + algo-name = "sha256,rsa2048"; + padding-name = "pss"; + signature-size = <256>; + mandatory = "yes"; + + key-name = "dev"; + }; + }; + }; +}; diff --git a/test/py/tests/vboot/sandbox-u-boot-global.dts b/test/py/tests/vboot/sandbox-u-boot-global.dts new file mode 100644 index 0000000..1409f9e --- /dev/null +++ b/test/py/tests/vboot/sandbox-u-boot-global.dts @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + model = "Sandbox Verified Boot Test"; + compatible = "sandbox"; + + binman { + }; + + reset@0 { + compatible = "sandbox,reset"; + }; + + image { + pre-load { + sig { + algo-name = "sha256,rsa2048"; + signature-size = <256>; + mandatory = "yes"; + + key-name = "dev"; + }; + }; + }; +}; diff --git a/test/py/tests/vboot/sandbox-u-boot.dts b/test/py/tests/vboot/sandbox-u-boot.dts index 63f8f40..5809c62 100644 --- a/test/py/tests/vboot/sandbox-u-boot.dts +++ b/test/py/tests/vboot/sandbox-u-boot.dts @@ -4,6 +4,9 @@ model = "Sandbox Verified Boot Test"; compatible = "sandbox"; + binman { + }; + reset@0 { compatible = "sandbox,reset"; }; diff --git a/test/py/tests/vboot/simple-images.its b/test/py/tests/vboot/simple-images.its new file mode 100644 index 0000000..f627864 --- /dev/null +++ b/test/py/tests/vboot/simple-images.its @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + }; + fdt-1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel"; + fdt = "fdt-1"; + }; + }; +}; diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index be8de55..ae4305c 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -1155,6 +1155,44 @@ placed at offset 'RESET_VECTOR_ADDRESS - 0xffc'. +Entry: pre-load: Pre load image header +-------------------------------------- + +Properties / Entry arguments: + - key-path: Path of the directory that store key (provided by the environment variable KEY_PATH) + - content: List of phandles to entries to sign + - algo-name: Hash and signature algo to use for the signature + - padding-name: Name of the padding (pkcs-1.5 or pss) + - key-name: Filename of the private key to sign + - header-size: Total size of the header + - version: Version of the header + +This entry creates a pre-load header that contains a global +image signature. + +For example, this creates an image with a pre-load header and a binary:: + + binman { + image2 { + filename = "sandbox.bin"; + + pre-load { + content = <&image>; + algo-name = "sha256,rsa2048"; + padding-name = "pss"; + key-name = "private.pem"; + header-size = <4096>; + version = <1>; + }; + + image: blob-ext { + filename = "sandbox.itb"; + }; + }; + }; + + + Entry: scp: System Control Processor (SCP) firmware blob -------------------------------------------------------- diff --git a/tools/binman/etype/pre_load.py b/tools/binman/etype/pre_load.py new file mode 100644 index 0000000..245ee75 --- /dev/null +++ b/tools/binman/etype/pre_load.py @@ -0,0 +1,162 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2022 Softathome +# Written by Philippe Reynes <philippe.reynes@softathome.com> +# +# Entry-type for the global header +# + +import os +import struct +from dtoc import fdt_util +from patman import tools + +from binman.entry import Entry +from binman.etype.collection import Entry_collection +from binman.entry import EntryArg + +from Cryptodome.Hash import SHA256, SHA384, SHA512 +from Cryptodome.PublicKey import RSA +from Cryptodome.Signature import pkcs1_15 +from Cryptodome.Signature import pss + +PRE_LOAD_MAGIC = b'UBSH' + +RSAS = { + 'rsa1024': 1024 / 8, + 'rsa2048': 2048 / 8, + 'rsa4096': 4096 / 8 +} + +SHAS = { + 'sha256': SHA256, + 'sha384': SHA384, + 'sha512': SHA512 +} + +class Entry_pre_load(Entry_collection): + """Pre load image header + + Properties / Entry arguments: + - pre-load-key-path: Path of the directory that store key (provided by the environment variable PRE_LOAD_KEY_PATH) + - content: List of phandles to entries to sign + - algo-name: Hash and signature algo to use for the signature + - padding-name: Name of the padding (pkcs-1.5 or pss) + - key-name: Filename of the private key to sign + - header-size: Total size of the header + - version: Version of the header + + This entry creates a pre-load header that contains a global + image signature. + + For example, this creates an image with a pre-load header and a binary:: + + binman { + image2 { + filename = "sandbox.bin"; + + pre-load { + content = <&image>; + algo-name = "sha256,rsa2048"; + padding-name = "pss"; + key-name = "private.pem"; + header-size = <4096>; + version = <1>; + }; + + image: blob-ext { + filename = "sandbox.itb"; + }; + }; + }; + """ + + def __init__(self, section, etype, node): + super().__init__(section, etype, node) + self.algo_name = fdt_util.GetString(self._node, 'algo-name') + self.padding_name = fdt_util.GetString(self._node, 'padding-name') + self.key_name = fdt_util.GetString(self._node, 'key-name') + self.header_size = fdt_util.GetInt(self._node, 'header-size') + self.version = fdt_util.GetInt(self._node, 'version') + + def ReadNode(self): + super().ReadNode() + self.key_path, = self.GetEntryArgsOrProps([EntryArg('pre-load-key-path', str)]) + if self.key_path is None: + self.key_path = '' + + def _CreateHeader(self): + """Create a pre load header""" + hash_name, sign_name = self.algo_name.split(',') + padding_name = self.padding_name + key_name = os.path.join(self.key_path, self.key_name) + + # Check hash and signature name/type + if hash_name not in SHAS: + self.Raise(hash_name + " is not supported") + if sign_name not in RSAS: + self.Raise(sign_name + " is not supported") + + # Read the key + with open(key_name, 'rb') as pem: + key = RSA.import_key(pem.read()) + + # Check if the key has the expected size + if key.size_in_bytes() != RSAS[sign_name]: + self.Raise("The key " + self.key_name + " don't have the expected size") + + # Compute the hash + hash_image = SHAS[hash_name].new() + hash_image.update(self.image) + + # Compute the signature + if padding_name is None: + padding_name = "pkcs-1.5" + if padding_name == "pss": + salt_len = key.size_in_bytes() - hash_image.digest_size - 2 + padding = pss + padding_args = {'salt_bytes': salt_len} + elif padding_name == "pkcs-1.5": + padding = pkcs1_15 + padding_args = {} + else: + self.Raise(padding_name + " is not supported") + + sig = padding.new(key, **padding_args).sign(hash_image) + + hash_sig = SHA256.new() + hash_sig.update(sig) + + version = self.version + header_size = self.header_size + image_size = len(self.image) + ofs_img_sig = 64 + len(sig) + flags = 0 + reserved0 = 0 + reserved1 = 0 + + first_header = struct.pack('>4sIIIIIII32s', PRE_LOAD_MAGIC, + version, header_size, image_size, + ofs_img_sig, flags, reserved0, + reserved1, hash_sig.digest()) + + hash_first_header = SHAS[hash_name].new() + hash_first_header.update(first_header) + sig_first_header = padding.new(key, **padding_args).sign(hash_first_header) + + data = first_header + sig_first_header + sig + pad = bytearray(self.header_size - len(data)) + + 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()) + return True + + def ProcessContents(self): + data = self._CreateHeader() + return self.ProcessContentsUpdate(data) diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index 876953f..4ce181a 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -91,6 +91,9 @@ SCP_DATA = b'scp' TEST_FDT1_DATA = b'fdt1' TEST_FDT2_DATA = b'test-fdt2' ENV_DATA = b'var1=1\nvar2="2"' +PRE_LOAD_MAGIC = b'UBSH' +PRE_LOAD_VERSION = 0x11223344.to_bytes(4, 'big') +PRE_LOAD_HDR_SIZE = 0x00001000.to_bytes(4, 'big') # Subdirectory of the input dir to use to put test FDTs TEST_FDT_SUBDIR = 'fdts' @@ -5471,6 +5474,54 @@ fdt fdtmap Extract the devicetree blob from the fdtmap err, "Image '.*' is missing external blobs and is non-functional: .*") + def testPreLoad(self): + """Test an image with a pre-load header""" + entry_args = { + 'pre-load-key-path': '.', + } + data, _, _, _ = self._DoReadFileDtb('225_pre_load.dts', + entry_args=entry_args) + 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)]) + data = self._DoReadFile('225_pre_load.dts') + 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)]) + + def testPreLoadPkcs(self): + """Test an image with a pre-load header with padding pkcs""" + data = self._DoReadFile('226_pre_load_pkcs.dts') + 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)]) + + def testPreLoadPss(self): + """Test an image with a pre-load header with padding pss""" + data = self._DoReadFile('227_pre_load_pss.dts') + 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)]) + + def testPreLoadInvalidPadding(self): + """Test an image with a pre-load header with an invalid padding""" + with self.assertRaises(ValueError) as e: + data = self._DoReadFile('228_pre_load_invalid_padding.dts') + + def testPreLoadInvalidSha(self): + """Test an image with a pre-load header with an invalid hash""" + with self.assertRaises(ValueError) as e: + data = self._DoReadFile('229_pre_load_invalid_sha.dts') + + def testPreLoadInvalidAlgo(self): + """Test an image with a pre-load header with an invalid algo""" + with self.assertRaises(ValueError) as e: + data = self._DoReadFile('230_pre_load_invalid_algo.dts') + + def testPreLoadInvalidKey(self): + """Test an image with a pre-load header with an invalid key""" + with self.assertRaises(ValueError) as e: + data = self._DoReadFile('231_pre_load_invalid_key.dts') if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/225_dev.key b/tools/binman/test/225_dev.key new file mode 100644 index 0000000..b36bad2 --- /dev/null +++ b/tools/binman/test/225_dev.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDYngNWUvXYRXX/ +WEUI7k164fcpv1srXz+u+5Y3Yhouw3kPs+ffvYyHAPfjF7aUIAgezKk/4o7AvsxE +Rdih3T+0deAd/q/yuqN4Adzt6ImnsO/EqdtYl3Yh+Vck9xWhLd3SAw1++GfSmNMT +gxlcc/z6z+bIh2tJNtPtRSNNHMmvYYOkBmkfwcjbMXD+fe4vBwYjVrIize+l7Yuv +1qN2nFlq56pFi8Lj5vOvFyNhZHRvwcpWdUdkx39beNUfwrGhgewOeWngTcY75n7S +FY45TBR1G2PR90CQvyDinCi9Mm0u5s+1WASQWPblovfD6CPbHQu4GZm+FAs7yUvr +hA7VCyNxAgMBAAECggEAUbq0uaJNfc8faTtNuMPo2d9eGRNI+8FRTt0/3R+Xj2NT +TvhrGUD0P4++96Df012OkshXZ3I8uD6E5ZGQ3emTeqwq5kZM7oE64jGZwO3G2k1o ++cO4reFfwgvItHrBX3HlyrI6KljhG1Vr9mW1cOuWXK+KfMiTUylrpo86dYLSGeg3 +7ZlsOPArr4eof/A0iPryQZX6X5POf7k/e9qRFYsOkoRQO8pBL3J4rIKwBl3uBN3K ++FY40vCkd8JyTo2DNfHeIe1XYA9fG2ahjD2qMsw10TUsRRMd5yhonEcJ7VzGzy8m +MnuMDAr7CwbbLkKi4UfZUl6YDkojqerwLOrxikBqkQKBgQD6sS6asDgwiq5MtstE +4/PxMrVEsCdkrU+jjQN749qIt/41a6lbp0Pr6aUKKKGs0QbcnCtlpp7qmhvymBcW +hlqxk2wokKMChv4WLXjZS3DGcOdMglc81y2F+252bToN8vwUfm6DPp9/GKtejA0a +GP57GeHxoVO7vfDX1F/vZRogRQKBgQDdNCLWOlGWvnKjfgNZHgX+Ou6ZgTSAzy+/ +hRsZPlY5nwO5iD7YkIKvqBdOmfyjlUpHWk2uAcT9pfgzYygvyBRaoQhAYBGkHItt +slaMxnLd+09wWufoCbgJvFn+wVQxBLcA5PXB98ws0Dq8ZYuo6AOuoRivsSO4lblK +MW0guBJXPQKBgQDGjf0ukbH/aGfC5Oi8SJvWhuYhYC/jQo2YKUEAKCjXLnuOThZW +PHXEbUrFcAcVfH0l0B9jJIQrpiHKlAF9Wq6MhQoeWuhxQQAQCrXzzRemZJgd9gIo +cvlgbBNCgyJ/F9vmU3kuRDRJkv1wJhbee7tbPtXA7pkGUttl5pSRZI87zQKBgQC/ +0ZkwCox72xTQP9MpcYai6nnDta5Q0NnIC+Xu4wakmwcA2WweIlqhdnMXnyLcu/YY +n+9iqHgpuMXd0eukW62C1cexA13o4TPrYU36b5BmfKprdPlLVzo3fxTPfNjEVSFY +7jNLC9YLOlrkym3sf53Jzjr5B/RA+d0ewHOwfs6wxQKBgFSyfjx5wtdHK4fO+Z1+ +q3bxouZryM/4CiPCFuw4+aZmRHPmufuNCvfXdF+IH8dM0E9ObwKZAe/aMP/Y+Abx +Wz9Vm4CP6g7k3DU3INEygyjmIQQDKQ9lFdDnsP9ESzrPbaGxZhc4x2lo7qmeW1BR +/RuiAofleFkT4s+EhLrfE/v5 +-----END PRIVATE KEY----- diff --git a/tools/binman/test/225_pre_load.dts b/tools/binman/test/225_pre_load.dts new file mode 100644 index 0000000..c1ffe1a --- /dev/null +++ b/tools/binman/test/225_pre_load.dts @@ -0,0 +1,22 @@ +// 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 = "tools/binman/test/225_dev.key"; + header-size = <4096>; + version = <0x11223344>; + }; + + image: blob-ext { + filename = "refcode.bin"; + }; + }; +}; diff --git a/tools/binman/test/226_pre_load_pkcs.dts b/tools/binman/test/226_pre_load_pkcs.dts new file mode 100644 index 0000000..3db0a37 --- /dev/null +++ b/tools/binman/test/226_pre_load_pkcs.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pre-load { + content = <&image>; + algo-name = "sha256,rsa2048"; + padding-name = "pkcs-1.5"; + key-name = "tools/binman/test/225_dev.key"; + header-size = <4096>; + version = <0x11223344>; + }; + + image: blob-ext { + filename = "refcode.bin"; + }; + }; +}; diff --git a/tools/binman/test/227_pre_load_pss.dts b/tools/binman/test/227_pre_load_pss.dts new file mode 100644 index 0000000..b1b01d5 --- /dev/null +++ b/tools/binman/test/227_pre_load_pss.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pre-load { + content = <&image>; + algo-name = "sha256,rsa2048"; + padding-name = "pss"; + key-name = "tools/binman/test/225_dev.key"; + header-size = <4096>; + version = <0x11223344>; + }; + + image: blob-ext { + filename = "refcode.bin"; + }; + }; +}; diff --git a/tools/binman/test/228_pre_load_invalid_padding.dts b/tools/binman/test/228_pre_load_invalid_padding.dts new file mode 100644 index 0000000..84fe289 --- /dev/null +++ b/tools/binman/test/228_pre_load_invalid_padding.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pre-load { + content = <&image>; + algo-name = "sha256,rsa2048"; + padding-name = "padding"; + key-name = "tools/binman/test/225_dev.key"; + header-size = <4096>; + version = <1>; + }; + + image: blob-ext { + filename = "refcode.bin"; + }; + }; +}; diff --git a/tools/binman/test/229_pre_load_invalid_sha.dts b/tools/binman/test/229_pre_load_invalid_sha.dts new file mode 100644 index 0000000..a2b6725 --- /dev/null +++ b/tools/binman/test/229_pre_load_invalid_sha.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pre-load { + content = <&image>; + algo-name = "sha2560,rsa2048"; + padding-name = "pkcs-1.5"; + key-name = "tools/binman/test/225_dev.key"; + header-size = <4096>; + version = <1>; + }; + + image: blob-ext { + filename = "refcode.bin"; + }; + }; +}; diff --git a/tools/binman/test/230_pre_load_invalid_algo.dts b/tools/binman/test/230_pre_load_invalid_algo.dts new file mode 100644 index 0000000..34c8d34 --- /dev/null +++ b/tools/binman/test/230_pre_load_invalid_algo.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pre-load { + content = <&image>; + algo-name = "sha256,rsa20480"; + padding-name = "pkcs-1.5"; + key-name = "tools/binman/test/225_dev.key"; + header-size = <4096>; + version = <1>; + }; + + image: blob-ext { + filename = "refcode.bin"; + }; + }; +}; diff --git a/tools/binman/test/231_pre_load_invalid_key.dts b/tools/binman/test/231_pre_load_invalid_key.dts new file mode 100644 index 0000000..08d5a75 --- /dev/null +++ b/tools/binman/test/231_pre_load_invalid_key.dts @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pre-load { + content = <&image>; + algo-name = "sha256,rsa4096"; + padding-name = "pkcs-1.5"; + key-name = "tools/binman/test/225_dev.key"; + header-size = <4096>; + version = <1>; + }; + + image: blob-ext { + filename = "refcode.bin"; + }; + }; +}; diff --git a/tools/fit_image.c b/tools/fit_image.c index 15f7c82..1884a2eb 100644 --- a/tools/fit_image.c +++ b/tools/fit_image.c @@ -59,6 +59,9 @@ static int fit_add_file_data(struct image_tool_params *params, size_t size_inc, ret = fit_set_timestamp(ptr, 0, time); } + if (!ret) + ret = fit_pre_load_data(params->keydir, dest_blob, ptr); + if (!ret) { ret = fit_cipher_data(params->keydir, dest_blob, ptr, params->comment, diff --git a/tools/image-host.c b/tools/image-host.c index eaeb765..ab6f756 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -14,6 +14,11 @@ #include <image.h> #include <version.h> +#include <openssl/pem.h> +#include <openssl/evp.h> + +#define IMAGE_PRE_LOAD_PATH "/image/pre-load/sig" + /** * fit_set_hash_value - set hash value in requested has node * @fit: pointer to the FIT format image header @@ -1111,6 +1116,115 @@ static int fit_config_add_verification_data(const char *keydir, return 0; } +/* + * 0) open file (open) + * 1) read certificate (PEM_read_X509) + * 2) get public key (X509_get_pubkey) + * 3) provide der format (d2i_RSAPublicKey) + */ +static int read_pub_key(const char *keydir, const void *name, + unsigned char **pubkey, int *pubkey_len) +{ + char path[1024]; + EVP_PKEY *key = NULL; + X509 *cert; + FILE *f; + int ret; + + memset(path, 0, 1024); + snprintf(path, sizeof(path), "%s/%s.crt", keydir, (char *)name); + + /* Open certificate file */ + f = fopen(path, "r"); + if (!f) { + fprintf(stderr, "Couldn't open RSA certificate: '%s': %s\n", + path, strerror(errno)); + return -EACCES; + } + + /* Read the certificate */ + cert = NULL; + if (!PEM_read_X509(f, &cert, NULL, NULL)) { + printf("Couldn't read certificate"); + ret = -EINVAL; + goto err_cert; + } + + /* Get the public key from the certificate. */ + key = X509_get_pubkey(cert); + if (!key) { + printf("Couldn't read public key\n"); + ret = -EINVAL; + goto err_pubkey; + } + + /* Get DER form */ + ret = i2d_PublicKey(key, pubkey); + if (ret < 0) { + printf("Couldn't get DER form\n"); + ret = -EINVAL; + goto err_pubkey; + } + + *pubkey_len = ret; + ret = 0; + +err_pubkey: + X509_free(cert); +err_cert: + fclose(f); + return ret; +} + +int fit_pre_load_data(const char *keydir, void *keydest, void *fit) +{ + int pre_load_noffset; + const void *algo_name; + const void *key_name; + unsigned char *pubkey = NULL; + int ret, pubkey_len; + + if (!keydir || !keydest || !fit) + return 0; + + /* Search node pre-load sig */ + pre_load_noffset = fdt_path_offset(keydest, IMAGE_PRE_LOAD_PATH); + if (pre_load_noffset < 0) { + ret = 0; + goto out; + } + + algo_name = fdt_getprop(keydest, pre_load_noffset, "algo-name", NULL); + key_name = fdt_getprop(keydest, pre_load_noffset, "key-name", NULL); + + /* Check that all mandatory properties are present */ + if (!algo_name || !key_name) { + if (!algo_name) + printf("The property algo-name is missing in the node %s\n", + IMAGE_PRE_LOAD_PATH); + if (!key_name) + printf("The property key-name is missing in the node %s\n", + IMAGE_PRE_LOAD_PATH); + ret = -ENODATA; + goto out; + } + + /* Read public key */ + ret = read_pub_key(keydir, key_name, &pubkey, &pubkey_len); + if (ret < 0) + goto out; + + /* Add the public key to the device tree */ + ret = fdt_setprop(keydest, pre_load_noffset, "public-key", + pubkey, pubkey_len); + if (ret) + printf("Can't set public-key in node %s (ret = %d)\n", + IMAGE_PRE_LOAD_PATH, ret); + + out: + return ret; +} + int fit_cipher_data(const char *keydir, void *keydest, void *fit, const char *comment, int require_keys, const char *engine_id, const char *cmdname) |