Explorar o código

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
Tom Rini %!s(int64=3) %!d(string=hai) anos
pai
achega
a6232e065d

+ 0 - 9
arch/arm/cpu/armv8/fsl-layerscape/spl.c

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

+ 0 - 10
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

+ 10 - 0
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

+ 5 - 0
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.

+ 1 - 1
common/image-fit-sig.c

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

+ 4 - 0
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:
@@ -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);

+ 10 - 3
common/image-sig.c

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

+ 33 - 7
common/spl/spl.c

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

+ 86 - 27
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;
@@ -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));
 

+ 2 - 1
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";
 		};
 	};
 };

+ 19 - 1
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::
 
@@ -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
 -----------------------------------------
 

+ 1 - 0
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

+ 16 - 5
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);
@@ -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 - 0
include/u-boot/ecdsa.h

@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2020, Alexandru Gagniuc <mr.nuke.me@gmail.com>.
+ */
+
+#ifndef _ECDSA_H
+#define _ECDSA_H
+
+#include <errno.h>
+#include <image.h>
+#include <linux/kconfig.h>
+
+/**
+ * crypto_algo API impementation for ECDSA;
+ * @see "struct crypto_algo"
+ * @{
+ */
+#if IMAGE_ENABLE_SIGN
+/**
+ * sign() - calculate and return signature for given input data
+ *
+ * @info:	Specifies key and FIT information
+ * @data:	Pointer to the input data
+ * @data_len:	Data length
+ * @sigp:	Set to an allocated buffer holding the signature
+ * @sig_len:	Set to length of the calculated hash
+ *
+ * This computes input data signature according to selected algorithm.
+ * Resulting signature value is placed in an allocated buffer, the
+ * pointer is returned as *sigp. The length of the calculated
+ * signature is returned via the sig_len pointer argument. The caller
+ * should free *sigp.
+ *
+ * @return: 0, on success, -ve on error
+ */
+int ecdsa_sign(struct image_sign_info *info, const struct image_region region[],
+	       int region_count, uint8_t **sigp, uint *sig_len);
+
+/**
+ * add_verify_data() - Add verification information to FDT
+ *
+ * Add public key information to the FDT node, suitable for
+ * verification at run-time. The information added depends on the
+ * algorithm being used. I just copypasted this from rsa.h.
+ *
+ * @info:	Specifies key and FIT information
+ * @keydest:	Destination FDT blob for public key data
+ * @return: 0, on success, -ENOSPC if the keydest FDT blob ran out of space,
+ * other -ve value on error
+ */
+int ecdsa_add_verify_data(struct image_sign_info *info, void *keydest);
+#else
+static inline
+int ecdsa_sign(struct image_sign_info *info, const struct image_region region[],
+	       int region_count, uint8_t **sigp, uint *sig_len)
+{
+	return -ENXIO;
+}
+
+static inline
+int ecdsa_add_verify_data(struct image_sign_info *info, void *keydest)
+{
+	return -ENXIO;
+}
+#endif
+
+#if IMAGE_ENABLE_VERIFY_ECDSA
+/**
+ * verify() - Verify a signature against some data
+ *
+ * @info:	Specifies key and FIT information
+ * @data:	Pointer to the input data
+ * @data_len:	Data length
+ * @sig:	Signature
+ * @sig_len:	Number of bytes in signature
+ * @return 0 if verified, -ve on error
+ */
+int ecdsa_verify(struct image_sign_info *info,
+		 const struct image_region region[], int region_count,
+		 uint8_t *sig, uint sig_len);
+#else
+static inline
+int ecdsa_verify(struct image_sign_info *info,
+		 const struct image_region region[], int region_count,
+		 uint8_t *sig, uint sig_len)
+{
+	return -ENXIO;
+}
+#endif
+/** @} */
+
+#define ECDSA256_BYTES	(256 / 8)
+
+#endif

+ 27 - 0
include/u-boot/fdt-libcrypto.h

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

+ 0 - 0
include/u-boot/rsa-checksum.h → include/u-boot/hash-checksum.h


+ 1 - 0
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

+ 1 - 1
lib/crypto/pkcs7_verify.c

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

+ 1 - 1
lib/crypto/x509_public_key.c

@@ -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 - 0
lib/ecdsa/ecdsa-libcrypto.c

@@ -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 - 0
lib/fdt-libcrypto.c

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

+ 2 - 1
lib/rsa/rsa-checksum.c → lib/hash-checksum.c

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

+ 1 - 1
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

+ 89 - 169
lib/rsa/rsa-sign.c

@@ -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;
+
+	if (!evpp)
+		return -EINVAL;
 
-	*rsap = NULL;
+	*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);

+ 1 - 0
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

+ 111 - 0
test/py/tests/test_fit_ecdsa.py

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

+ 10 - 1
tools/Makefile

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

+ 2 - 1
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,

+ 32 - 26
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,
 				&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);

+ 1 - 0
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 */

+ 5 - 1
tools/mkimage.c

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