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:
Tom Rini 2021-04-15 17:10:25 -04:00
commit a6232e065d
31 changed files with 955 additions and 266 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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[] = {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

@ -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,
&region_count, &region_prop, &region_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);

View File

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

View 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;