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(¶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 <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, ®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