[PATCH 14/16] tools: Add a new tool to check FDT-blob signatures

Simon Glass sjg at chromium.org
Fri Nov 12 20:28:15 CET 2021


Add a stand-alone (from mkimage) tool that checks signatures in an FDT
blob.

Signed-off-by: Simon Glass <sjg at chromium.org>
---

 tools/Makefile         |   6 +-
 tools/fdt-host.c       |  20 ++++
 tools/fdt_check_sign.c |  85 ++++++++++++++
 tools/fdt_host.h       |  19 +++
 tools/image-fdt-sig.c  | 254 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 382 insertions(+), 2 deletions(-)
 create mode 100644 tools/fdt_check_sign.c
 create mode 100644 tools/image-fdt-sig.c

diff --git a/tools/Makefile b/tools/Makefile
index 07eca631cb0..b7857d86fac 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -73,13 +73,13 @@ mkenvimage-objs := mkenvimage.o os_support.o lib/crc32.o
 
 hostprogs-y += dumpimage mkimage
 hostprogs-$(CONFIG_TOOLS_LIBCRYPTO) += fit_info fit_check_sign \
-	fdt_sign
+	fdt_sign fdt_check_sign
 
 hostprogs-$(CONFIG_CMD_BOOTEFI_SELFTEST) += file2include
 
 FIT_OBJS-y := fit_common.o fit_image.o image-host.o boot/image-fit.o
 FIT_SIG_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := image-sig-host.o \
-	boot/image-fit-sig.o fdt-host.o
+	boot/image-fit-sig.o fdt-host.o image-fdt-sig.o
 FIT_CIPHER_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := boot/image-cipher.o
 
 # The following files are synced with upstream DTC.
@@ -157,6 +157,7 @@ mkimage-objs   := $(dumpimage-mkimage-objs) mkimage.o
 fit_info-objs   := $(dumpimage-mkimage-objs) fit_info.o
 fit_check_sign-objs   := $(dumpimage-mkimage-objs) fit_check_sign.o
 fdt_sign-objs   := $(dumpimage-mkimage-objs) fdt_sign.o
+fdt_check_sign-objs   := $(dumpimage-mkimage-objs) fdt_check_sign.o
 file2include-objs := file2include.o
 
 ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_TOOLS_LIBCRYPTO),)
@@ -195,6 +196,7 @@ HOSTLDLIBS_dumpimage := $(HOSTLDLIBS_mkimage)
 HOSTLDLIBS_fit_info := $(HOSTLDLIBS_mkimage)
 HOSTLDLIBS_fit_check_sign := $(HOSTLDLIBS_mkimage)
 HOSTLDLIBS_fdt_sign := $(HOSTLDLIBS_mkimage)
+HOSTLDLIBS_fdt_check_sign := $(HOSTLDLIBS_mkimage)
 
 hostprogs-$(CONFIG_EXYNOS5250) += mkexynosspl
 hostprogs-$(CONFIG_EXYNOS5420) += mkexynosspl
diff --git a/tools/fdt-host.c b/tools/fdt-host.c
index 8aa0e099f2f..142d486091b 100644
--- a/tools/fdt-host.c
+++ b/tools/fdt-host.c
@@ -331,3 +331,23 @@ int fdt_add_verif_data(const char *keydir, const char *keyfile, void *keydest,
 
 	return ret;
 }
