mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2025-09-20 13:21:32 +02:00
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:
commit
52d8100b1d
1
Makefile
1
Makefile
@ -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
|
||||
|
@ -205,6 +205,7 @@ config SANDBOX
|
||||
imply KEYBOARD
|
||||
imply PHYSMEM
|
||||
imply GENERATE_ACPI_TABLE
|
||||
imply BINMAN
|
||||
|
||||
config SH
|
||||
bool "SuperH architecture"
|
||||
|
@ -7,6 +7,9 @@
|
||||
#define USB_CLASS_HUB 9
|
||||
|
||||
/ {
|
||||
binman {
|
||||
};
|
||||
|
||||
chosen {
|
||||
stdout-path = "/serial";
|
||||
};
|
||||
|
@ -61,6 +61,9 @@
|
||||
osd0 = "/osd";
|
||||
};
|
||||
|
||||
binman {
|
||||
};
|
||||
|
||||
config {
|
||||
testing-bool;
|
||||
testing-int = <123>;
|
||||
|
55
boot/Kconfig
55
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
|
||||
|
@ -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
|
||||
|
33
boot/bootm.c
33
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 */
|
||||
|
416
boot/image-pre-load.c
Normal file
416
boot/image-pre-load.c
Normal 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, ®, 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;
|
||||
}
|
10
cmd/Kconfig
10
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
|
||||
|
35
cmd/bootm.c
35
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"
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
*
|
||||
|
37
lib/Kconfig
37
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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -89,6 +89,9 @@ base_fdt = '''
|
||||
model = "Sandbox Verified Boot Test";
|
||||
compatible = "sandbox";
|
||||
|
||||
binman {
|
||||
};
|
||||
|
||||
reset@0 {
|
||||
compatible = "sandbox,reset";
|
||||
reg = <0>;
|
||||
|
@ -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)
|
||||
|
25
test/py/tests/vboot/sandbox-binman-pss.dts
Normal file
25
test/py/tests/vboot/sandbox-binman-pss.dts
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
24
test/py/tests/vboot/sandbox-binman.dts
Normal file
24
test/py/tests/vboot/sandbox-binman.dts
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
28
test/py/tests/vboot/sandbox-u-boot-global-pss.dts
Normal file
28
test/py/tests/vboot/sandbox-u-boot-global-pss.dts
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
27
test/py/tests/vboot/sandbox-u-boot-global.dts
Normal file
27
test/py/tests/vboot/sandbox-u-boot-global.dts
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
@ -4,6 +4,9 @@
|
||||
model = "Sandbox Verified Boot Test";
|
||||
compatible = "sandbox";
|
||||
|
||||
binman {
|
||||
};
|
||||
|
||||
reset@0 {
|
||||
compatible = "sandbox,reset";
|
||||
};
|
||||
|
36
test/py/tests/vboot/simple-images.its
Normal file
36
test/py/tests/vboot/simple-images.its
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
@ -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
|
||||
--------------------------------------------------------
|
||||
|
||||
|
162
tools/binman/etype/pre_load.py
Normal file
162
tools/binman/etype/pre_load.py
Normal 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)
|
@ -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()
|
||||
|
28
tools/binman/test/225_dev.key
Normal file
28
tools/binman/test/225_dev.key
Normal 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-----
|
22
tools/binman/test/225_pre_load.dts
Normal file
22
tools/binman/test/225_pre_load.dts
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
23
tools/binman/test/226_pre_load_pkcs.dts
Normal file
23
tools/binman/test/226_pre_load_pkcs.dts
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
23
tools/binman/test/227_pre_load_pss.dts
Normal file
23
tools/binman/test/227_pre_load_pss.dts
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
23
tools/binman/test/228_pre_load_invalid_padding.dts
Normal file
23
tools/binman/test/228_pre_load_invalid_padding.dts
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
23
tools/binman/test/229_pre_load_invalid_sha.dts
Normal file
23
tools/binman/test/229_pre_load_invalid_sha.dts
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
23
tools/binman/test/230_pre_load_invalid_algo.dts
Normal file
23
tools/binman/test/230_pre_load_invalid_algo.dts
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
23
tools/binman/test/231_pre_load_invalid_key.dts
Normal file
23
tools/binman/test/231_pre_load_invalid_key.dts
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
@ -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,
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user