From 0bcb28dfb946b32ed7550fc4c24c5dcea6718554 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Fri, 19 Feb 2021 12:45:10 -0600 Subject: [PATCH 01/26] lib: Rename rsa-checksum.c to hash-checksum.c rsa-checksum.c sontains the hash_calculate() implementations. Despite the "rsa-" file prefix, this function is useful for other algorithms. To prevent confusion, move this file to lib/, and rename it to hash-checksum.c, to give it a more "generic" feel. Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- common/image-fit-sig.c | 2 +- common/image-sig.c | 2 +- include/image.h | 2 +- include/u-boot/{rsa-checksum.h => hash-checksum.h} | 0 lib/Makefile | 1 + lib/crypto/pkcs7_verify.c | 2 +- lib/crypto/x509_public_key.c | 2 +- lib/{rsa/rsa-checksum.c => hash-checksum.c} | 3 ++- lib/rsa/Makefile | 2 +- tools/Makefile | 3 ++- 10 files changed, 11 insertions(+), 8 deletions(-) rename include/u-boot/{rsa-checksum.h => hash-checksum.h} (100%) rename lib/{rsa/rsa-checksum.c => hash-checksum.c} (96%) diff --git a/common/image-fit-sig.c b/common/image-fit-sig.c index 34ebb8edfe..55ddf1879e 100644 --- a/common/image-fit-sig.c +++ b/common/image-fit-sig.c @@ -16,7 +16,7 @@ DECLARE_GLOBAL_DATA_PTR; #include #include #include -#include +#include #define IMAGE_MAX_HASHED_NODES 100 diff --git a/common/image-sig.c b/common/image-sig.c index 4abd3c080f..54f0eb2019 100644 --- a/common/image-sig.c +++ b/common/image-sig.c @@ -17,7 +17,7 @@ DECLARE_GLOBAL_DATA_PTR; #endif /* !USE_HOSTCC*/ #include #include -#include +#include #define IMAGE_MAX_HASHED_NODES 100 diff --git a/include/image.h b/include/image.h index aeb0d37ac0..375fb46bf9 100644 --- a/include/image.h +++ b/include/image.h @@ -1283,7 +1283,7 @@ struct image_region { }; #if IMAGE_ENABLE_VERIFY -# include +# include #endif struct checksum_algo { const char *name; diff --git a/include/u-boot/rsa-checksum.h b/include/u-boot/hash-checksum.h similarity index 100% rename from include/u-boot/rsa-checksum.h rename to include/u-boot/hash-checksum.h diff --git a/lib/Makefile b/lib/Makefile index c42d4e1233..6825671955 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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 diff --git a/lib/crypto/pkcs7_verify.c b/lib/crypto/pkcs7_verify.c index 58683ef614..82c5c745d4 100644 --- a/lib/crypto/pkcs7_verify.c +++ b/lib/crypto/pkcs7_verify.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #else diff --git a/lib/crypto/x509_public_key.c b/lib/crypto/x509_public_key.c index 91810a8640..d557ab27ae 100644 --- a/lib/crypto/x509_public_key.c +++ b/lib/crypto/x509_public_key.c @@ -19,7 +19,7 @@ #include #ifdef __UBOOT__ #include -#include +#include #else #include #include diff --git a/lib/rsa/rsa-checksum.c b/lib/hash-checksum.c similarity index 96% rename from lib/rsa/rsa-checksum.c rename to lib/hash-checksum.c index e60debb7df..d732ecc38f 100644 --- a/lib/rsa/rsa-checksum.c +++ b/lib/hash-checksum.c @@ -13,7 +13,8 @@ #else #include "fdt_host.h" #endif -#include +#include +#include int hash_calculate(const char *name, const struct image_region region[], diff --git a/lib/rsa/Makefile b/lib/rsa/Makefile index 8b75d41f04..c9ac72c1e2 100644 --- a/lib/rsa/Makefile +++ b/lib/rsa/Makefile @@ -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 diff --git a/tools/Makefile b/tools/Makefile index 62de7e6fe0..e968b20fbd 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -67,7 +67,7 @@ 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) AES_OBJS-$(CONFIG_FIT_CIPHER) := $(addprefix lib/aes/, \ @@ -106,6 +106,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 \ From 4c17e5f69170bf033df7b4f1a2b87fa72f18aaf5 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Fri, 19 Feb 2021 12:45:11 -0600 Subject: [PATCH 02/26] lib/rsa: Make fdt_add_bignum() available outside of RSA code fdt_add_bignum() is useful for algorithms other than just RSA. To allow its use for ECDSA, move it to a common file under lib/. The new file is suffixed with '-libcrypto' because it has a direct dependency on openssl. This is due to the use of the "BIGNUM *" type. Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- include/u-boot/fdt-libcrypto.h | 27 +++++++++++++ lib/fdt-libcrypto.c | 72 ++++++++++++++++++++++++++++++++++ lib/rsa/rsa-sign.c | 65 +----------------------------- tools/Makefile | 5 +++ 4 files changed, 105 insertions(+), 64 deletions(-) create mode 100644 include/u-boot/fdt-libcrypto.h create mode 100644 lib/fdt-libcrypto.c diff --git a/include/u-boot/fdt-libcrypto.h b/include/u-boot/fdt-libcrypto.h new file mode 100644 index 0000000000..5142f37039 --- /dev/null +++ b/include/u-boot/fdt-libcrypto.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2020, Alexandru Gagniuc + * Copyright (c) 2013, Google Inc. + */ + +#ifndef _FDT_LIBCRYPTO_H +#define _FDT_LIBCRYPTO_H + +#include + +/** + * 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 */ diff --git a/lib/fdt-libcrypto.c b/lib/fdt-libcrypto.c new file mode 100644 index 0000000000..ecb0344c8f --- /dev/null +++ b/lib/fdt-libcrypto.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2020, Alexandru Gagniuc + * Copyright (c) 2013, Google Inc. + */ + +#include +#include + +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; +} diff --git a/lib/rsa/rsa-sign.c b/lib/rsa/rsa-sign.c index 1f0d81bd7a..557c690a6d 100644 --- a/lib/rsa/rsa-sign.c +++ b/lib/rsa/rsa-sign.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -680,70 +681,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; diff --git a/tools/Makefile b/tools/Makefile index e968b20fbd..3b8e7ac73a 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -73,6 +73,10 @@ RSA_OBJS-$(CONFIG_FIT_SIGNATURE) := $(addprefix lib/rsa/, \ 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 @@ -115,6 +119,7 @@ dumpimage-mkimage-objs := aisimage.o \ zynqimage.o \ zynqmpimage.o \ zynqmpbif.o \ + $(LIBCRYPTO_OBJS-y) \ $(LIBFDT_OBJS) \ gpimage.o \ gpimage-common.o \ From ed6c9e0b6668a05d62f5d1b75aecaf246ba51042 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Fri, 19 Feb 2021 12:45:12 -0600 Subject: [PATCH 03/26] lib: Add support for ECDSA image signing mkimage supports rsa2048, and rsa4096 signatures. With newer silicon now supporting hardware-accelerated ECDSA, it makes sense to expand signing support to elliptic curves. Implement host-side ECDSA signing and verification with libcrypto. Device-side implementation of signature verification is beyond the scope of this patch. Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- common/image-sig.c | 11 +- include/image.h | 3 + include/u-boot/ecdsa.h | 94 +++++++++++ lib/ecdsa/ecdsa-libcrypto.c | 306 ++++++++++++++++++++++++++++++++++++ tools/Makefile | 3 + 5 files changed, 415 insertions(+), 2 deletions(-) create mode 100644 include/u-boot/ecdsa.h create mode 100644 lib/ecdsa/ecdsa-libcrypto.c diff --git a/common/image-sig.c b/common/image-sig.c index 54f0eb2019..0f8e592aba 100644 --- a/common/image-sig.c +++ b/common/image-sig.c @@ -16,6 +16,7 @@ DECLARE_GLOBAL_DATA_PTR; #endif /* !USE_HOSTCC*/ #include +#include #include #include @@ -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[] = { diff --git a/include/image.h b/include/image.h index 375fb46bf9..f172b1224d 100644 --- a/include/image.h +++ b/include/image.h @@ -1224,16 +1224,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 # 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 diff --git a/include/u-boot/ecdsa.h b/include/u-boot/ecdsa.h new file mode 100644 index 0000000000..979690d966 --- /dev/null +++ b/include/u-boot/ecdsa.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2020, Alexandru Gagniuc . + */ + +#ifndef _ECDSA_H +#define _ECDSA_H + +#include +#include +#include + +/** + * 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 diff --git a/lib/ecdsa/ecdsa-libcrypto.c b/lib/ecdsa/ecdsa-libcrypto.c new file mode 100644 index 0000000000..322880963f --- /dev/null +++ b/lib/ecdsa/ecdsa-libcrypto.c @@ -0,0 +1,306 @@ +// 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 + */ + +#include +#include +#include +#include +#include + +/* 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) +{ + const char *kname = info->keydir; + int key_len_bytes, ret; + + 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; +} diff --git a/tools/Makefile b/tools/Makefile index 3b8e7ac73a..d020c55d66 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -70,6 +70,8 @@ RSA_OBJS-$(CONFIG_FIT_SIGNATURE) := $(addprefix lib/rsa/, \ 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) @@ -124,6 +126,7 @@ dumpimage-mkimage-objs := aisimage.o \ gpimage.o \ gpimage-common.o \ mtk_image.o \ + $(ECDSA_OBJS-y) \ $(RSA_OBJS-y) \ $(AES_OBJS-y) From 9eef0fe7e5b47b5bc2dde19d67fcde6ee91662c8 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Fri, 19 Feb 2021 12:45:13 -0600 Subject: [PATCH 04/26] doc: signature.txt: Document devicetree format for ECDSA keys Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- doc/uImage.FIT/signature.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/uImage.FIT/signature.txt b/doc/uImage.FIT/signature.txt index a3455889ed..0139295d33 100644 --- a/doc/uImage.FIT/signature.txt +++ b/doc/uImage.FIT/signature.txt @@ -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:: From a4515f0ff775fb5e1ba35cc86b1b2c4e9caf6a99 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Fri, 19 Feb 2021 12:45:14 -0600 Subject: [PATCH 05/26] test/py: Add pycryptodomex to list of required pakages We wish to use pycryptodomex to verify code paths involving ECDSA signatures. Add it to requirements.txt so that they get picked up automatically .gitlab and .azure tasks Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- test/py/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/py/requirements.txt b/test/py/requirements.txt index 89ca259b21..9c346b4b41 100644 --- a/test/py/requirements.txt +++ b/test/py/requirements.txt @@ -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 From f91de329abb6f3a247758354bccb172070ccc4ac Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Fri, 19 Feb 2021 12:45:15 -0600 Subject: [PATCH 06/26] test/py: ecdsa: Add test for mkimage ECDSA signing Add a test to make sure that the ECDSA signatures generated by mkimage can be verified successfully. pyCryptodomex was chosen as the crypto library because it integrates much better with python code. Using openssl would have been unnecessarily painful. Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- test/py/tests/test_fit_ecdsa.py | 111 ++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 test/py/tests/test_fit_ecdsa.py diff --git a/test/py/tests/test_fit_ecdsa.py b/test/py/tests/test_fit_ecdsa.py new file mode 100644 index 0000000000..f597570281 --- /dev/null +++ b/test/py/tests/test_fit_ecdsa.py @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2020,2021 Alexandru Gagniuc + +""" +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'-k{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) From fb6532ec6c0c247dc204f65cb298d0865f7eaf3b Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Fri, 19 Feb 2021 12:45:16 -0600 Subject: [PATCH 07/26] doc: signature.txt: Document the keydir and keyfile arguments After lots of debating, this documents how we'd like mkimage to treat 'keydir' and 'keyfile' arguments. The rest is in the docs. Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- doc/uImage.FIT/signature.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/uImage.FIT/signature.txt b/doc/uImage.FIT/signature.txt index 0139295d33..d9a9121190 100644 --- a/doc/uImage.FIT/signature.txt +++ b/doc/uImage.FIT/signature.txt @@ -472,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 "/." Here the extension "ext" is +specific to the signing algorithm. + + Hardware Signing with PKCS#11 or with HSM ----------------------------------------- From 36bfcb62b3e7b846d0b693828df54a0d58e07511 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Fri, 19 Feb 2021 12:45:17 -0600 Subject: [PATCH 08/26] mkimage: Add a 'keyfile' argument for image signing It's not always desirable to use 'keydir' and some ad-hoc heuristics to get the filename of the signing key. More often, just passing the filename is the simpler, easier, and logical thing to do. Since mkimage doesn't use long options, we're slowly running out of letters. I've chosen '-G' because it was available. Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- include/image.h | 16 +++++++++---- tools/fit_image.c | 3 ++- tools/image-host.c | 58 +++++++++++++++++++++++++--------------------- tools/imagetool.h | 1 + tools/mkimage.c | 6 ++++- 5 files changed, 52 insertions(+), 32 deletions(-) diff --git a/include/image.h b/include/image.h index f172b1224d..3ff3c035a7 100644 --- a/include/image.h +++ b/include/image.h @@ -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); @@ -1256,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 */ diff --git a/tools/fit_image.c b/tools/fit_image.c index d440d143c6..ae30f80783 100644 --- a/tools/fit_image.c +++ b/tools/fit_image.c @@ -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, diff --git a/tools/image-host.c b/tools/image-host.c index 33a224129a..270d36fe45 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -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); diff --git a/tools/imagetool.h b/tools/imagetool.h index 2801ea9e9f..e229a34ffc 100644 --- a/tools/imagetool.h +++ b/tools/imagetool.h @@ -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 */ diff --git a/tools/mkimage.c b/tools/mkimage.c index 68d5206cb4..cc7b242faf 100644 --- a/tools/mkimage.c +++ b/tools/mkimage.c @@ -108,6 +108,7 @@ static void usage(const char *msg) "Signing / verified boot options: [-k keydir] [-K dtb] [ -c ] [-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; From 824ee745fbcaa73ad74a30f992aaf2e732a5a325 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Fri, 19 Feb 2021 12:45:18 -0600 Subject: [PATCH 09/26] lib/rsa: Use the 'keyfile' argument from mkimage Keys can be derived from keydir, and the "key-name-hint" property of the FIT. They can also be specified ad-literam via 'keyfile'. Update the RSA signing path to use the appropriate one. Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- lib/rsa/rsa-sign.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/lib/rsa/rsa-sign.c b/lib/rsa/rsa-sign.c index 557c690a6d..65c6d4490c 100644 --- a/lib/rsa/rsa-sign.c +++ b/lib/rsa/rsa-sign.c @@ -210,14 +210,20 @@ static int rsa_get_pub_key(const char *keydir, const char *name, * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL) */ static int rsa_pem_get_priv_key(const char *keydir, const char *name, - RSA **rsap) + const char *keyfile, RSA **rsap) { char path[1024]; RSA *rsa; FILE *f; *rsap = NULL; - snprintf(path, sizeof(path), "%s/%s.key", keydir, name); + if (keydir && name) + snprintf(path, sizeof(path), "%s/%s.key", keydir, name); + else if (keyfile) + snprintf(path, sizeof(path), "%s", keyfile); + else + return -EINVAL; + f = fopen(path, "r"); if (!f) { fprintf(stderr, "Couldn't open RSA private key: '%s': %s\n", @@ -247,6 +253,7 @@ static int rsa_pem_get_priv_key(const char *keydir, const char *name, * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL) */ static int rsa_engine_get_priv_key(const char *keydir, const char *name, + const char *keyfile, ENGINE *engine, RSA **rsap) { const char *engine_id; @@ -260,6 +267,10 @@ static int rsa_engine_get_priv_key(const char *keydir, const char *name, 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), @@ -274,14 +285,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; @@ -319,11 +335,12 @@ err_rsa: * @return 0 if ok, -ve on error (in which case *rsap 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, RSA **rsap) { 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, + rsap); + return rsa_pem_get_priv_key(keydir, name, keyfile, rsap); } static int rsa_init(void) @@ -534,7 +551,8 @@ 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, &rsa); if (ret) goto err_priv; ret = rsa_sign_with_key(rsa, info->padding, info->checksum, region, From eb22759e2be9c45b0f39ee7ab028e6e4144ce629 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Fri, 19 Feb 2021 12:45:19 -0600 Subject: [PATCH 10/26] lib/ecdsa: Use the 'keydir' argument from mkimage if appropriate Keys can be derived from keydir, and the "key-name-hint" property of the FIT. They can also be specified ad-literam via 'keyfile'. Update the ECDSA signing path to use the appropriate one. Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- lib/ecdsa/ecdsa-libcrypto.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/ecdsa/ecdsa-libcrypto.c b/lib/ecdsa/ecdsa-libcrypto.c index 322880963f..1757a14562 100644 --- a/lib/ecdsa/ecdsa-libcrypto.c +++ b/lib/ecdsa/ecdsa-libcrypto.c @@ -140,8 +140,20 @@ static int read_key(struct signer *ctx, const char *key_name) /* Prepare a 'signer' context that's ready to sign and verify. */ static int prepare_ctx(struct signer *ctx, const struct image_sign_info *info) { - const char *kname = info->keydir; 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) From 78015263b9789ef12fa6e454cf5041f97ce40da4 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Fri, 19 Feb 2021 12:45:20 -0600 Subject: [PATCH 11/26] test/py: ecdsa: Use mkimage keyfile instead of keydir argument Originally, the ECDSA code path used 'keydir' as the key filename. mkimage has since been updated to include a new 'keyfile' argument. Use the new argument for passing in the key. Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- test/py/tests/test_fit_ecdsa.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/py/tests/test_fit_ecdsa.py b/test/py/tests/test_fit_ecdsa.py index f597570281..87b6081222 100644 --- a/test/py/tests/test_fit_ecdsa.py +++ b/test/py/tests/test_fit_ecdsa.py @@ -52,7 +52,7 @@ class SignableFitImage(object): 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'-k{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: From f0a6ec3656e5561a3d2e246b0fad7e55e419515b Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Mon, 29 Mar 2021 12:05:10 -0500 Subject: [PATCH 12/26] spl: fit: Don't overwrite previous loadable if "load" is missing spl_load_fit_image() will try to load an image at the address given in the "load" property. Absent such property, it uses image_info->load_addr Correct use of this is demonstrated in spl_fit_append_fdt(), which resets the 'load_addr' before each spl_load_fit_image() call. On the other hand loading "loadables" loop in spl_load_simple_fit() completely ignores this. It re-uses the same structure, but doesn't reset load_addr. If loadable [i] does not have a "load" property, its load address defaults to load_addr, which still contains the address of loadable [i - 1]. A simple solution is to treat NULL as an invalid load address. The caller can set load_addr = 0 to request an abort if the "load" property is absent. Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- common/spl/spl_fit.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c index 49508fc518..b7755cc124 100644 --- a/common/spl/spl_fit.c +++ b/common/spl/spl_fit.c @@ -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; @@ -700,6 +706,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", From e1662d699551de17e62fee6e2c3a883854e0e2f5 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Mon, 29 Mar 2021 12:05:11 -0500 Subject: [PATCH 13/26] doc: FIT image: Introduce "u-boot, fpga-legacy" property Commit 4afc4f37c70e ("doc: FIT image: Clarify format and simplify syntax") introduced a "compatible" property for loadable images. It did not define its contents. Use "u-boot,fpga-legacy" compatible string to specify that fpga_load() should be used to load the image. Signed-off-by: Alexandru Gagniuc --- doc/uImage.FIT/source_file_format.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/uImage.FIT/source_file_format.txt b/doc/uImage.FIT/source_file_format.txt index 00ed3ebe93..f93ac6d1c7 100644 --- a/doc/uImage.FIT/source_file_format.txt +++ b/doc/uImage.FIT/source_file_format.txt @@ -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 From 55e7a1a4350c70d685684111a48e1875e04aad8b Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Mon, 29 Mar 2021 12:05:12 -0500 Subject: [PATCH 14/26] spl: fit: Move FPGA loading code to separate functions The FPGA loading code in spl_simple_fit_read() can easily be separated from the rest of the logic. It is split into two functions instead of one because spl_fit_upload_fpga() is used in a subsequent patch. Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- common/spl/spl_fit.c | 70 ++++++++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c index b7755cc124..514ec9138f 100644 --- a/common/spl/spl_fit.c +++ b/common/spl/spl_fit.c @@ -529,6 +529,49 @@ __weak bool spl_load_simple_fit_skip_processing(void) return false; } +static int spl_fit_upload_fpga(struct spl_fit_info *ctx, int node, + struct spl_image_info *fpga_image) +{ + int ret; + + debug("FPGA bitstream at: %x, size: %x\n", + (u32)fpga_image->load_addr, fpga_image->size); + + 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; + + /* 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) @@ -612,31 +655,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: From d8a395109e127cda11bcfceae2f7200c785e8443 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Mon, 29 Mar 2021 12:05:13 -0500 Subject: [PATCH 15/26] spl: fit: Warn if FIT contains "fpga" property in config node Commit 4afc4f37c70e ("doc: FIT image: Clarify format and simplify syntax") requires that FPGA images be referenced through the "loadables" in the config node. This means that "fpga" properties in config nodes are deprecated. Given that there are likely FIT images which use "fpga", let's not break those right away. Print a warning message that such use is deprecated, and give users a couple of releases to update their Signed-off-by: Alexandru Gagniuc --- common/spl/spl_fit.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c index 514ec9138f..496fabc4e7 100644 --- a/common/spl/spl_fit.c +++ b/common/spl/spl_fit.c @@ -529,6 +529,12 @@ __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) { @@ -562,6 +568,8 @@ static int spl_fit_load_fpga(struct spl_fit_info *ctx, 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) { From 35f4f8e6fc670c73fe418f87249ec9b52ada1e14 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Mon, 29 Mar 2021 12:05:14 -0500 Subject: [PATCH 16/26] spl: fit: Support loading FPGA images from list of "loadables" Commit 4afc4f37c70e ("doc: FIT image: Clarify format and simplify syntax") and delegated FPGA images to be added via the list of "loadables" in lieu of the "fpga" property. Now actually implement this in code. Note that the "compatible" property is ignored for the time being, as implementing "compatible" loading is beyond the scope of this change. However, "u-boot,fpga-legacy" is accepted without warning. Signed-off-by: Alexandru Gagniuc --- common/spl/spl_fit.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c index 496fabc4e7..4288f571fc 100644 --- a/common/spl/spl_fit.c +++ b/common/spl/spl_fit.c @@ -480,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)) @@ -538,11 +552,18 @@ static void warn_deprecated(const char *msg) 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) { @@ -742,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)); From df51ae767ecb8e5e10c0709a5674e2ef0fe20961 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Mon, 29 Mar 2021 12:05:15 -0500 Subject: [PATCH 17/26] Kconfig: Document the limitations of the simple SPL_LOAD_FIT path The "simple" SPL_LOAD_FIT path is the most compliant with the format documented in doc/uImage.FIT/source_file_format.txt. The other two paths to load a FIT are SPL_LOAD_FIT_FULL and the "bootm" command. Since the Kconfig menu is the most likely place for a new user to see these options, it seems like the most logical candidate to document the limitations. This documents the _known_ issues, and is not intended to be a complete list of all follies. Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- common/Kconfig.boot | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/common/Kconfig.boot b/common/Kconfig.boot index 9c335f4f8c..5a18d62d78 100644 --- a/common/Kconfig.boot +++ b/common/Kconfig.boot @@ -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 From 6795c751098810c3f8489d94d5a7cba430bade6d Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Mon, 29 Mar 2021 12:05:16 -0500 Subject: [PATCH 18/26] doc: FIT image: Update FPGA example to make use of "loadables" The new correct way to load an FPGA image is to declare it in the list of "loadables". multi-with-fpga.its used the now deprecated "fpga" property. Since this example most likely intended to use u-boot's generic FPGA loading code, compatible = "u-boot,fpga-legacy" is also appropriate here. Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- doc/uImage.FIT/multi-with-fpga.its | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/uImage.FIT/multi-with-fpga.its b/doc/uImage.FIT/multi-with-fpga.its index 47ee5760c4..021cbc7cf4 100644 --- a/doc/uImage.FIT/multi-with-fpga.its +++ b/doc/uImage.FIT/multi-with-fpga.its @@ -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"; }; }; }; From d442f69399996d426cdc45ce40bcf1e48d9cd8b9 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Wed, 31 Mar 2021 14:32:27 -0400 Subject: [PATCH 19/26] common: fit: Add weak board_fit_config_name_match Several architectures had a default board_fit_config_name_match already; this provides a generic weak version. We default to rejecting all configs. This will use the FIT's default config, instead of the first config. This may result in boot failures if there are multiple configurations and the first config is *not* the default. Signed-off-by: Sean Anderson --- arch/arm/cpu/armv8/fsl-layerscape/spl.c | 9 --------- arch/arm/mach-rockchip/spl.c | 10 ---------- common/common_fit.c | 5 +++++ 3 files changed, 5 insertions(+), 19 deletions(-) diff --git a/arch/arm/cpu/armv8/fsl-layerscape/spl.c b/arch/arm/cpu/armv8/fsl-layerscape/spl.c index d5131bcf4b..46e8af8ebd 100644 --- a/arch/arm/cpu/armv8/fsl-layerscape/spl.c +++ b/arch/arm/cpu/armv8/fsl-layerscape/spl.c @@ -139,13 +139,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 */ diff --git a/arch/arm/mach-rockchip/spl.c b/arch/arm/mach-rockchip/spl.c index 4b5c22dc13..02c40fb37e 100644 --- a/arch/arm/mach-rockchip/spl.c +++ b/arch/arm/mach-rockchip/spl.c @@ -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 diff --git a/common/common_fit.c b/common/common_fit.c index 219674d467..cde2dc45e9 100644 --- a/common/common_fit.c +++ b/common/common_fit.c @@ -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. From 18fd663c630729695caba060aca394d8a910c573 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Thu, 1 Apr 2021 13:25:26 -0500 Subject: [PATCH 20/26] spl: LOAD_FIT_FULL: Fix selection of the "fdt" node The correct FDT to use is described by the "fdt" property of the configuration node. When the fit_unamep argument to fit_image_load() is "fdt", we get the "/images/fdt" node. This is incorrect, as it ignores the "fdt" property of the config node, and in most cases, the "/images/fdt" node doesn't exist. Use NULL for the 'fit_unamep' argument. With NULL, fit_image_load() uses the IH_TYPE_FLATDT value to read the config property "fdt", which points to the correct FDT node(s). fit_image_load() should probably be split into a function that reads an image by name, and one that reads an image by config reference. I don't make those decisions, I just point out the craziness. Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- common/spl/spl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/spl/spl.c b/common/spl/spl.c index 556a91ab53..33119e95b8 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -202,7 +202,6 @@ 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; const char *uname; ulong fw_data = 0, dt_data = 0, img_data = 0; ulong fw_len = 0, dt_len = 0, img_len = 0; @@ -231,8 +230,7 @@ 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) From 2eaae17bc90ec524dbddc52a85ff3705727d7c5a Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Thu, 1 Apr 2021 13:25:27 -0500 Subject: [PATCH 21/26] spl: LOAD_FIT_FULL: Do not hard-code os to IH_OS_U_BOOT The information on the OS should be contained in the FIT, as the self-explanatory "os" property of a node under /images. Hard-coding this to U_BOOT might send us down the wrong path later in the boot process. Reviewed-by: Simon Glass Signed-off-by: Alexandru Gagniuc --- common/spl/spl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common/spl/spl.c b/common/spl/spl.c index 33119e95b8..85fccb5de4 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -221,8 +221,9 @@ static int spl_load_fit_image(struct spl_image_info *spl_image, 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); From 60138aa87cb3281dafebf25125d546718beb3969 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Thu, 1 Apr 2021 13:25:28 -0500 Subject: [PATCH 22/26] spl: LOAD_FIT_FULL: Relocate FDT for u-boot payloads U-Boot expects the FDT to be located right after the _end linker symbol (see fdtdec.c: board_fdt_blob_setup()) The "basic" LOAD_FIT path is aware of this limitation, and relocates the FDT at the expected location. Guessing the expected location probably only works reliably on 32-bit arm, and it feels like a hack. One proposal would be to pass the FDT address to u-boot (e.g. using 'r2' on arm platforms). The variable is named "fdt_hack" to remind future contributors that, "hey! we should fix the underlying problem". However, that is beyond the scope of this patch. Signed-off-by: Alexandru Gagniuc --- common/spl/spl.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/common/spl/spl.c b/common/spl/spl.c index 85fccb5de4..8941cbf596 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -202,6 +202,7 @@ static int spl_load_fit_image(struct spl_image_info *spl_image, { bootm_headers_t images; const char *fit_uname_config = NULL; + 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; @@ -234,9 +235,18 @@ 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_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) From 51d3a8b54f2cd1bd8278d0611db4ff2d17235229 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Thu, 1 Apr 2021 13:25:29 -0500 Subject: [PATCH 23/26] spl: LOAD_FIT_FULL: Support 'kernel' and 'firmware' properties The 'firmware' property of a config node takes precedence over the 'kernel' property. 'standalone' is deprecated. However, give users a couple of releases where 'standalone' still works, but warns loudly. Signed-off-by: Alexandru Gagniuc --- common/spl/spl.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/common/spl/spl.c b/common/spl/spl.c index 8941cbf596..8c4cd933cf 100644 --- a/common/spl/spl.c +++ b/common/spl/spl.c @@ -215,7 +215,24 @@ 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; From 47b6f7f8451117546dd12af3eccd58961a9f7f05 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Thu, 1 Apr 2021 13:25:30 -0500 Subject: [PATCH 24/26] image-fit: Accept IH_TYPE_FIRMWARE in fit_image_load() as valid Consider the following FIT: images { whipple {}; }; configurations { conf-1 { firmware = "whipple"; }; }; Getting the 'firmware' image with fit_image_load() is not possible, as it doesn't understand 'firmware =' properties. Although one could pass IH_TYPE_FIRMWARE for 'image_type', this needs to be converted to a "firmware" string for FDT lookup -- exactly what this change does. Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- common/image-fit.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/image-fit.c b/common/image-fit.c index b972042f43..4e033e19cf 100644 --- a/common/image-fit.c +++ b/common/image-fit.c @@ -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: From 033ac4ebbad82241e2d7da3ead662ccb495b9e89 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Thu, 1 Apr 2021 13:25:31 -0500 Subject: [PATCH 25/26] image-fit: Accept OP-TEE images when booting a FIT OP-TEE images are normally packaged with type = "tee; os = "tee"; However, fit_image_load() thinks that is somehow invalid. However if they were declared as type = "kernel", os = "linux", fit_image_load() would happily accept them and allow the boot to continue. There is no technical limitation to excluding "tee". Allowing "tee" images is useful in a boot flow where OP-TEE is executed before linux. In fact, I think it's unintuitive for a "load"ing function to also do parsing and contain a bunch ad-hoc heuristics that only its caller might know. But I don't make the rules, I just write fixes. In more polite terms: refactoring the fit_image API is beyond the scope of this change. Signed-off-by: Alexandru Gagniuc Reviewed-by: Simon Glass --- common/image-fit.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/image-fit.c b/common/image-fit.c index 4e033e19cf..e614643fe3 100644 --- a/common/image-fit.c +++ b/common/image-fit.c @@ -2093,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)); @@ -2100,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); From fbc777429fa35312a9ea5f106692172d3153e659 Mon Sep 17 00:00:00 2001 From: "Chan, Donald" Date: Thu, 1 Apr 2021 22:42:36 +0000 Subject: [PATCH 26/26] lib/rsa: Use EVP_PKEY instead of RSA Most modern OpenSSL engines have methods overridden at the EVP level rather than at RSA level, to make these engines work properly with mkimage, the RSA signing code needs to switch to using EVP_* APIs as much as possible. Signed-off-by: Donald Chan [trini: Rebase on top of keyfile changes] Signed-off-by: Tom Rini --- lib/rsa/rsa-sign.c | 169 ++++++++++++++++++--------------------------- 1 file changed, 67 insertions(+), 102 deletions(-) diff --git a/lib/rsa/rsa-sign.c b/lib/rsa/rsa-sign.c index 65c6d4490c..5a1583b8f7 100644 --- a/lib/rsa/rsa-sign.c +++ b/lib/rsa/rsa-sign.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -52,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) { @@ -89,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: @@ -118,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); @@ -166,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; } /** @@ -190,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); } /** @@ -206,17 +187,19 @@ 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, - const char *keyfile, RSA **rsap) + const char *keyfile, EVP_PKEY **evpp) { - char path[1024]; - RSA *rsa; - FILE *f; + char path[1024] = {0}; + FILE *f = NULL; - *rsap = NULL; + if (!evpp) + return -EINVAL; + + *evpp = NULL; if (keydir && name) snprintf(path, sizeof(path), "%s/%s.key", keydir, name); else if (keyfile) @@ -231,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; } @@ -249,20 +230,19 @@ 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, const char *keyfile, - 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; engine_id = ENGINE_get_id(engine); @@ -307,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; } /** @@ -331,16 +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, - const char *keyfile, ENGINE *engine, RSA **rsap) + const char *keyfile, ENGINE *engine, EVP_PKEY **evpp) { if (engine) return rsa_engine_get_priv_key(keydir, name, keyfile, engine, - rsap); - return rsa_pem_get_priv_key(keydir, name, keyfile, rsap); + evpp); + return rsa_pem_get_priv_key(keydir, name, keyfile, evpp); } static int rsa_init(void) @@ -434,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; @@ -447,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", @@ -472,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; @@ -480,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; } @@ -515,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; @@ -528,8 +484,6 @@ err_sign: err_create: free(sig); err_alloc: -err_set: - EVP_PKEY_free(key); return ret; } @@ -537,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; @@ -552,15 +506,15 @@ int rsa_sign(struct image_sign_info *info, } ret = rsa_get_priv_key(info->keydir, info->keyname, info->keyfile, - e, &rsa); + 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(); @@ -568,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); @@ -709,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__); @@ -717,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; @@ -789,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);