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

Signed-off-by: Simon Glass <s...@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 <s...@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 <s...@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

Reply via email to