mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-09-21 15:12:04 +00:00
Merge branch '2021-04-14-assorted-vboot-improvements'
- Add ECDSA support to FIT images - Improve FIT image loadables (incl fpga) support - Further FIT improvements with SPL
This commit is contained in:
commit
a6232e065d
31 changed files with 955 additions and 266 deletions
|
@ -150,13 +150,4 @@ int spl_start_uboot(void)
|
|||
return 1;
|
||||
}
|
||||
#endif /* CONFIG_SPL_OS_BOOT */
|
||||
#ifdef CONFIG_SPL_LOAD_FIT
|
||||
__weak int board_fit_config_name_match(const char *name)
|
||||
{
|
||||
/* Just empty function now - can't decide what to choose */
|
||||
debug("%s: %s\n", __func__, name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif /* CONFIG_SPL_BUILD */
|
||||
|
|
|
@ -151,13 +151,3 @@ void board_init_f(ulong dummy)
|
|||
#endif
|
||||
preloader_console_init();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SPL_LOAD_FIT
|
||||
int __weak board_fit_config_name_match(const char *name)
|
||||
{
|
||||
/* Just empty function now - can't decide what to choose */
|
||||
debug("%s: %s\n", __func__, name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -202,6 +202,16 @@ config SPL_LOAD_FIT
|
|||
particular it can handle selecting from multiple device tree
|
||||
and passing the correct one to U-Boot.
|
||||
|
||||
This path has the following limitations:
|
||||
|
||||
1. "loadables" images, other than FTDs, which do not have a "load"
|
||||
property will not be loaded. This limitation also applies to FPGA
|
||||
images with the correct "compatible" string.
|
||||
2. For FPGA images, only the "compatible" = "u-boot,fpga-legacy"
|
||||
loading method is supported.
|
||||
3. FDTs are only loaded for images with an "os" property of "u-boot".
|
||||
"linux" images are also supported with Falcon boot mode.
|
||||
|
||||
config SPL_LOAD_FIT_ADDRESS
|
||||
hex "load address of fit image"
|
||||
depends on SPL_LOAD_FIT
|
||||
|
|
|
@ -22,6 +22,11 @@ ulong fdt_getprop_u32(const void *fdt, int node, const char *prop)
|
|||
return fdt32_to_cpu(*cell);
|
||||
}
|
||||
|
||||
__weak int board_fit_config_name_match(const char *name)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all /configurations subnodes and call a platform specific
|
||||
* function to find the matching configuration.
|
||||
|
|
|
@ -16,7 +16,7 @@ DECLARE_GLOBAL_DATA_PTR;
|
|||
#include <fdt_region.h>
|
||||
#include <image.h>
|
||||
#include <u-boot/rsa.h>
|
||||
#include <u-boot/rsa-checksum.h>
|
||||
#include <u-boot/hash-checksum.h>
|
||||
|
||||
#define IMAGE_MAX_HASHED_NODES 100
|
||||
|
||||
|
|
|
@ -1959,6 +1959,8 @@ static const char *fit_get_image_type_property(int type)
|
|||
return FIT_FDT_PROP;
|
||||
case IH_TYPE_KERNEL:
|
||||
return FIT_KERNEL_PROP;
|
||||
case IH_TYPE_FIRMWARE:
|
||||
return FIT_FIRMWARE_PROP;
|
||||
case IH_TYPE_RAMDISK:
|
||||
return FIT_RAMDISK_PROP;
|
||||
case IH_TYPE_X86_SETUP:
|
||||
|
@ -2091,6 +2093,7 @@ int fit_image_load(bootm_headers_t *images, ulong addr,
|
|||
bootstage_mark(bootstage_id + BOOTSTAGE_SUB_CHECK_ALL);
|
||||
type_ok = fit_image_check_type(fit, noffset, image_type) ||
|
||||
fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE) ||
|
||||
fit_image_check_type(fit, noffset, IH_TYPE_TEE) ||
|
||||
(image_type == IH_TYPE_KERNEL &&
|
||||
fit_image_check_type(fit, noffset, IH_TYPE_KERNEL_NOLOAD));
|
||||
|
||||
|
@ -2098,6 +2101,7 @@ int fit_image_load(bootm_headers_t *images, ulong addr,
|
|||
image_type == IH_TYPE_FPGA ||
|
||||
fit_image_check_os(fit, noffset, IH_OS_LINUX) ||
|
||||
fit_image_check_os(fit, noffset, IH_OS_U_BOOT) ||
|
||||
fit_image_check_os(fit, noffset, IH_OS_TEE) ||
|
||||
fit_image_check_os(fit, noffset, IH_OS_OPENRTOS) ||
|
||||
fit_image_check_os(fit, noffset, IH_OS_EFI) ||
|
||||
fit_image_check_os(fit, noffset, IH_OS_VXWORKS);
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
DECLARE_GLOBAL_DATA_PTR;
|
||||
#endif /* !USE_HOSTCC*/
|
||||
#include <image.h>
|
||||
#include <u-boot/ecdsa.h>
|
||||
#include <u-boot/rsa.h>
|
||||
#include <u-boot/rsa-checksum.h>
|
||||
#include <u-boot/hash-checksum.h>
|
||||
|
||||
#define IMAGE_MAX_HASHED_NODES 100
|
||||
|
||||
|
@ -83,8 +84,14 @@ struct crypto_algo crypto_algos[] = {
|
|||
.sign = rsa_sign,
|
||||
.add_verify_data = rsa_add_verify_data,
|
||||
.verify = rsa_verify,
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
.name = "ecdsa256",
|
||||
.key_len = ECDSA256_BYTES,
|
||||
.sign = ecdsa_sign,
|
||||
.add_verify_data = ecdsa_add_verify_data,
|
||||
.verify = ecdsa_verify,
|
||||
},
|
||||
};
|
||||
|
||||
struct padding_algo padding_algos[] = {
|
||||
|
|
|
@ -202,7 +202,7 @@ static int spl_load_fit_image(struct spl_image_info *spl_image,
|
|||
{
|
||||
bootm_headers_t images;
|
||||
const char *fit_uname_config = NULL;
|
||||
const char *fit_uname_fdt = FIT_FDT_PROP;
|
||||
uintptr_t fdt_hack;
|
||||
const char *uname;
|
||||
ulong fw_data = 0, dt_data = 0, img_data = 0;
|
||||
ulong fw_len = 0, dt_len = 0, img_len = 0;
|
||||
|
@ -215,15 +215,33 @@ static int spl_load_fit_image(struct spl_image_info *spl_image,
|
|||
ret = fit_image_load(&images, (ulong)header,
|
||||
NULL, &fit_uname_config,
|
||||
IH_ARCH_DEFAULT, IH_TYPE_STANDALONE, -1,
|
||||
FIT_LOAD_REQUIRED, &fw_data, &fw_len);
|
||||
FIT_LOAD_OPTIONAL, &fw_data, &fw_len);
|
||||
if (ret >= 0) {
|
||||
printf("DEPRECATED: 'standalone = ' property.");
|
||||
printf("Please use either 'firmware =' or 'kernel ='\n");
|
||||
} else {
|
||||
ret = fit_image_load(&images, (ulong)header, NULL,
|
||||
&fit_uname_config, IH_ARCH_DEFAULT,
|
||||
IH_TYPE_FIRMWARE, -1, FIT_LOAD_OPTIONAL,
|
||||
&fw_data, &fw_len);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
ret = fit_image_load(&images, (ulong)header, NULL,
|
||||
&fit_uname_config, IH_ARCH_DEFAULT,
|
||||
IH_TYPE_KERNEL, -1, FIT_LOAD_OPTIONAL,
|
||||
&fw_data, &fw_len);
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
spl_image->size = fw_len;
|
||||
spl_image->entry_point = fw_data;
|
||||
spl_image->load_addr = fw_data;
|
||||
spl_image->os = IH_OS_U_BOOT;
|
||||
spl_image->name = "U-Boot";
|
||||
if (fit_image_get_os(header, ret, &spl_image->os))
|
||||
spl_image->os = IH_OS_INVALID;
|
||||
spl_image->name = genimg_get_os_name(spl_image->os);
|
||||
|
||||
debug(SPL_TPL_PROMPT "payload image: %32s load addr: 0x%lx size: %d\n",
|
||||
spl_image->name, spl_image->load_addr, spl_image->size);
|
||||
|
@ -231,13 +249,21 @@ static int spl_load_fit_image(struct spl_image_info *spl_image,
|
|||
#ifdef CONFIG_SPL_FIT_SIGNATURE
|
||||
images.verify = 1;
|
||||
#endif
|
||||
ret = fit_image_load(&images, (ulong)header,
|
||||
&fit_uname_fdt, &fit_uname_config,
|
||||
ret = fit_image_load(&images, (ulong)header, NULL, &fit_uname_config,
|
||||
IH_ARCH_DEFAULT, IH_TYPE_FLATDT, -1,
|
||||
FIT_LOAD_OPTIONAL, &dt_data, &dt_len);
|
||||
if (ret >= 0)
|
||||
if (ret >= 0) {
|
||||
spl_image->fdt_addr = (void *)dt_data;
|
||||
|
||||
if (spl_image->os == IH_OS_U_BOOT) {
|
||||
/* HACK: U-boot expects FDT at a specific address */
|
||||
fdt_hack = spl_image->load_addr + spl_image->size;
|
||||
fdt_hack = (fdt_hack + 3) & ~3;
|
||||
debug("Relocating FDT to %p\n", spl_image->fdt_addr);
|
||||
memcpy((void *)fdt_hack, spl_image->fdt_addr, dt_len);
|
||||
}
|
||||
}
|
||||
|
||||
conf_noffset = fit_conf_get_node((const void *)header,
|
||||
fit_uname_config);
|
||||
if (conf_noffset <= 0)
|
||||
|
|
|
@ -224,7 +224,7 @@ static int get_aligned_image_size(struct spl_load_info *info, int data_size,
|
|||
* @image_info: will be filled with information about the loaded image
|
||||
* If the FIT node does not contain a "load" (address) property,
|
||||
* the image gets loaded to the address pointed to by the
|
||||
* load_addr member in this struct.
|
||||
* load_addr member in this struct, if load_addr is not 0
|
||||
*
|
||||
* Return: 0 on success or a negative error number.
|
||||
*/
|
||||
|
@ -259,8 +259,14 @@ static int spl_load_fit_image(struct spl_load_info *info, ulong sector,
|
|||
debug("%s ", genimg_get_comp_name(image_comp));
|
||||
}
|
||||
|
||||
if (fit_image_get_load(fit, node, &load_addr))
|
||||
if (fit_image_get_load(fit, node, &load_addr)) {
|
||||
if (!image_info->load_addr) {
|
||||
printf("Can't load %s: No load address and no buffer\n",
|
||||
fit_get_name(fit, node, NULL));
|
||||
return -ENOBUFS;
|
||||
}
|
||||
load_addr = image_info->load_addr;
|
||||
}
|
||||
|
||||
if (!fit_image_get_data_position(fit, node, &offset)) {
|
||||
external_data = true;
|
||||
|
@ -474,6 +480,20 @@ static int spl_fit_record_loadable(const struct spl_fit_info *ctx, int index,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int spl_fit_image_is_fpga(const void *fit, int node)
|
||||
{
|
||||
const char *type;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_SPL_FPGA))
|
||||
return 0;
|
||||
|
||||
type = fdt_getprop(fit, node, FIT_TYPE_PROP, NULL);
|
||||
if (!type)
|
||||
return 0;
|
||||
|
||||
return !strcmp(type, "fpga");
|
||||
}
|
||||
|
||||
static int spl_fit_image_get_os(const void *fit, int noffset, uint8_t *os)
|
||||
{
|
||||
if (!CONFIG_IS_ENABLED(FIT_IMAGE_TINY) || CONFIG_IS_ENABLED(OS_BOOT))
|
||||
|
@ -523,6 +543,64 @@ __weak bool spl_load_simple_fit_skip_processing(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
static void warn_deprecated(const char *msg)
|
||||
{
|
||||
printf("DEPRECATED: %s\n", msg);
|
||||
printf("\tSee doc/uImage.FIT/source_file_format.txt\n");
|
||||
}
|
||||
|
||||
static int spl_fit_upload_fpga(struct spl_fit_info *ctx, int node,
|
||||
struct spl_image_info *fpga_image)
|
||||
{
|
||||
const char *compatible;
|
||||
int ret;
|
||||
|
||||
debug("FPGA bitstream at: %x, size: %x\n",
|
||||
(u32)fpga_image->load_addr, fpga_image->size);
|
||||
|
||||
compatible = fdt_getprop(ctx->fit, node, "compatible", NULL);
|
||||
if (!compatible)
|
||||
warn_deprecated("'fpga' image without 'compatible' property");
|
||||
else if (strcmp(compatible, "u-boot,fpga-legacy"))
|
||||
printf("Ignoring compatible = %s property\n", compatible);
|
||||
|
||||
ret = fpga_load(0, (void *)fpga_image->load_addr, fpga_image->size,
|
||||
BIT_FULL);
|
||||
if (ret) {
|
||||
printf("%s: Cannot load the image to the FPGA\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
puts("FPGA image loaded from FIT\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spl_fit_load_fpga(struct spl_fit_info *ctx,
|
||||
struct spl_load_info *info, ulong sector)
|
||||
{
|
||||
int node, ret;
|
||||
|
||||
struct spl_image_info fpga_image = {
|
||||
.load_addr = 0,
|
||||
};
|
||||
|
||||
node = spl_fit_get_image_node(ctx, "fpga", 0);
|
||||
if (node < 0)
|
||||
return node;
|
||||
|
||||
warn_deprecated("'fpga' property in config node. Use 'loadables'");
|
||||
|
||||
/* Load the image and set up the fpga_image structure */
|
||||
ret = spl_load_fit_image(info, sector, ctx, node, &fpga_image);
|
||||
if (ret) {
|
||||
printf("%s: Cannot load the FPGA: %i\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return spl_fit_upload_fpga(ctx, node, &fpga_image);
|
||||
}
|
||||
|
||||
static int spl_simple_fit_read(struct spl_fit_info *ctx,
|
||||
struct spl_load_info *info, ulong sector,
|
||||
const void *fit_header)
|
||||
|
@ -606,31 +684,8 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_SPL_FPGA
|
||||
node = spl_fit_get_image_node(&ctx, "fpga", 0);
|
||||
if (node >= 0) {
|
||||
/* Load the image and set up the spl_image structure */
|
||||
ret = spl_load_fit_image(info, sector, &ctx, node, spl_image);
|
||||
if (ret) {
|
||||
printf("%s: Cannot load the FPGA: %i\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
debug("FPGA bitstream at: %x, size: %x\n",
|
||||
(u32)spl_image->load_addr, spl_image->size);
|
||||
|
||||
ret = fpga_load(0, (const void *)spl_image->load_addr,
|
||||
spl_image->size, BIT_FULL);
|
||||
if (ret) {
|
||||
printf("%s: Cannot load the image to the FPGA\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
puts("FPGA image loaded from FIT\n");
|
||||
node = -1;
|
||||
}
|
||||
#endif
|
||||
if (IS_ENABLED(CONFIG_SPL_FPGA))
|
||||
spl_fit_load_fpga(&ctx, info, sector);
|
||||
|
||||
/*
|
||||
* Find the U-Boot image using the following search order:
|
||||
|
@ -700,6 +755,7 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
|
|||
if (firmware_node == node)
|
||||
continue;
|
||||
|
||||
image_info.load_addr = 0;
|
||||
ret = spl_load_fit_image(info, sector, &ctx, node, &image_info);
|
||||
if (ret < 0) {
|
||||
printf("%s: can't load image loadables index %d (ret = %d)\n",
|
||||
|
@ -707,6 +763,9 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (spl_fit_image_is_fpga(ctx.fit, node))
|
||||
spl_fit_upload_fpga(&ctx, node, &image_info);
|
||||
|
||||
if (!spl_fit_image_get_os(ctx.fit, node, &os_type))
|
||||
debug("Loadable is %s\n", genimg_get_os_name(os_type));
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
arch = "arm";
|
||||
compression = "none";
|
||||
load = <0x30000000>;
|
||||
compatible = "u-boot,fpga-legacy"
|
||||
hash-1 {
|
||||
algo = "md5";
|
||||
};
|
||||
|
@ -61,7 +62,7 @@
|
|||
description = "Linux with fpga";
|
||||
kernel = "linux_kernel";
|
||||
fdt = "fdt-1";
|
||||
fpga = "fpga";
|
||||
loadables = "fpga";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -142,7 +142,7 @@ public key in U-Boot's control FDT (using CONFIG_OF_CONTROL).
|
|||
Public keys should be stored as sub-nodes in a /signature node. Required
|
||||
properties are:
|
||||
|
||||
- algo: Algorithm name (e.g. "sha1,rsa2048")
|
||||
- algo: Algorithm name (e.g. "sha1,rsa2048" or "sha256,ecdsa256")
|
||||
|
||||
Optional properties are:
|
||||
|
||||
|
@ -167,6 +167,11 @@ For RSA the following are mandatory:
|
|||
- rsa,r-squared: (2^num-bits)^2 as a big-endian multi-word integer
|
||||
- rsa,n0-inverse: -1 / modulus[0] mod 2^32
|
||||
|
||||
For ECDSA the following are mandatory:
|
||||
- ecdsa,curve: Name of ECDSA curve (e.g. "prime256v1")
|
||||
- ecdsa,x-point: Public key X coordinate as a big-endian multi-word integer
|
||||
- ecdsa,y-point: Public key Y coordinate as a big-endian multi-word integer
|
||||
|
||||
These parameters can be added to a binary device tree using parameter -K of the
|
||||
mkimage command::
|
||||
|
||||
|
@ -467,6 +472,19 @@ Test Verified Boot Run: signed config with bad hash: OK
|
|||
Test passed
|
||||
|
||||
|
||||
Software signing: keydir vs keyfile
|
||||
-----------------------------------
|
||||
|
||||
In the simplest case, signing is done by giving mkimage the 'keyfile'. This is
|
||||
the path to a file containing the signing key.
|
||||
|
||||
The alternative is to pass the 'keydir' argument. In this case the filename of
|
||||
the key is derived from the 'keydir' and the "key-name-hint" property in the
|
||||
FIT. In this case the "key-name-hint" property is mandatory, and the key must
|
||||
exist in "<keydir>/<key-name-hint>.<ext>" Here the extension "ext" is
|
||||
specific to the signing algorithm.
|
||||
|
||||
|
||||
Hardware Signing with PKCS#11 or with HSM
|
||||
-----------------------------------------
|
||||
|
||||
|
|
|
@ -184,6 +184,7 @@ the '/images' node should have the following layout:
|
|||
Mandatory for types: "firmware", and "kernel".
|
||||
- compatible : compatible method for loading image.
|
||||
Mandatory for types: "fpga", and images that do not specify a load address.
|
||||
To use the generic fpga loading routine, use "u-boot,fpga-legacy".
|
||||
|
||||
Optional nodes:
|
||||
- hash-1 : Each hash sub-node represents separate hash or checksum
|
||||
|
|
|
@ -1136,9 +1136,10 @@ int fit_cipher_data(const char *keydir, void *keydest, void *fit,
|
|||
* 0, on success
|
||||
* libfdt error code, on failure
|
||||
*/
|
||||
int fit_add_verification_data(const char *keydir, void *keydest, void *fit,
|
||||
const char *comment, int require_keys,
|
||||
const char *engine_id, const char *cmdname);
|
||||
int fit_add_verification_data(const char *keydir, const char *keyfile,
|
||||
void *keydest, void *fit, const char *comment,
|
||||
int require_keys, const char *engine_id,
|
||||
const char *cmdname);
|
||||
|
||||
int fit_image_verify_with_data(const void *fit, int image_noffset,
|
||||
const void *data, size_t size);
|
||||
|
@ -1224,16 +1225,19 @@ int calculate_hash(const void *data, int data_len, const char *algo,
|
|||
# if defined(CONFIG_FIT_SIGNATURE)
|
||||
# define IMAGE_ENABLE_SIGN 1
|
||||
# define IMAGE_ENABLE_VERIFY 1
|
||||
# define IMAGE_ENABLE_VERIFY_ECDSA 1
|
||||
# define FIT_IMAGE_ENABLE_VERIFY 1
|
||||
# include <openssl/evp.h>
|
||||
# else
|
||||
# define IMAGE_ENABLE_SIGN 0
|
||||
# define IMAGE_ENABLE_VERIFY 0
|
||||
# define IMAGE_ENABLE_VERIFY_ECDSA 0
|
||||
# define FIT_IMAGE_ENABLE_VERIFY 0
|
||||
# endif
|
||||
#else
|
||||
# define IMAGE_ENABLE_SIGN 0
|
||||
# define IMAGE_ENABLE_VERIFY CONFIG_IS_ENABLED(RSA_VERIFY)
|
||||
# define IMAGE_ENABLE_VERIFY_ECDSA 0
|
||||
# define FIT_IMAGE_ENABLE_VERIFY CONFIG_IS_ENABLED(FIT_SIGNATURE)
|
||||
#endif
|
||||
|
||||
|
@ -1253,10 +1257,17 @@ void image_set_host_blob(void *host_blob);
|
|||
#endif
|
||||
#endif /* IMAGE_ENABLE_FIT */
|
||||
|
||||
/* Information passed to the signing routines */
|
||||
/*
|
||||
* Information passed to the signing routines
|
||||
*
|
||||
* Either 'keydir', 'keyname', or 'keyfile' can be NULL. However, either
|
||||
* 'keyfile', or both 'keydir' and 'keyname' should have valid values. If
|
||||
* neither are valid, some operations might fail with EINVAL.
|
||||
*/
|
||||
struct image_sign_info {
|
||||
const char *keydir; /* Directory conaining keys */
|
||||
const char *keyname; /* Name of key to use */
|
||||
const char *keyfile; /* Filename of private or public key */
|
||||
void *fit; /* Pointer to FIT blob */
|
||||
int node_offset; /* Offset of signature node */
|
||||
const char *name; /* Algorithm name */
|
||||
|
@ -1283,7 +1294,7 @@ struct image_region {
|
|||
};
|
||||
|
||||
#if IMAGE_ENABLE_VERIFY
|
||||
# include <u-boot/rsa-checksum.h>
|
||||
# include <u-boot/hash-checksum.h>
|
||||
#endif
|
||||
struct checksum_algo {
|
||||
const char *name;
|
||||
|
|
94
include/u-boot/ecdsa.h
Normal file
94
include/u-boot/ecdsa.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (c) 2020, Alexandru Gagniuc <mr.nuke.me@gmail.com>.
|
||||
*/
|
||||
|
||||
#ifndef _ECDSA_H
|
||||
#define _ECDSA_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <image.h>
|
||||
#include <linux/kconfig.h>
|
||||
|
||||
/**
|
||||
* crypto_algo API impementation for ECDSA;
|
||||
* @see "struct crypto_algo"
|
||||
* @{
|
||||
*/
|
||||
#if IMAGE_ENABLE_SIGN
|
||||
/**
|
||||
* sign() - calculate and return signature for given input data
|
||||
*
|
||||
* @info: Specifies key and FIT information
|
||||
* @data: Pointer to the input data
|
||||
* @data_len: Data length
|
||||
* @sigp: Set to an allocated buffer holding the signature
|
||||
* @sig_len: Set to length of the calculated hash
|
||||
*
|
||||
* This computes input data signature according to selected algorithm.
|
||||
* Resulting signature value is placed in an allocated buffer, the
|
||||
* pointer is returned as *sigp. The length of the calculated
|
||||
* signature is returned via the sig_len pointer argument. The caller
|
||||
* should free *sigp.
|
||||
*
|
||||
* @return: 0, on success, -ve on error
|
||||
*/
|
||||
int ecdsa_sign(struct image_sign_info *info, const struct image_region region[],
|
||||
int region_count, uint8_t **sigp, uint *sig_len);
|
||||
|
||||
/**
|
||||
* add_verify_data() - Add verification information to FDT
|
||||
*
|
||||
* Add public key information to the FDT node, suitable for
|
||||
* verification at run-time. The information added depends on the
|
||||
* algorithm being used. I just copypasted this from rsa.h.
|
||||
*
|
||||
* @info: Specifies key and FIT information
|
||||
* @keydest: Destination FDT blob for public key data
|
||||
* @return: 0, on success, -ENOSPC if the keydest FDT blob ran out of space,
|
||||
* other -ve value on error
|
||||
*/
|
||||
int ecdsa_add_verify_data(struct image_sign_info *info, void *keydest);
|
||||
#else
|
||||
static inline
|
||||
int ecdsa_sign(struct image_sign_info *info, const struct image_region region[],
|
||||
int region_count, uint8_t **sigp, uint *sig_len)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static inline
|
||||
int ecdsa_add_verify_data(struct image_sign_info *info, void *keydest)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if IMAGE_ENABLE_VERIFY_ECDSA
|
||||
/**
|
||||
* verify() - Verify a signature against some data
|
||||
*
|
||||
* @info: Specifies key and FIT information
|
||||
* @data: Pointer to the input data
|
||||
* @data_len: Data length
|
||||
* @sig: Signature
|
||||
* @sig_len: Number of bytes in signature
|
||||
* @return 0 if verified, -ve on error
|
||||
*/
|
||||
int ecdsa_verify(struct image_sign_info *info,
|
||||
const struct image_region region[], int region_count,
|
||||
uint8_t *sig, uint sig_len);
|
||||
#else
|
||||
static inline
|
||||
int ecdsa_verify(struct image_sign_info *info,
|
||||
const struct image_region region[], int region_count,
|
||||
uint8_t *sig, uint sig_len)
|
||||
{
|
||||
return -ENXIO;
|
||||
}
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
#define ECDSA256_BYTES (256 / 8)
|
||||
|
||||
#endif
|
27
include/u-boot/fdt-libcrypto.h
Normal file
27
include/u-boot/fdt-libcrypto.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (c) 2020, Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||
* Copyright (c) 2013, Google Inc.
|
||||
*/
|
||||
|
||||
#ifndef _FDT_LIBCRYPTO_H
|
||||
#define _FDT_LIBCRYPTO_H
|
||||
|
||||
#include <openssl/bn.h>
|
||||
|
||||
/**
|
||||
* fdt_add_bignum() - Write a libcrypto BIGNUM as an FDT property
|
||||
*
|
||||
* Convert a libcrypto BIGNUM * into a big endian array of integers.
|
||||
*
|
||||
* @blob: FDT blob to modify
|
||||
* @noffset: Offset of the FDT node
|
||||
* @prop_name: What to call the property in the FDT
|
||||
* @num: pointer to a libcrypto big number
|
||||
* @num_bits: How big is 'num' in bits?
|
||||
* @return 0 if all good all working, -ve on horror
|
||||
*/
|
||||
int fdt_add_bignum(void *blob, int noffset, const char *prop_name,
|
||||
BIGNUM *num, int num_bits);
|
||||
|
||||
#endif /* _FDT_LIBCRYPTO_H */
|
|
@ -61,6 +61,7 @@ endif
|
|||
obj-$(CONFIG_$(SPL_)ACPIGEN) += acpi/
|
||||
obj-$(CONFIG_$(SPL_)MD5) += md5.o
|
||||
obj-$(CONFIG_$(SPL_)RSA) += rsa/
|
||||
obj-$(CONFIG_FIT_SIGNATURE) += hash-checksum.o
|
||||
obj-$(CONFIG_SHA1) += sha1.o
|
||||
obj-$(CONFIG_SHA256) += sha256.o
|
||||
obj-$(CONFIG_SHA512_ALGO) += sha512.o
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include <linux/bitops.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/asn1.h>
|
||||
#include <u-boot/rsa-checksum.h>
|
||||
#include <u-boot/hash-checksum.h>
|
||||
#include <crypto/public_key.h>
|
||||
#include <crypto/pkcs7_parser.h>
|
||||
#else
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#ifdef __UBOOT__
|
||||
#include <crypto/x509_parser.h>
|
||||
#include <u-boot/rsa-checksum.h>
|
||||
#include <u-boot/hash-checksum.h>
|
||||
#else
|
||||
#include <linux/slab.h>
|
||||
#include <keys/asymmetric-subtype.h>
|
||||
|
|
318
lib/ecdsa/ecdsa-libcrypto.c
Normal file
318
lib/ecdsa/ecdsa-libcrypto.c
Normal file
|
@ -0,0 +1,318 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* ECDSA image signing implementation using libcrypto backend
|
||||
*
|
||||
* The signature is a binary representation of the (R, S) points, padded to the
|
||||
* key size. The signature will be (2 * key_size_bits) / 8 bytes.
|
||||
*
|
||||
* Deviations from behavior of RSA equivalent:
|
||||
* - Verification uses private key. This is not technically required, but a
|
||||
* limitation on how clumsy the openssl API is to use.
|
||||
* - Handling of keys and key paths:
|
||||
* - The '-K' key directory option must contain path to the key file,
|
||||
* instead of the key directory.
|
||||
* - No assumptions are made about the file extension of the key
|
||||
* - The 'key-name-hint' property is only used for naming devicetree nodes,
|
||||
* but is not used for looking up keys on the filesystem.
|
||||
*
|
||||
* Copyright (c) 2020,2021, Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||
*/
|
||||
|
||||
#include <u-boot/ecdsa.h>
|
||||
#include <u-boot/fdt-libcrypto.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/bn.h>
|
||||
|
||||
/* Image signing context for openssl-libcrypto */
|
||||
struct signer {
|
||||
EVP_PKEY *evp_key; /* Pointer to EVP_PKEY object */
|
||||
EC_KEY *ecdsa_key; /* Pointer to EC_KEY object */
|
||||
void *hash; /* Pointer to hash used for verification */
|
||||
void *signature; /* Pointer to output signature. Do not free()!*/
|
||||
};
|
||||
|
||||
static int alloc_ctx(struct signer *ctx, const struct image_sign_info *info)
|
||||
{
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
|
||||
if (!OPENSSL_init_ssl(0, NULL)) {
|
||||
fprintf(stderr, "Failure to init SSL library\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->hash = malloc(info->checksum->checksum_len);
|
||||
ctx->signature = malloc(info->crypto->key_len * 2);
|
||||
|
||||
if (!ctx->hash || !ctx->signature)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_ctx(struct signer *ctx)
|
||||
{
|
||||
if (ctx->ecdsa_key)
|
||||
EC_KEY_free(ctx->ecdsa_key);
|
||||
|
||||
if (ctx->evp_key)
|
||||
EVP_PKEY_free(ctx->evp_key);
|
||||
|
||||
if (ctx->hash)
|
||||
free(ctx->hash);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an ECDSA signature to raw format
|
||||
*
|
||||
* openssl DER-encodes 'binary' signatures. We want the signature in a raw
|
||||
* (R, S) point pair. So we have to dance a bit.
|
||||
*/
|
||||
static void ecdsa_sig_encode_raw(void *buf, const ECDSA_SIG *sig, size_t order)
|
||||
{
|
||||
int point_bytes = order;
|
||||
const BIGNUM *r, *s;
|
||||
uintptr_t s_buf;
|
||||
|
||||
ECDSA_SIG_get0(sig, &r, &s);
|
||||
s_buf = (uintptr_t)buf + point_bytes;
|
||||
BN_bn2binpad(r, buf, point_bytes);
|
||||
BN_bn2binpad(s, (void *)s_buf, point_bytes);
|
||||
}
|
||||
|
||||
/* Get a signature from a raw encoding */
|
||||
static ECDSA_SIG *ecdsa_sig_from_raw(void *buf, size_t order)
|
||||
{
|
||||
int point_bytes = order;
|
||||
uintptr_t s_buf;
|
||||
ECDSA_SIG *sig;
|
||||
BIGNUM *r, *s;
|
||||
|
||||
sig = ECDSA_SIG_new();
|
||||
if (!sig)
|
||||
return NULL;
|
||||
|
||||
s_buf = (uintptr_t)buf + point_bytes;
|
||||
r = BN_bin2bn(buf, point_bytes, NULL);
|
||||
s = BN_bin2bn((void *)s_buf, point_bytes, NULL);
|
||||
ECDSA_SIG_set0(sig, r, s);
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
/* ECDSA key size in bytes */
|
||||
static size_t ecdsa_key_size_bytes(const EC_KEY *key)
|
||||
{
|
||||
const EC_GROUP *group;
|
||||
|
||||
group = EC_KEY_get0_group(key);
|
||||
return EC_GROUP_order_bits(group) / 8;
|
||||
}
|
||||
|
||||
static int read_key(struct signer *ctx, const char *key_name)
|
||||
{
|
||||
FILE *f = fopen(key_name, "r");
|
||||
|
||||
if (!f) {
|
||||
fprintf(stderr, "Can not get key file '%s'\n", key_name);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ctx->evp_key = PEM_read_PrivateKey(f, NULL, NULL, NULL);
|
||||
fclose(f);
|
||||
if (!ctx->evp_key) {
|
||||
fprintf(stderr, "Can not read key from '%s'\n", key_name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_id(ctx->evp_key) != EVP_PKEY_EC) {
|
||||
fprintf(stderr, "'%s' is not an ECDSA key\n", key_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctx->ecdsa_key = EVP_PKEY_get1_EC_KEY(ctx->evp_key);
|
||||
if (!ctx->ecdsa_key)
|
||||
fprintf(stderr, "Can not extract ECDSA key\n");
|
||||
|
||||
return (ctx->ecdsa_key) ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
/* Prepare a 'signer' context that's ready to sign and verify. */
|
||||
static int prepare_ctx(struct signer *ctx, const struct image_sign_info *info)
|
||||
{
|
||||
int key_len_bytes, ret;
|
||||
char kname[1024];
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
|
||||
if (info->keyfile) {
|
||||
snprintf(kname, sizeof(kname), "%s", info->keyfile);
|
||||
} else if (info->keydir && info->keyname) {
|
||||
snprintf(kname, sizeof(kname), "%s/%s.pem", info->keydir,
|
||||
info->keyname);
|
||||
} else {
|
||||
fprintf(stderr, "keyfile, keyname, or key-name-hint missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = alloc_ctx(ctx, info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = read_key(ctx, kname);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
key_len_bytes = ecdsa_key_size_bytes(ctx->ecdsa_key);
|
||||
if (key_len_bytes != info->crypto->key_len) {
|
||||
fprintf(stderr, "Expected a %u-bit key, got %u-bit key\n",
|
||||
info->crypto->key_len * 8, key_len_bytes * 8);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_sign(struct signer *ctx, struct image_sign_info *info,
|
||||
const struct image_region region[], int region_count)
|
||||
{
|
||||
const struct checksum_algo *algo = info->checksum;
|
||||
ECDSA_SIG *sig;
|
||||
|
||||
algo->calculate(algo->name, region, region_count, ctx->hash);
|
||||
sig = ECDSA_do_sign(ctx->hash, algo->checksum_len, ctx->ecdsa_key);
|
||||
|
||||
ecdsa_sig_encode_raw(ctx->signature, sig, info->crypto->key_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ecdsa_check_signature(struct signer *ctx, struct image_sign_info *info)
|
||||
{
|
||||
ECDSA_SIG *sig;
|
||||
int okay;
|
||||
|
||||
sig = ecdsa_sig_from_raw(ctx->signature, info->crypto->key_len);
|
||||
if (!sig)
|
||||
return -ENOMEM;
|
||||
|
||||
okay = ECDSA_do_verify(ctx->hash, info->checksum->checksum_len,
|
||||
sig, ctx->ecdsa_key);
|
||||
if (!okay)
|
||||
fprintf(stderr, "WARNING: Signature is fake news!\n");
|
||||
|
||||
ECDSA_SIG_free(sig);
|
||||
return !okay;
|
||||
}
|
||||
|
||||
static int do_verify(struct signer *ctx, struct image_sign_info *info,
|
||||
const struct image_region region[], int region_count,
|
||||
uint8_t *raw_sig, uint sig_len)
|
||||
{
|
||||
const struct checksum_algo *algo = info->checksum;
|
||||
|
||||
if (sig_len != info->crypto->key_len * 2) {
|
||||
fprintf(stderr, "Signature has wrong length\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(ctx->signature, raw_sig, sig_len);
|
||||
algo->calculate(algo->name, region, region_count, ctx->hash);
|
||||
|
||||
return ecdsa_check_signature(ctx, info);
|
||||
}
|
||||
|
||||
int ecdsa_sign(struct image_sign_info *info, const struct image_region region[],
|
||||
int region_count, uint8_t **sigp, uint *sig_len)
|
||||
{
|
||||
struct signer ctx;
|
||||
int ret;
|
||||
|
||||
ret = prepare_ctx(&ctx, info);
|
||||
if (ret >= 0) {
|
||||
do_sign(&ctx, info, region, region_count);
|
||||
*sigp = ctx.signature;
|
||||
*sig_len = info->crypto->key_len * 2;
|
||||
|
||||
ret = ecdsa_check_signature(&ctx, info);
|
||||
}
|
||||
|
||||
free_ctx(&ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ecdsa_verify(struct image_sign_info *info,
|
||||
const struct image_region region[], int region_count,
|
||||
uint8_t *sig, uint sig_len)
|
||||
{
|
||||
struct signer ctx;
|
||||
int ret;
|
||||
|
||||
ret = prepare_ctx(&ctx, info);
|
||||
if (ret >= 0)
|
||||
ret = do_verify(&ctx, info, region, region_count, sig, sig_len);
|
||||
|
||||
free_ctx(&ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_add(struct signer *ctx, void *fdt, const char *key_node_name)
|
||||
{
|
||||
int signature_node, key_node, ret, key_bits;
|
||||
const char *curve_name;
|
||||
const EC_GROUP *group;
|
||||
const EC_POINT *point;
|
||||
BIGNUM *x, *y;
|
||||
|
||||
signature_node = fdt_subnode_offset(fdt, 0, FIT_SIG_NODENAME);
|
||||
if (signature_node < 0) {
|
||||
fprintf(stderr, "Could not find 'signature node: %s\n",
|
||||
fdt_strerror(signature_node));
|
||||
return signature_node;
|
||||
}
|
||||
|
||||
key_node = fdt_add_subnode(fdt, signature_node, key_node_name);
|
||||
if (key_node < 0) {
|
||||
fprintf(stderr, "Could not create '%s' node: %s\n",
|
||||
key_node_name, fdt_strerror(key_node));
|
||||
return key_node;
|
||||
}
|
||||
|
||||
group = EC_KEY_get0_group(ctx->ecdsa_key);
|
||||
key_bits = EC_GROUP_order_bits(group);
|
||||
curve_name = OBJ_nid2sn(EC_GROUP_get_curve_name(group));
|
||||
/* Let 'x' and 'y' memory leak by not BN_free()'ing them. */
|
||||
x = BN_new();
|
||||
y = BN_new();
|
||||
point = EC_KEY_get0_public_key(ctx->ecdsa_key);
|
||||
EC_POINT_get_affine_coordinates(group, point, x, y, NULL);
|
||||
|
||||
ret = fdt_setprop_string(fdt, key_node, "ecdsa,curve", curve_name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = fdt_add_bignum(fdt, key_node, "ecdsa,x-point", x, key_bits);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = fdt_add_bignum(fdt, key_node, "ecdsa,y-point", y, key_bits);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ecdsa_add_verify_data(struct image_sign_info *info, void *fdt)
|
||||
{
|
||||
const char *fdt_key_name;
|
||||
struct signer ctx;
|
||||
int ret;
|
||||
|
||||
fdt_key_name = info->keyname ? info->keyname : "default-key";
|
||||
ret = prepare_ctx(&ctx, info);
|
||||
if (ret >= 0)
|
||||
do_add(&ctx, fdt, fdt_key_name);
|
||||
|
||||
free_ctx(&ctx);
|
||||
return ret;
|
||||
}
|
72
lib/fdt-libcrypto.c
Normal file
72
lib/fdt-libcrypto.c
Normal file
|
@ -0,0 +1,72 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2020, Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||
* Copyright (c) 2013, Google Inc.
|
||||
*/
|
||||
|
||||
#include <libfdt.h>
|
||||
#include <u-boot/fdt-libcrypto.h>
|
||||
|
||||
int fdt_add_bignum(void *blob, int noffset, const char *prop_name,
|
||||
BIGNUM *num, int num_bits)
|
||||
{
|
||||
int nwords = num_bits / 32;
|
||||
int size;
|
||||
uint32_t *buf, *ptr;
|
||||
BIGNUM *tmp, *big2, *big32, *big2_32;
|
||||
BN_CTX *ctx;
|
||||
int ret;
|
||||
|
||||
tmp = BN_new();
|
||||
big2 = BN_new();
|
||||
big32 = BN_new();
|
||||
big2_32 = BN_new();
|
||||
|
||||
/*
|
||||
* Note: This code assumes that all of the above succeed, or all fail.
|
||||
* In practice memory allocations generally do not fail (unless the
|
||||
* process is killed), so it does not seem worth handling each of these
|
||||
* as a separate case. Technicaly this could leak memory on failure,
|
||||
* but a) it won't happen in practice, and b) it doesn't matter as we
|
||||
* will immediately exit with a failure code.
|
||||
*/
|
||||
if (!tmp || !big2 || !big32 || !big2_32) {
|
||||
fprintf(stderr, "Out of memory (bignum)\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ctx = BN_CTX_new();
|
||||
if (!ctx) {
|
||||
fprintf(stderr, "Out of memory (bignum context)\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
BN_set_word(big2, 2L);
|
||||
BN_set_word(big32, 32L);
|
||||
BN_exp(big2_32, big2, big32, ctx); /* B = 2^32 */
|
||||
|
||||
size = nwords * sizeof(uint32_t);
|
||||
buf = malloc(size);
|
||||
if (!buf) {
|
||||
fprintf(stderr, "Out of memory (%d bytes)\n", size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Write out modulus as big endian array of integers */
|
||||
for (ptr = buf + nwords - 1; ptr >= buf; ptr--) {
|
||||
BN_mod(tmp, num, big2_32, ctx); /* n = N mod B */
|
||||
*ptr = cpu_to_fdt32(BN_get_word(tmp));
|
||||
BN_rshift(num, num, 32); /* N = N/B */
|
||||
}
|
||||
|
||||
/*
|
||||
* We try signing with successively increasing size values, so this
|
||||
* might fail several times
|
||||
*/
|
||||
ret = fdt_setprop(blob, noffset, prop_name, buf, size);
|
||||
free(buf);
|
||||
BN_free(tmp);
|
||||
BN_free(big2);
|
||||
BN_free(big32);
|
||||
BN_free(big2_32);
|
||||
|
||||
return ret ? -FDT_ERR_NOSPACE : 0;
|
||||
}
|
|
@ -13,7 +13,8 @@
|
|||
#else
|
||||
#include "fdt_host.h"
|
||||
#endif
|
||||
#include <u-boot/rsa.h>
|
||||
#include <hash.h>
|
||||
#include <image.h>
|
||||
|
||||
int hash_calculate(const char *name,
|
||||
const struct image_region region[],
|
|
@ -5,6 +5,6 @@
|
|||
# (C) Copyright 2000-2007
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
|
||||
obj-$(CONFIG_$(SPL_TPL_)RSA_VERIFY) += rsa-verify.o rsa-checksum.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)RSA_VERIFY) += rsa-verify.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)RSA_VERIFY_WITH_PKEY) += rsa-keyprop.o
|
||||
obj-$(CONFIG_RSA_SOFTWARE_EXP) += rsa-mod-exp.o
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
#include <string.h>
|
||||
#include <image.h>
|
||||
#include <time.h>
|
||||
#include <u-boot/fdt-libcrypto.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
|
@ -51,19 +53,21 @@ static int rsa_err(const char *msg)
|
|||
*
|
||||
* @keydir: Directory containins the key
|
||||
* @name Name of key file (will have a .crt extension)
|
||||
* @rsap Returns RSA object, or NULL on failure
|
||||
* @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
|
||||
* @evpp Returns EVP_PKEY object, or NULL on failure
|
||||
* @return 0 if ok, -ve on error (in which case *evpp will be set to NULL)
|
||||
*/
|
||||
static int rsa_pem_get_pub_key(const char *keydir, const char *name, RSA **rsap)
|
||||
static int rsa_pem_get_pub_key(const char *keydir, const char *name, EVP_PKEY **evpp)
|
||||
{
|
||||
char path[1024];
|
||||
EVP_PKEY *key;
|
||||
EVP_PKEY *key = NULL;
|
||||
X509 *cert;
|
||||
RSA *rsa;
|
||||
FILE *f;
|
||||
int ret;
|
||||
|
||||
*rsap = NULL;
|
||||
if (!evpp)
|
||||
return -EINVAL;
|
||||
|
||||
*evpp = NULL;
|
||||
snprintf(path, sizeof(path), "%s/%s.crt", keydir, name);
|
||||
f = fopen(path, "r");
|
||||
if (!f) {
|
||||
|
@ -88,22 +92,12 @@ static int rsa_pem_get_pub_key(const char *keydir, const char *name, RSA **rsap)
|
|||
goto err_pubkey;
|
||||
}
|
||||
|
||||
/* Convert to a RSA_style key. */
|
||||
rsa = EVP_PKEY_get1_RSA(key);
|
||||
if (!rsa) {
|
||||
rsa_err("Couldn't convert to a RSA style key");
|
||||
ret = -EINVAL;
|
||||
goto err_rsa;
|
||||
}
|
||||
fclose(f);
|
||||
EVP_PKEY_free(key);
|
||||
*evpp = key;
|
||||
X509_free(cert);
|
||||
*rsap = rsa;
|
||||
|
||||
return 0;
|
||||
|
||||
err_rsa:
|
||||
EVP_PKEY_free(key);
|
||||
err_pubkey:
|
||||
X509_free(cert);
|
||||
err_cert:
|
||||
|
@ -117,19 +111,20 @@ err_cert:
|
|||
* @keydir: Key prefix
|
||||
* @name Name of key
|
||||
* @engine Engine to use
|
||||
* @rsap Returns RSA object, or NULL on failure
|
||||
* @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
|
||||
* @evpp Returns EVP_PKEY object, or NULL on failure
|
||||
* @return 0 if ok, -ve on error (in which case *evpp will be set to NULL)
|
||||
*/
|
||||
static int rsa_engine_get_pub_key(const char *keydir, const char *name,
|
||||
ENGINE *engine, RSA **rsap)
|
||||
ENGINE *engine, EVP_PKEY **evpp)
|
||||
{
|
||||
const char *engine_id;
|
||||
char key_id[1024];
|
||||
EVP_PKEY *key;
|
||||
RSA *rsa;
|
||||
int ret;
|
||||
EVP_PKEY *key = NULL;
|
||||
|
||||
*rsap = NULL;
|
||||
if (!evpp)
|
||||
return -EINVAL;
|
||||
|
||||
*evpp = NULL;
|
||||
|
||||
engine_id = ENGINE_get_id(engine);
|
||||
|
||||
|
@ -165,22 +160,9 @@ static int rsa_engine_get_pub_key(const char *keydir, const char *name,
|
|||
if (!key)
|
||||
return rsa_err("Failure loading public key from engine");
|
||||
|
||||
/* Convert to a RSA_style key. */
|
||||
rsa = EVP_PKEY_get1_RSA(key);
|
||||
if (!rsa) {
|
||||
rsa_err("Couldn't convert to a RSA style key");
|
||||
ret = -EINVAL;
|
||||
goto err_rsa;
|
||||
}
|
||||
|
||||
EVP_PKEY_free(key);
|
||||
*rsap = rsa;
|
||||
*evpp = key;
|
||||
|
||||
return 0;
|
||||
|
||||
err_rsa:
|
||||
EVP_PKEY_free(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -189,15 +171,15 @@ err_rsa:
|
|||
* @keydir: Directory containing the key (PEM file) or key prefix (engine)
|
||||
* @name Name of key file (will have a .crt extension)
|
||||
* @engine Engine to use
|
||||
* @rsap Returns RSA object, or NULL on failure
|
||||
* @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
|
||||
* @evpp Returns EVP_PKEY object, or NULL on failure
|
||||
* @return 0 if ok, -ve on error (in which case *evpp will be set to NULL)
|
||||
*/
|
||||
static int rsa_get_pub_key(const char *keydir, const char *name,
|
||||
ENGINE *engine, RSA **rsap)
|
||||
ENGINE *engine, EVP_PKEY **evpp)
|
||||
{
|
||||
if (engine)
|
||||
return rsa_engine_get_pub_key(keydir, name, engine, rsap);
|
||||
return rsa_pem_get_pub_key(keydir, name, rsap);
|
||||
return rsa_engine_get_pub_key(keydir, name, engine, evpp);
|
||||
return rsa_pem_get_pub_key(keydir, name, evpp);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,18 +187,26 @@ static int rsa_get_pub_key(const char *keydir, const char *name,
|
|||
*
|
||||
* @keydir: Directory containing the key
|
||||
* @name Name of key file (will have a .key extension)
|
||||
* @rsap Returns RSA object, or NULL on failure
|
||||
* @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
|
||||
* @evpp Returns EVP_PKEY object, or NULL on failure
|
||||
* @return 0 if ok, -ve on error (in which case *evpp will be set to NULL)
|
||||
*/
|
||||
static int rsa_pem_get_priv_key(const char *keydir, const char *name,
|
||||
RSA **rsap)
|
||||
const char *keyfile, EVP_PKEY **evpp)
|
||||
{
|
||||
char path[1024];
|
||||
RSA *rsa;
|
||||
FILE *f;
|
||||
char path[1024] = {0};
|
||||
FILE *f = NULL;
|
||||
|
||||
if (!evpp)
|
||||
return -EINVAL;
|
||||
|
||||
*evpp = NULL;
|
||||
if (keydir && name)
|
||||
snprintf(path, sizeof(path), "%s/%s.key", keydir, name);
|
||||
else if (keyfile)
|
||||
snprintf(path, sizeof(path), "%s", keyfile);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
*rsap = NULL;
|
||||
snprintf(path, sizeof(path), "%s/%s.key", keydir, name);
|
||||
f = fopen(path, "r");
|
||||
if (!f) {
|
||||
fprintf(stderr, "Couldn't open RSA private key: '%s': %s\n",
|
||||
|
@ -224,14 +214,12 @@ static int rsa_pem_get_priv_key(const char *keydir, const char *name,
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
rsa = PEM_read_RSAPrivateKey(f, 0, NULL, path);
|
||||
if (!rsa) {
|
||||
if (!PEM_read_PrivateKey(f, evpp, NULL, path)) {
|
||||
rsa_err("Failure reading private key");
|
||||
fclose(f);
|
||||
return -EPROTO;
|
||||
}
|
||||
fclose(f);
|
||||
*rsap = rsa;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -242,23 +230,27 @@ static int rsa_pem_get_priv_key(const char *keydir, const char *name,
|
|||
* @keydir: Key prefix
|
||||
* @name Name of key
|
||||
* @engine Engine to use
|
||||
* @rsap Returns RSA object, or NULL on failure
|
||||
* @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
|
||||
* @evpp Returns EVP_PKEY object, or NULL on failure
|
||||
* @return 0 if ok, -ve on error (in which case *evpp will be set to NULL)
|
||||
*/
|
||||
static int rsa_engine_get_priv_key(const char *keydir, const char *name,
|
||||
ENGINE *engine, RSA **rsap)
|
||||
const char *keyfile,
|
||||
ENGINE *engine, EVP_PKEY **evpp)
|
||||
{
|
||||
const char *engine_id;
|
||||
char key_id[1024];
|
||||
EVP_PKEY *key;
|
||||
RSA *rsa;
|
||||
int ret;
|
||||
EVP_PKEY *key = NULL;
|
||||
|
||||
*rsap = NULL;
|
||||
if (!evpp)
|
||||
return -EINVAL;
|
||||
|
||||
engine_id = ENGINE_get_id(engine);
|
||||
|
||||
if (engine_id && !strcmp(engine_id, "pkcs11")) {
|
||||
if (!keydir && !name) {
|
||||
fprintf(stderr, "Please use 'keydir' with PKCS11\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (keydir)
|
||||
if (strstr(keydir, "object="))
|
||||
snprintf(key_id, sizeof(key_id),
|
||||
|
@ -273,14 +265,19 @@ static int rsa_engine_get_priv_key(const char *keydir, const char *name,
|
|||
"pkcs11:object=%s;type=private",
|
||||
name);
|
||||
} else if (engine_id) {
|
||||
if (keydir)
|
||||
if (keydir && name)
|
||||
snprintf(key_id, sizeof(key_id),
|
||||
"%s%s",
|
||||
keydir, name);
|
||||
else
|
||||
else if (keydir)
|
||||
snprintf(key_id, sizeof(key_id),
|
||||
"%s",
|
||||
name);
|
||||
else if (keyfile)
|
||||
snprintf(key_id, sizeof(key_id), "%s", keyfile);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "Engine not supported\n");
|
||||
return -ENOTSUP;
|
||||
|
@ -290,22 +287,9 @@ static int rsa_engine_get_priv_key(const char *keydir, const char *name,
|
|||
if (!key)
|
||||
return rsa_err("Failure loading private key from engine");
|
||||
|
||||
/* Convert to a RSA_style key. */
|
||||
rsa = EVP_PKEY_get1_RSA(key);
|
||||
if (!rsa) {
|
||||
rsa_err("Couldn't convert to a RSA style key");
|
||||
ret = -EINVAL;
|
||||
goto err_rsa;
|
||||
}
|
||||
|
||||
EVP_PKEY_free(key);
|
||||
*rsap = rsa;
|
||||
*evpp = key;
|
||||
|
||||
return 0;
|
||||
|
||||
err_rsa:
|
||||
EVP_PKEY_free(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -314,15 +298,16 @@ err_rsa:
|
|||
* @keydir: Directory containing the key (PEM file) or key prefix (engine)
|
||||
* @name Name of key
|
||||
* @engine Engine to use for signing
|
||||
* @rsap Returns RSA object, or NULL on failure
|
||||
* @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
|
||||
* @evpp Returns EVP_PKEY object, or NULL on failure
|
||||
* @return 0 if ok, -ve on error (in which case *evpp will be set to NULL)
|
||||
*/
|
||||
static int rsa_get_priv_key(const char *keydir, const char *name,
|
||||
ENGINE *engine, RSA **rsap)
|
||||
const char *keyfile, ENGINE *engine, EVP_PKEY **evpp)
|
||||
{
|
||||
if (engine)
|
||||
return rsa_engine_get_priv_key(keydir, name, engine, rsap);
|
||||
return rsa_pem_get_priv_key(keydir, name, rsap);
|
||||
return rsa_engine_get_priv_key(keydir, name, keyfile, engine,
|
||||
evpp);
|
||||
return rsa_pem_get_priv_key(keydir, name, keyfile, evpp);
|
||||
}
|
||||
|
||||
static int rsa_init(void)
|
||||
|
@ -416,12 +401,11 @@ static void rsa_engine_remove(ENGINE *e)
|
|||
}
|
||||
}
|
||||
|
||||
static int rsa_sign_with_key(RSA *rsa, struct padding_algo *padding_algo,
|
||||
static int rsa_sign_with_key(EVP_PKEY *pkey, struct padding_algo *padding_algo,
|
||||
struct checksum_algo *checksum_algo,
|
||||
const struct image_region region[], int region_count,
|
||||
uint8_t **sigp, uint *sig_size)
|
||||
{
|
||||
EVP_PKEY *key;
|
||||
EVP_PKEY_CTX *ckey;
|
||||
EVP_MD_CTX *context;
|
||||
int ret = 0;
|
||||
|
@ -429,16 +413,7 @@ static int rsa_sign_with_key(RSA *rsa, struct padding_algo *padding_algo,
|
|||
uint8_t *sig;
|
||||
int i;
|
||||
|
||||
key = EVP_PKEY_new();
|
||||
if (!key)
|
||||
return rsa_err("EVP_PKEY object creation failed");
|
||||
|
||||
if (!EVP_PKEY_set1_RSA(key, rsa)) {
|
||||
ret = rsa_err("EVP key setup failed");
|
||||
goto err_set;
|
||||
}
|
||||
|
||||
size = EVP_PKEY_size(key);
|
||||
size = EVP_PKEY_size(pkey);
|
||||
sig = malloc(size);
|
||||
if (!sig) {
|
||||
fprintf(stderr, "Out of memory for signature (%zu bytes)\n",
|
||||
|
@ -454,7 +429,7 @@ static int rsa_sign_with_key(RSA *rsa, struct padding_algo *padding_algo,
|
|||
}
|
||||
EVP_MD_CTX_init(context);
|
||||
|
||||
ckey = EVP_PKEY_CTX_new(key, NULL);
|
||||
ckey = EVP_PKEY_CTX_new(pkey, NULL);
|
||||
if (!ckey) {
|
||||
ret = rsa_err("EVP key context creation failed");
|
||||
goto err_create;
|
||||
|
@ -462,7 +437,7 @@ static int rsa_sign_with_key(RSA *rsa, struct padding_algo *padding_algo,
|
|||
|
||||
if (EVP_DigestSignInit(context, &ckey,
|
||||
checksum_algo->calculate_sign(),
|
||||
NULL, key) <= 0) {
|
||||
NULL, pkey) <= 0) {
|
||||
ret = rsa_err("Signer setup failed");
|
||||
goto err_sign;
|
||||
}
|
||||
|
@ -497,7 +472,6 @@ static int rsa_sign_with_key(RSA *rsa, struct padding_algo *padding_algo,
|
|||
EVP_MD_CTX_reset(context);
|
||||
#endif
|
||||
EVP_MD_CTX_destroy(context);
|
||||
EVP_PKEY_free(key);
|
||||
|
||||
debug("Got signature: %d bytes, expected %zu\n", *sig_size, size);
|
||||
*sigp = sig;
|
||||
|
@ -510,8 +484,6 @@ err_sign:
|
|||
err_create:
|
||||
free(sig);
|
||||
err_alloc:
|
||||
err_set:
|
||||
EVP_PKEY_free(key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -519,7 +491,7 @@ int rsa_sign(struct image_sign_info *info,
|
|||
const struct image_region region[], int region_count,
|
||||
uint8_t **sigp, uint *sig_len)
|
||||
{
|
||||
RSA *rsa;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
ENGINE *e = NULL;
|
||||
int ret;
|
||||
|
||||
|
@ -533,15 +505,16 @@ int rsa_sign(struct image_sign_info *info,
|
|||
goto err_engine;
|
||||
}
|
||||
|
||||
ret = rsa_get_priv_key(info->keydir, info->keyname, e, &rsa);
|
||||
ret = rsa_get_priv_key(info->keydir, info->keyname, info->keyfile,
|
||||
e, &pkey);
|
||||
if (ret)
|
||||
goto err_priv;
|
||||
ret = rsa_sign_with_key(rsa, info->padding, info->checksum, region,
|
||||
ret = rsa_sign_with_key(pkey, info->padding, info->checksum, region,
|
||||
region_count, sigp, sig_len);
|
||||
if (ret)
|
||||
goto err_sign;
|
||||
|
||||
RSA_free(rsa);
|
||||
EVP_PKEY_free(pkey);
|
||||
if (info->engine_id)
|
||||
rsa_engine_remove(e);
|
||||
rsa_remove();
|
||||
|
@ -549,7 +522,7 @@ int rsa_sign(struct image_sign_info *info,
|
|||
return ret;
|
||||
|
||||
err_sign:
|
||||
RSA_free(rsa);
|
||||
EVP_PKEY_free(pkey);
|
||||
err_priv:
|
||||
if (info->engine_id)
|
||||
rsa_engine_remove(e);
|
||||
|
@ -680,70 +653,6 @@ int rsa_get_params(RSA *key, uint64_t *exponent, uint32_t *n0_invp,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int fdt_add_bignum(void *blob, int noffset, const char *prop_name,
|
||||
BIGNUM *num, int num_bits)
|
||||
{
|
||||
int nwords = num_bits / 32;
|
||||
int size;
|
||||
uint32_t *buf, *ptr;
|
||||
BIGNUM *tmp, *big2, *big32, *big2_32;
|
||||
BN_CTX *ctx;
|
||||
int ret;
|
||||
|
||||
tmp = BN_new();
|
||||
big2 = BN_new();
|
||||
big32 = BN_new();
|
||||
big2_32 = BN_new();
|
||||
|
||||
/*
|
||||
* Note: This code assumes that all of the above succeed, or all fail.
|
||||
* In practice memory allocations generally do not fail (unless the
|
||||
* process is killed), so it does not seem worth handling each of these
|
||||
* as a separate case. Technicaly this could leak memory on failure,
|
||||
* but a) it won't happen in practice, and b) it doesn't matter as we
|
||||
* will immediately exit with a failure code.
|
||||
*/
|
||||
if (!tmp || !big2 || !big32 || !big2_32) {
|
||||
fprintf(stderr, "Out of memory (bignum)\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ctx = BN_CTX_new();
|
||||
if (!ctx) {
|
||||
fprintf(stderr, "Out of memory (bignum context)\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
BN_set_word(big2, 2L);
|
||||
BN_set_word(big32, 32L);
|
||||
BN_exp(big2_32, big2, big32, ctx); /* B = 2^32 */
|
||||
|
||||
size = nwords * sizeof(uint32_t);
|
||||
buf = malloc(size);
|
||||
if (!buf) {
|
||||
fprintf(stderr, "Out of memory (%d bytes)\n", size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Write out modulus as big endian array of integers */
|
||||
for (ptr = buf + nwords - 1; ptr >= buf; ptr--) {
|
||||
BN_mod(tmp, num, big2_32, ctx); /* n = N mod B */
|
||||
*ptr = cpu_to_fdt32(BN_get_word(tmp));
|
||||
BN_rshift(num, num, 32); /* N = N/B */
|
||||
}
|
||||
|
||||
/*
|
||||
* We try signing with successively increasing size values, so this
|
||||
* might fail several times
|
||||
*/
|
||||
ret = fdt_setprop(blob, noffset, prop_name, buf, size);
|
||||
free(buf);
|
||||
BN_free(tmp);
|
||||
BN_free(big2);
|
||||
BN_free(big32);
|
||||
BN_free(big2_32);
|
||||
|
||||
return ret ? -FDT_ERR_NOSPACE : 0;
|
||||
}
|
||||
|
||||
int rsa_add_verify_data(struct image_sign_info *info, void *keydest)
|
||||
{
|
||||
BIGNUM *modulus, *r_squared;
|
||||
|
@ -754,6 +663,7 @@ int rsa_add_verify_data(struct image_sign_info *info, void *keydest)
|
|||
int ret;
|
||||
int bits;
|
||||
RSA *rsa;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
ENGINE *e = NULL;
|
||||
|
||||
debug("%s: Getting verification data\n", __func__);
|
||||
|
@ -762,9 +672,15 @@ int rsa_add_verify_data(struct image_sign_info *info, void *keydest)
|
|||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = rsa_get_pub_key(info->keydir, info->keyname, e, &rsa);
|
||||
ret = rsa_get_pub_key(info->keydir, info->keyname, e, &pkey);
|
||||
if (ret)
|
||||
goto err_get_pub_key;
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
|
||||
(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x02070000fL)
|
||||
rsa = EVP_PKEY_get1_RSA(pkey);
|
||||
#else
|
||||
rsa = EVP_PKEY_get0_RSA(pkey);
|
||||
#endif
|
||||
ret = rsa_get_params(rsa, &exponent, &n0_inv, &modulus, &r_squared);
|
||||
if (ret)
|
||||
goto err_get_params;
|
||||
|
@ -834,7 +750,11 @@ done:
|
|||
if (ret)
|
||||
ret = ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO;
|
||||
err_get_params:
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
|
||||
(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x02070000fL)
|
||||
RSA_free(rsa);
|
||||
#endif
|
||||
EVP_PKEY_free(pkey);
|
||||
err_get_pub_key:
|
||||
if (info->engine_id)
|
||||
rsa_engine_remove(e);
|
||||
|
|
|
@ -10,6 +10,7 @@ packaging==19.2
|
|||
pbr==5.4.3
|
||||
pluggy==0.13.0
|
||||
py==1.8.0
|
||||
pycryptodomex==3.9.8
|
||||
pyelftools==0.27
|
||||
pygit2==0.28.2
|
||||
pyparsing==2.4.2
|
||||
|
|
111
test/py/tests/test_fit_ecdsa.py
Normal file
111
test/py/tests/test_fit_ecdsa.py
Normal file
|
@ -0,0 +1,111 @@
|
|||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Copyright (c) 2020,2021 Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
||||
|
||||
"""
|
||||
Test ECDSA signing of FIT images
|
||||
|
||||
This test uses mkimage to sign an existing FIT image with an ECDSA key. The
|
||||
signature is then extracted, and verified against pyCryptodome.
|
||||
This test doesn't run the sandbox. It only checks the host tool 'mkimage'
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import u_boot_utils as util
|
||||
from Cryptodome.Hash import SHA256
|
||||
from Cryptodome.PublicKey import ECC
|
||||
from Cryptodome.Signature import DSS
|
||||
|
||||
class SignableFitImage(object):
|
||||
""" Helper to manipulate a FIT image on disk """
|
||||
def __init__(self, cons, file_name):
|
||||
self.fit = file_name
|
||||
self.cons = cons
|
||||
self.signable_nodes = set()
|
||||
|
||||
def __fdt_list(self, path):
|
||||
return util.run_and_log(self.cons, f'fdtget -l {self.fit} {path}')
|
||||
|
||||
def __fdt_set(self, node, **prop_value):
|
||||
for prop, value in prop_value.items():
|
||||
util.run_and_log(self.cons, f'fdtput -ts {self.fit} {node} {prop} {value}')
|
||||
|
||||
def __fdt_get_binary(self, node, prop):
|
||||
numbers = util.run_and_log(self.cons, f'fdtget -tbi {self.fit} {node} {prop}')
|
||||
|
||||
bignum = bytearray()
|
||||
for little_num in numbers.split():
|
||||
bignum.append(int(little_num))
|
||||
|
||||
return bignum
|
||||
|
||||
def find_signable_image_nodes(self):
|
||||
for node in self.__fdt_list('/images').split():
|
||||
image = f'/images/{node}'
|
||||
if 'signature' in self.__fdt_list(image):
|
||||
self.signable_nodes.add(image)
|
||||
|
||||
return self.signable_nodes
|
||||
|
||||
def change_signature_algo_to_ecdsa(self):
|
||||
for image in self.signable_nodes:
|
||||
self.__fdt_set(f'{image}/signature', algo='sha256,ecdsa256')
|
||||
|
||||
def sign(self, mkimage, key_file):
|
||||
util.run_and_log(self.cons, [mkimage, '-F', self.fit, f'-G{key_file}'])
|
||||
|
||||
def check_signatures(self, key):
|
||||
for image in self.signable_nodes:
|
||||
raw_sig = self.__fdt_get_binary(f'{image}/signature', 'value')
|
||||
raw_bin = self.__fdt_get_binary(image, 'data')
|
||||
|
||||
sha = SHA256.new(raw_bin)
|
||||
verifier = DSS.new(key, 'fips-186-3')
|
||||
verifier.verify(sha, bytes(raw_sig))
|
||||
|
||||
|
||||
@pytest.mark.buildconfigspec('fit_signature')
|
||||
@pytest.mark.requiredtool('dtc')
|
||||
@pytest.mark.requiredtool('fdtget')
|
||||
@pytest.mark.requiredtool('fdtput')
|
||||
def test_fit_ecdsa(u_boot_console):
|
||||
""" Test that signatures generated by mkimage are legible. """
|
||||
def generate_ecdsa_key():
|
||||
return ECC.generate(curve='prime256v1')
|
||||
|
||||
def assemble_fit_image(dest_fit, its, destdir):
|
||||
dtc_args = f'-I dts -O dtb -i {destdir}'
|
||||
util.run_and_log(cons, [mkimage, '-D', dtc_args, '-f', its, dest_fit])
|
||||
|
||||
def dtc(dts):
|
||||
dtb = dts.replace('.dts', '.dtb')
|
||||
util.run_and_log(cons, f'dtc {datadir}/{dts} -O dtb -o {tempdir}/{dtb}')
|
||||
|
||||
cons = u_boot_console
|
||||
mkimage = cons.config.build_dir + '/tools/mkimage'
|
||||
datadir = cons.config.source_dir + '/test/py/tests/vboot/'
|
||||
tempdir = cons.config.result_dir
|
||||
key_file = f'{tempdir}/ecdsa-test-key.pem'
|
||||
fit_file = f'{tempdir}/test.fit'
|
||||
dtc('sandbox-kernel.dts')
|
||||
|
||||
key = generate_ecdsa_key()
|
||||
|
||||
# Create a fake kernel image -- zeroes will do just fine
|
||||
with open(f'{tempdir}/test-kernel.bin', 'w') as fd:
|
||||
fd.write(500 * chr(0))
|
||||
|
||||
# invocations of mkimage expect to read the key from disk
|
||||
with open(key_file, 'w') as f:
|
||||
f.write(key.export_key(format='PEM'))
|
||||
|
||||
assemble_fit_image(fit_file, f'{datadir}/sign-images-sha256.its', tempdir)
|
||||
|
||||
fit = SignableFitImage(cons, fit_file)
|
||||
nodes = fit.find_signable_image_nodes()
|
||||
if len(nodes) == 0:
|
||||
raise ValueError('FIT image has no "/image" nodes with "signature"')
|
||||
|
||||
fit.change_signature_algo_to_ecdsa()
|
||||
fit.sign(mkimage, key_file)
|
||||
fit.check_signatures(key)
|
|
@ -67,12 +67,18 @@ LIBFDT_OBJS := $(addprefix libfdt/, fdt.o fdt_ro.o fdt_wip.o fdt_sw.o fdt_rw.o \
|
|||
fdt_strerror.o fdt_empty_tree.o fdt_addresses.o fdt_overlay.o)
|
||||
|
||||
RSA_OBJS-$(CONFIG_FIT_SIGNATURE) := $(addprefix lib/rsa/, \
|
||||
rsa-sign.o rsa-verify.o rsa-checksum.o \
|
||||
rsa-sign.o rsa-verify.o \
|
||||
rsa-mod-exp.o)
|
||||
|
||||
ECDSA_OBJS-$(CONFIG_FIT_SIGNATURE) := $(addprefix lib/ecdsa/, ecdsa-libcrypto.o)
|
||||
|
||||
AES_OBJS-$(CONFIG_FIT_CIPHER) := $(addprefix lib/aes/, \
|
||||
aes-encrypt.o aes-decrypt.o)
|
||||
|
||||
# Cryptographic helpers that depend on openssl/libcrypto
|
||||
LIBCRYPTO_OBJS-$(CONFIG_FIT_SIGNATURE) := $(addprefix lib/, \
|
||||
fdt-libcrypto.o)
|
||||
|
||||
ROCKCHIP_OBS = lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o
|
||||
|
||||
# common objs for dumpimage and mkimage
|
||||
|
@ -106,6 +112,7 @@ dumpimage-mkimage-objs := aisimage.o \
|
|||
socfpgaimage.o \
|
||||
sunxi_egon.o \
|
||||
lib/crc16.o \
|
||||
lib/hash-checksum.o \
|
||||
lib/sha1.o \
|
||||
lib/sha256.o \
|
||||
lib/sha512.o \
|
||||
|
@ -114,10 +121,12 @@ dumpimage-mkimage-objs := aisimage.o \
|
|||
zynqimage.o \
|
||||
zynqmpimage.o \
|
||||
zynqmpbif.o \
|
||||
$(LIBCRYPTO_OBJS-y) \
|
||||
$(LIBFDT_OBJS) \
|
||||
gpimage.o \
|
||||
gpimage-common.o \
|
||||
mtk_image.o \
|
||||
$(ECDSA_OBJS-y) \
|
||||
$(RSA_OBJS-y) \
|
||||
$(AES_OBJS-y)
|
||||
|
||||
|
|
|
@ -68,7 +68,8 @@ static int fit_add_file_data(struct image_tool_params *params, size_t size_inc,
|
|||
}
|
||||
|
||||
if (!ret) {
|
||||
ret = fit_add_verification_data(params->keydir, dest_blob, ptr,
|
||||
ret = fit_add_verification_data(params->keydir,
|
||||
params->keyfile, dest_blob, ptr,
|
||||
params->comment,
|
||||
params->require_keys,
|
||||
params->engine_id,
|
||||
|
|
|
@ -153,8 +153,9 @@ static int fit_image_write_sig(void *fit, int noffset, uint8_t *value,
|
|||
}
|
||||
|
||||
static int fit_image_setup_sig(struct image_sign_info *info,
|
||||
const char *keydir, void *fit, const char *image_name,
|
||||
int noffset, const char *require_keys, const char *engine_id)
|
||||
const char *keydir, const char *keyfile, void *fit,
|
||||
const char *image_name, int noffset, const char *require_keys,
|
||||
const char *engine_id)
|
||||
{
|
||||
const char *node_name;
|
||||
char *algo_name;
|
||||
|
@ -171,6 +172,7 @@ static int fit_image_setup_sig(struct image_sign_info *info,
|
|||
|
||||
memset(info, '\0', sizeof(*info));
|
||||
info->keydir = keydir;
|
||||
info->keyfile = keyfile;
|
||||
info->keyname = fdt_getprop(fit, noffset, FIT_KEY_HINT, NULL);
|
||||
info->fit = fit;
|
||||
info->node_offset = noffset;
|
||||
|
@ -207,8 +209,8 @@ static int fit_image_setup_sig(struct image_sign_info *info,
|
|||
* @engine_id: Engine to use for signing
|
||||
* @return 0 if ok, -1 on error
|
||||
*/
|
||||
static int fit_image_process_sig(const char *keydir, void *keydest,
|
||||
void *fit, const char *image_name,
|
||||
static int fit_image_process_sig(const char *keydir, const char *keyfile,
|
||||
void *keydest, void *fit, const char *image_name,
|
||||
int noffset, const void *data, size_t size,
|
||||
const char *comment, int require_keys, const char *engine_id,
|
||||
const char *cmdname)
|
||||
|
@ -220,8 +222,9 @@ static int fit_image_process_sig(const char *keydir, void *keydest,
|
|||
uint value_len;
|
||||
int ret;
|
||||
|
||||
if (fit_image_setup_sig(&info, keydir, fit, image_name, noffset,
|
||||
require_keys ? "image" : NULL, engine_id))
|
||||
if (fit_image_setup_sig(&info, keydir, keyfile, fit, image_name,
|
||||
noffset, require_keys ? "image" : NULL,
|
||||
engine_id))
|
||||
return -1;
|
||||
|
||||
node_name = fit_get_name(fit, noffset, NULL);
|
||||
|
@ -598,9 +601,10 @@ int fit_image_cipher_data(const char *keydir, void *keydest,
|
|||
* @engine_id: Engine to use for signing
|
||||
* @return: 0 on success, <0 on failure
|
||||
*/
|
||||
int fit_image_add_verification_data(const char *keydir, void *keydest,
|
||||
void *fit, int image_noffset, const char *comment,
|
||||
int require_keys, const char *engine_id, const char *cmdname)
|
||||
int fit_image_add_verification_data(const char *keydir, const char *keyfile,
|
||||
void *keydest, void *fit, int image_noffset,
|
||||
const char *comment, int require_keys, const char *engine_id,
|
||||
const char *cmdname)
|
||||
{
|
||||
const char *image_name;
|
||||
const void *data;
|
||||
|
@ -632,10 +636,10 @@ int fit_image_add_verification_data(const char *keydir, void *keydest,
|
|||
strlen(FIT_HASH_NODENAME))) {
|
||||
ret = fit_image_process_hash(fit, image_name, noffset,
|
||||
data, size);
|
||||
} else if (IMAGE_ENABLE_SIGN && keydir &&
|
||||
} else if (IMAGE_ENABLE_SIGN && (keydir || keyfile) &&
|
||||
!strncmp(node_name, FIT_SIG_NODENAME,
|
||||
strlen(FIT_SIG_NODENAME))) {
|
||||
ret = fit_image_process_sig(keydir, keydest,
|
||||
ret = fit_image_process_sig(keydir, keyfile, keydest,
|
||||
fit, image_name, noffset, data, size,
|
||||
comment, require_keys, engine_id, cmdname);
|
||||
}
|
||||
|
@ -918,10 +922,10 @@ static int fit_config_get_data(void *fit, int conf_noffset, int noffset,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fit_config_process_sig(const char *keydir, void *keydest,
|
||||
void *fit, const char *conf_name, int conf_noffset,
|
||||
int noffset, const char *comment, int require_keys,
|
||||
const char *engine_id, const char *cmdname)
|
||||
static int fit_config_process_sig(const char *keydir, const char *keyfile,
|
||||
void *keydest, void *fit, const char *conf_name,
|
||||
int conf_noffset, int noffset, const char *comment,
|
||||
int require_keys, const char *engine_id, const char *cmdname)
|
||||
{
|
||||
struct image_sign_info info;
|
||||
const char *node_name;
|
||||
|
@ -938,7 +942,7 @@ static int fit_config_process_sig(const char *keydir, void *keydest,
|
|||
®ion_count, ®ion_prop, ®ion_proplen))
|
||||
return -1;
|
||||
|
||||
if (fit_image_setup_sig(&info, keydir, fit, conf_name, noffset,
|
||||
if (fit_image_setup_sig(&info, keydir, keyfile, fit, conf_name, noffset,
|
||||
require_keys ? "conf" : NULL, engine_id))
|
||||
return -1;
|
||||
|
||||
|
@ -983,9 +987,10 @@ static int fit_config_process_sig(const char *keydir, void *keydest,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int fit_config_add_verification_data(const char *keydir, void *keydest,
|
||||
void *fit, int conf_noffset, const char *comment,
|
||||
int require_keys, const char *engine_id, const char *cmdname)
|
||||
static int fit_config_add_verification_data(const char *keydir,
|
||||
const char *keyfile, void *keydest, void *fit, int conf_noffset,
|
||||
const char *comment, int require_keys, const char *engine_id,
|
||||
const char *cmdname)
|
||||
{
|
||||
const char *conf_name;
|
||||
int noffset;
|
||||
|
@ -1002,7 +1007,7 @@ static int fit_config_add_verification_data(const char *keydir, void *keydest,
|
|||
node_name = fit_get_name(fit, noffset, NULL);
|
||||
if (!strncmp(node_name, FIT_SIG_NODENAME,
|
||||
strlen(FIT_SIG_NODENAME))) {
|
||||
ret = fit_config_process_sig(keydir, keydest,
|
||||
ret = fit_config_process_sig(keydir, keyfile, keydest,
|
||||
fit, conf_name, conf_noffset, noffset, comment,
|
||||
require_keys, engine_id, cmdname);
|
||||
}
|
||||
|
@ -1048,9 +1053,10 @@ int fit_cipher_data(const char *keydir, void *keydest, void *fit,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int fit_add_verification_data(const char *keydir, void *keydest, void *fit,
|
||||
const char *comment, int require_keys,
|
||||
const char *engine_id, const char *cmdname)
|
||||
int fit_add_verification_data(const char *keydir, const char *keyfile,
|
||||
void *keydest, void *fit, const char *comment,
|
||||
int require_keys, const char *engine_id,
|
||||
const char *cmdname)
|
||||
{
|
||||
int images_noffset, confs_noffset;
|
||||
int noffset;
|
||||
|
@ -1072,7 +1078,7 @@ int fit_add_verification_data(const char *keydir, void *keydest, void *fit,
|
|||
* Direct child node of the images parent node,
|
||||
* i.e. component image node.
|
||||
*/
|
||||
ret = fit_image_add_verification_data(keydir, keydest,
|
||||
ret = fit_image_add_verification_data(keydir, keyfile, keydest,
|
||||
fit, noffset, comment, require_keys, engine_id,
|
||||
cmdname);
|
||||
if (ret)
|
||||
|
@ -1080,7 +1086,7 @@ int fit_add_verification_data(const char *keydir, void *keydest, void *fit,
|
|||
}
|
||||
|
||||
/* If there are no keys, we can't sign configurations */
|
||||
if (!IMAGE_ENABLE_SIGN || !keydir)
|
||||
if (!IMAGE_ENABLE_SIGN || !(keydir || keyfile))
|
||||
return 0;
|
||||
|
||||
/* Find configurations parent node offset */
|
||||
|
@ -1095,7 +1101,7 @@ int fit_add_verification_data(const char *keydir, void *keydest, void *fit,
|
|||
for (noffset = fdt_first_subnode(fit, confs_noffset);
|
||||
noffset >= 0;
|
||||
noffset = fdt_next_subnode(fit, noffset)) {
|
||||
ret = fit_config_add_verification_data(keydir, keydest,
|
||||
ret = fit_config_add_verification_data(keydir, keyfile, keydest,
|
||||
fit, noffset, comment,
|
||||
require_keys,
|
||||
engine_id, cmdname);
|
||||
|
|
|
@ -67,6 +67,7 @@ struct image_tool_params {
|
|||
const char *outfile; /* Output filename */
|
||||
const char *keydir; /* Directory holding private keys */
|
||||
const char *keydest; /* Destination .dtb for public key */
|
||||
const char *keyfile; /* Filename of private or public key */
|
||||
const char *comment; /* Comment to add to signature node */
|
||||
int require_keys; /* 1 to mark signing keys as 'required' */
|
||||
int file_size; /* Total size of output file */
|
||||
|
|
|
@ -108,6 +108,7 @@ static void usage(const char *msg)
|
|||
"Signing / verified boot options: [-k keydir] [-K dtb] [ -c <comment>] [-p addr] [-r] [-N engine]\n"
|
||||
" -k => set directory containing private keys\n"
|
||||
" -K => write public keys to this .dtb file\n"
|
||||
" -G => use this signing key (in lieu of -k)\n"
|
||||
" -c => add comment in signature node\n"
|
||||
" -F => re-sign existing FIT image\n"
|
||||
" -p => place external data at a static position\n"
|
||||
|
@ -151,7 +152,7 @@ static void process_args(int argc, char **argv)
|
|||
int opt;
|
||||
|
||||
while ((opt = getopt(argc, argv,
|
||||
"a:A:b:B:c:C:d:D:e:Ef:Fk:i:K:ln:N:p:O:rR:qstT:vVx")) != -1) {
|
||||
"a:A:b:B:c:C:d:D:e:Ef:FG:k:i:K:ln:N:p:O:rR:qstT:vVx")) != -1) {
|
||||
switch (opt) {
|
||||
case 'a':
|
||||
params.addr = strtoull(optarg, &ptr, 16);
|
||||
|
@ -226,6 +227,9 @@ static void process_args(int argc, char **argv)
|
|||
params.type = IH_TYPE_FLATDT;
|
||||
params.fflag = 1;
|
||||
break;
|
||||
case 'G':
|
||||
params.keyfile = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
params.fit_ramdisk = optarg;
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue