[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(¶ms, '\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, ®ion, ®ion_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