+
+#ifdef CONFIG_FIT_SIGNATURE
+int fdt_check_sign(const void *blob, const void *key)
+{
+	int fdt_sigs;
+	int ret;
+
+	fdt_sigs = fdt_subnode_offset(blob, 0, FIT_SIG_NODENAME);
+	if (fdt_sigs < 0) {
+		printf("No %s node found (err=%d)\n", FIT_SIG_NODENAME,
+		       fdt_sigs);
+		return fdt_sigs;
+	}
+
+	ret = fdt_sig_verify(blob, fdt_sigs, key);
+	fprintf(stderr, "Verify %s\n", ret ? "failed" : "OK");
+
+	return ret;
+}
+#endif
diff --git a/tools/fdt_check_sign.c b/tools/fdt_check_sign.c
new file mode 100644
index 00000000000..943d01d5167
--- /dev/null
+++ b/tools/fdt_check_sign.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Check a signature in an FDT file
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg at chromium.org>
+ */
+
+#include "mkimage.h"
+#include "fit_common.h"
+#include <image.h>
+
+void usage(char *cmdname)
+{
+	fprintf(stderr,
+		"Usage: %s -f dtb_file -k key file\n"
+		"          -f ==> set dtb file which should be checked\n"
+		"          -k ==> set key .dtb file which should be checked\n",
+		cmdname);
+	exit(EXIT_FAILURE);
+}
+
+int main(int argc, char **argv)
+{
+	struct image_tool_params params;
+	char *fdtfile = NULL;
+	char *keyfile = NULL;
+	char cmdname[256];
+	struct stat fsbuf;
+	struct stat ksbuf;
+	void *fdt_blob;
+	void *key_blob;
+	int ffd = -1;
+	int kfd = -1;
+	int ret;
+	int c;
+
+	memset(&params, '\0', sizeof(params));
+	strncpy(cmdname, *argv, sizeof(cmdname) - 1);
+	cmdname[sizeof(cmdname) - 1] = '\0';
+	while ((c = getopt(argc, argv, "f:k:")) != -1)
+		switch (c) {
+		case 'f':
+			fdtfile = optarg;
+			break;
+		case 'k':
+			keyfile = optarg;
+			break;
+		default:
+			usage(cmdname);
+			break;
+	}
+
+	if (!fdtfile) {
+		fprintf(stderr, "%s: Missing fdt file\n", *argv);
+		usage(*argv);
+	}
+	if (!keyfile) {
+		fprintf(stderr, "%s: Missing key file\n", *argv);
+		usage(*argv);
+	}
+
+	ffd = mmap_fdt(cmdname, fdtfile, 0, &fdt_blob, &fsbuf, false, true);
+	if (ffd < 0)
+		return EXIT_FAILURE;
+	kfd = mmap_fdt(cmdname, keyfile, 0, &key_blob, &ksbuf, false, true);
+	if (kfd < 0)
+		return EXIT_FAILURE;
+
+	ret = fdt_check_sign(fdt_blob, key_blob);
+	if (!ret) {
+		ret = EXIT_SUCCESS;
+		printf("Signature check OK\n");
+	} else {
+		ret = EXIT_FAILURE;
+		fprintf(stderr, "Signature check bad (error %d)\n", ret);
+	}
+
+	(void)munmap((void *)fdt_blob, fsbuf.st_size);
+	(void)munmap((void *)key_blob, ksbuf.st_size);
+
+	close(ffd);
+	close(kfd);
+	exit(ret);
+}
diff --git a/tools/fdt_host.h b/tools/fdt_host.h
index 877f098676b..b653efcb5d3 100644
--- a/tools/fdt_host.h
+++ b/tools/fdt_host.h
@@ -57,4 +57,23 @@ int fit_check_sign(const void *fit, const void *key,
 int fdt_get_regions(const void *blob, int strtab_len,
 		    struct image_region **regionp, int *region_countp);
 
+/**
+ * fdt_check_sign() - Check signatures in an FDT blob
+ *
+ * @fdt: FDT blob to check
+ * @key: Key FDT blob to check against
+ * @return 0 if OK, -ve if any required signature failed
+ */
+int fdt_check_sign(const void *blob, const void *key);
+
+/**
+ * fdt_sig_verify() - Check signatures in an FDT blob
+ *
+ * @fdt: FDT blob to check
+ * @fit_sigs: Offset of /signatures node in the FDT
+ * @key: Key FDT blob to check against
+ * @return 0 if OK, -ve if any required signature failed
+ */
+int fdt_sig_verify(const void *blob, int fdt_sigs, const void *key);
+
 #endif /* __FDT_HOST_H__ */
diff --git a/tools/image-fdt-sig.c b/tools/image-fdt-sig.c
new file mode 100644
index 00000000000..8b2570cbb06
--- /dev/null
+++ b/tools/image-fdt-sig.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 Google, LLC
+ * Written by Simon Glass <sjg at chromium.org>
+ */
+
+#include "mkimage.h"
+#include <image.h>
+#include <u-boot/rsa.h>
+
+/**
+ * fdt_setup_sig() - Setup the signing info ready for use
+ *
+ * @info: Info to set up
+ * @blob: FDT blob to check
+ * @noffset: Offset of signature to verify, e.g. '/signatures/sig-1'
+ * @key_blob: FDT blob containing keys to verify against
+ * @required_keynode: Node containing the key to verify
+ * @err_msgp: Returns the error messages if something goes wrong
+ * @return 0 if OK, -E2BIG if the FDT is too large, -ENOSYS if unsupported
+ *	algorithm, -EINVAL if the algorithm name is not present in the signature
+ */
+static int fdt_image_setup_verify(struct image_sign_info *info,
+				  const void *blob, int noffset,
+				  const void *key_blob, int required_keynode,
+				  char **err_msgp)
+{
+	char *algo_name;
+	const char *padding_name;
+
+	if (fdt_totalsize(blob) > CONFIG_VAL(FIT_SIGNATURE_MAX_SIZE)) {
+		*err_msgp = "Total size too large";
+		return -E2BIG;
+	}
+	if (fit_image_hash_get_algo(blob, noffset, &algo_name)) {
+		*err_msgp = "Can't get hash algo property";
+		return -EINVAL;
+	}
+
+	padding_name = fdt_getprop(blob, noffset, "padding", NULL);
+	if (!padding_name)
+		padding_name = RSA_DEFAULT_PADDING_NAME;
+
+	memset(info, '\0', sizeof(*info));
+	info->keyname = fdt_getprop(blob, noffset, FIT_KEY_HINT, NULL);
+	info->fit = blob;
+	info->node_offset = noffset;
+	info->name = algo_name;
+	info->checksum = image_get_checksum_algo(algo_name);
+	info->crypto = image_get_crypto_algo(algo_name);
+	info->padding = image_get_padding_algo(padding_name);
+	info->fdt_blob = key_blob;
+	info->required_keynode = required_keynode;
+
+	if (!info->checksum || !info->crypto || !info->padding) {
+		*err_msgp = "Unknown signature algorithm";
+		return -ENOSYS;
+	}
+
+	return 0;
+}
+
+/**
+ * fdt_verify_sig() - Verify that a signature can be verified with a key
+ *
+ * This checks a particular key to see if it verifies the given signature
+ *
+ * @blob: FDT blob to verify
+ * @noffset: Offset of signature to verify, e.g. '/signatures/sig-1'
+ * @key_blob: FDT blob containing keys to verify against
+ * @required_keynode: Node containing the key to verify
+ * @err_msgp: Returns the error messages if something goes wrong
+ * @return 0 if OK, -EPERM if the key could not be verified
+ */
+static int fdt_check_sig(const void *blob, int noffset, const void *key_blob,
+			 int required_keynode, char **err_msgp)
+{
+	int region_count, strtab_len;
+	struct image_sign_info info;
+	struct image_region *region;
+	const uint32_t *strings;
+	uint8_t *fdt_value;
+	int fdt_value_len;
+	int size;
+	int ret;
+
+	if (fdt_image_setup_verify(&info, blob, noffset, key_blob,
+				   required_keynode, err_msgp))
+		return -EPERM;
+
+	if (fit_image_hash_get_value(blob, noffset, &fdt_value,
+				     &fdt_value_len)) {
+		*err_msgp = "Can't get hash value property";
+		return -EPERM;
+	}
+
+	/* Add the strings */
+	strings = fdt_getprop(blob, noffset, "hashed-strings", &size);
+	if (!strings) {
+		*err_msgp = "Missing 'hashed-strings' property";
+		return -EINVAL;
+	}
+	if (size != sizeof(u32) * 2) {
+		*err_msgp = "Invalid 'hashed-strings' property";
+		return -EINVAL;
+	}
+	strtab_len = fdt32_to_cpu(strings[1]);
+	debug("%s: strtab_len=%x\n", __func__, strtab_len);
+
+	ret = fdt_get_regions(blob, strtab_len, &region, &region_count);
+	if (ret) {
+		*err_msgp = "Cannot get regions";
+		return ret;
+	}
+
+	ret = info.crypto->verify(&info, region, region_count, fdt_value,
+				  fdt_value_len);
+	free(region);
+	if (ret) {
+		*err_msgp = "Verification failed";
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+/**
+ * fdt_verify_sig() - Verify that a signature exists for a key
+ *
+ * This checks a particular key to see if it verifies. It tries all signatures
+ * to find a match.
+ *
+ * @blob: FDT blob to verify
+ * @fdt_sigs: Offset of /signatures node in blob
+ * @key_blob: FDT blob containing keys to verify against
+ * @required_keynode: Node containing the key to verify
+ * @return 0 if OK, -EPERM if the key could not be verified
+ */
+static int fdt_verify_sig(const void *blob, int fdt_sigs,
+			  const void *key_blob, int required_keynode)
+{
+	char *err_msg = "No 'signature' subnode found";
+	int bad_noffset = -1;
+	int noffset;
+	int verified = 0;
+	int ret;
+
+	/* Process all subnodes of the /signature node */
+	fdt_for_each_subnode(noffset, blob, fdt_sigs) {
+		printf("%s", fdt_get_name(blob, noffset, NULL));
+		ret = fdt_check_sig(blob, noffset, key_blob, required_keynode,
+				    &err_msg);
+		if (ret) {
+			printf("- ");
+			bad_noffset = noffset;
+		} else {
+			printf("+ ");
+			verified = 1;
+			break;
+		}
+	}
+
+	if (noffset == -FDT_ERR_TRUNCATED || noffset == -FDT_ERR_BADSTRUCTURE) {
+		err_msg = "Corrupted or truncated tree";
+		goto error;
+	}
+
+	if (verified) {
+		printf("\n");
+		return 0;
+	}
+
+error:
+	printf(" error!\n%s for node '%s'\n", err_msg,
+	       fdt_get_name(blob, bad_noffset, NULL));
+	return -EPERM;
+}
+
+/**
+ * fdt_verify_required_sigs() - Verify that required signatures are valid
+ *
+ * This works through all the provided keys, checking for a signature that uses
+ * that key. If the key is required, then it must verify correctly. If it is not
+ * required, then the failure is ignored, just displayed for informational
+ * purposes.
+ *
+ * If the "required-mode" property is present and set to "any" then only one of
+ * the required keys needs to be verified
+ *
+ * @blob: FDT blob to verify
+ * @fdt_sigs: Offset of /signatures node in blob
+ * @key_blob: FDT blob containing keys to verify against
+ * @return 0 if OK, -EPERM if required keys could not be verified (either any or
+ *	all), -EINVAL if the FDT is missing something
+ */
+static int fdt_verify_required_sigs(const void *blob, int fdt_sigs,
+				    const void *key_blob)
+{
+	int key_node;
+	int keys_node;
+	int verified = 0;
+	int reqd_sigs = 0;
+	bool reqd_policy_all = true;
+	const char *reqd_mode;
+
+	/* Work out what we need to verify */
+	keys_node = fdt_subnode_offset(key_blob, 0, FIT_SIG_NODENAME);
+	if (keys_node < 0) {
+		debug("%s: No signature node found: %s\n", __func__,
+		      fdt_strerror(keys_node));
+		return 0;
+	}
+
+	/* Get required-mode policy property from DTB */
+	reqd_mode = fdt_getprop(key_blob, keys_node, "required-mode", NULL);
+	if (reqd_mode && !strcmp(reqd_mode, "any"))
+		reqd_policy_all = false;
+
+	debug("%s: required-mode policy set to '%s'\n", __func__,
+	      reqd_policy_all ? "all" : "any");
+
+	/* Check each key node */
+	fdt_for_each_subnode(key_node, key_blob, keys_node) {
+		const char *required_prop;
+		bool required;
+		int ret;
+
+		required_prop = fdt_getprop(key_blob, key_node, FIT_KEY_REQUIRED,
+					    NULL);
+		required = required_prop && !strcmp(required_prop, "fdt");
+
+		if (required)
+			reqd_sigs++;
+
+		ret = fdt_verify_sig(blob, fdt_sigs, key_blob, key_node);
+		if (!ret && required)
+			verified++;
+	}
+
+	if (reqd_policy_all && reqd_sigs != verified) {
+		fprintf(stderr, "Failed to verify all required signature\n");
+		return -EPERM;
+	} else if (reqd_sigs && !verified) {
+		fprintf(stderr, "Failed to verify 'any' of the required signature(s)\n");
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+int fdt_sig_verify(const void *blob, int fdt_sigs, const void *key)
+{
+	return fdt_verify_required_sigs(blob, fdt_sigs, key);
+}
-- 
2.34.0.rc1.387.gb447b232ab-goog



More information about the U-Boot mailing list