Merge branch '2022-03-31-image-add-a-stage-pre-load' into 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.
This commit is contained in:
Tom Rini 2022-03-31 14:12:30 -04:00
commit 52d8100b1d
39 changed files with 1544 additions and 29 deletions

View File

@ -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

View File

@ -205,6 +205,7 @@ config SANDBOX
imply KEYBOARD
imply PHYSMEM
imply GENERATE_ACPI_TABLE
imply BINMAN
config SH
bool "SuperH architecture"

View File

@ -7,6 +7,9 @@
#define USB_CLASS_HUB 9
/ {
binman {
};
chosen {
stdout-path = "/serial";
};

View File

@ -61,6 +61,9 @@
osd0 = "/osd";
};
binman {
};
config {
testing-bool;
testing-int = <123>;

View File

@ -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

View File

@ -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

View File

@ -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 */

416
boot/image-pre-load.c Normal file
View File

@ -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, &reg, 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, &reg, 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;
}

View File

@ -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

View File

@ -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"

View File

@ -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");

View File

@ -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

View File

@ -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);
@ -1323,6 +1340,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'
*

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -89,6 +89,9 @@ base_fdt = '''
model = "Sandbox Verified Boot Test";
compatible = "sandbox";
binman {
};
reset@0 {
compatible = "sandbox,reset";
reg = <0>;

View File

@ -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)

View File

@ -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";
};
};
};

View File

@ -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";
};
};
};

View File

@ -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";
};
};
};
};

View File

@ -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";
};
};
};
};

View File

@ -4,6 +4,9 @@
model = "Sandbox Verified Boot Test";
compatible = "sandbox";
binman {
};
reset@0 {
compatible = "sandbox,reset";
};

View File

@ -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";
};
};
};

View File

@ -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
--------------------------------------------------------

View File

@ -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)

View File

@ -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()

View File

@ -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-----

View File

@ -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";
};
};
};

View File

@ -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";
};
};
};

View File

@ -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";
};
};
};

View File

@ -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";
};
};
};

View File

@ -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";
};
};
};

View File

@ -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";
};
};
};

View File

@ -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";
};
};
};

View File

@ -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,

View File

@ -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)