
After checking hashes, also check signatures of FIT images.
Signed-off-by: Simon Glass sjg@chromium.org --- common/image-fit.c | 80 ++++++++++++++++++++-------- common/image-sig.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++ include/image.h | 37 +++++++++++++ 3 files changed, 243 insertions(+), 22 deletions(-)
diff --git a/common/image-fit.c b/common/image-fit.c index ed98460..d255595 100644 --- a/common/image-fit.c +++ b/common/image-fit.c @@ -31,6 +31,8 @@ #include <time.h> #else #include <common.h> +#include <errno.h> +DECLARE_GLOBAL_DATA_PTR; #endif /* !USE_HOSTCC*/
#include <bootstage.h> @@ -235,42 +237,42 @@ void fit_print_contents(const void *fit) * @fit: pointer to the FIT format image header * @noffset: offset of the hash node * @p: pointer to prefix string + * @type: Type of information to print ("hash" or "sign") * * fit_image_print_data() lists properies for the processed hash node * * returns: * no returned results */ -static void fit_image_print_data(const void *fit, int noffset, const char *p) +static void fit_image_print_data(const void *fit, int noffset, const char *p, + const char *type) { - char *algo; + const char *keyname; uint8_t *value; int value_len; - int i, ret; - - /* - * Check subnode name, must be equal to "hash". - * Multiple hash nodes require unique unit node - * names, e.g. hash@1, hash@2, etc. - */ - if (strncmp(fit_get_name(fit, noffset, NULL), - FIT_HASH_NODENAME, - strlen(FIT_HASH_NODENAME)) != 0) - return; - - debug("%s Hash node: '%s'\n", p, - fit_get_name(fit, noffset, NULL)); + char *algo; + int required; + int ret, i;
- printf("%s Hash algo: ", p); + debug("%s %s node: '%s'\n", p, type, + fit_get_name(fit, noffset, NULL)); + printf("%s %s algo: ", p, type); if (fit_image_hash_get_algo(fit, noffset, &algo)) { printf("invalid/unsupported\n"); return; } - printf("%s\n", algo); + printf("%s", algo); + keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + required = fdt_getprop(fit, noffset, "required", NULL) != NULL; + if (keyname) + printf(":%s", keyname); + if (required) + printf(" (required)"); + printf("\n");
ret = fit_image_hash_get_value(fit, noffset, &value, &value_len); - printf("%s Hash value: ", p); + printf("%s %s value: ", p, type); if (ret) { printf("unavailable\n"); } else { @@ -279,7 +281,18 @@ static void fit_image_print_data(const void *fit, int noffset, const char *p) printf("\n"); }
- debug("%s Hash len: %d\n", p, value_len); + debug("%s %s len: %d\n", p, type, value_len); + + /* Signatures have a time stamp */ + if (IMAGE_ENABLE_TIMESTAMP && keyname) { + time_t timestamp; + + printf("%s Timestamp: ", p); + if (fit_get_timestamp(fit, noffset, ×tamp)) + printf("unavailable\n"); + else + genimg_print_time(timestamp); + } }
/** @@ -304,8 +317,12 @@ static void fit_image_print_verification_data(const void *fit, int noffset, * names, e.g. hash@1, hash@2, signature@1, signature@2, etc. */ name = fit_get_name(fit, noffset, NULL); - if (!strncmp(name, FIT_HASH_NODENAME, strlen(FIT_HASH_NODENAME))) - fit_image_print_data(fit, noffset, p); + if (!strncmp(name, FIT_HASH_NODENAME, strlen(FIT_HASH_NODENAME))) { + fit_image_print_data(fit, noffset, p, "Hash"); + } else if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + fit_image_print_data(fit, noffset, p, "Sign"); + } }
/** @@ -952,6 +969,8 @@ int fit_image_verify(const void *fit, int image_noffset) int noffset; int ndepth; char *err_msg = ""; + int verify_all = 1; + int ret;
/* Get image data and data length */ if (fit_image_get_data(fit, image_noffset, &data, &size)) { @@ -959,6 +978,14 @@ int fit_image_verify(const void *fit, int image_noffset) return 0; }
+ /* Verify all required signatures */ + if (IMAGE_ENABLE_VERIFY && + fit_image_verify_required_sigs(fit, image_noffset, + data, size, gd_fdt_blob(), &verify_all)) { + err_msg = "Unable to verify required signature"; + goto error; + } + /* Process all hash subnodes of the component image node */ for (ndepth = 0, noffset = fdt_next_subnode(fit, image_noffset, &ndepth); @@ -977,6 +1004,15 @@ int fit_image_verify(const void *fit, int image_noffset) size, &err_msg)) goto error; puts("+ "); + } else if (IMAGE_ENABLE_VERIFY && verify_all && + !strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_image_check_sig(fit, noffset, data, + size, -1, &err_msg); + if (ret) + puts("- "); + else + puts("+ "); } }
diff --git a/common/image-sig.c b/common/image-sig.c index 841c662..793e7d6 100644 --- a/common/image-sig.c +++ b/common/image-sig.c @@ -22,6 +22,8 @@ #include <time.h> #else #include <common.h> +#include <malloc.h> +DECLARE_GLOBAL_DATA_PTR; #endif /* !USE_HOSTCC*/ #include <errno.h> #include <image.h> @@ -40,3 +42,149 @@ struct image_sig_algo *image_get_sig_algo(const char *name)
return NULL; } + +static int fit_image_setup_verify(struct image_sign_info *info, + const void *fit, int noffset, int required_keynode, + char **err_msgp) +{ + char *algo_name; + + if (fit_image_hash_get_algo(fit, noffset, &algo_name)) { + *err_msgp = "Can't get hash algo property"; + return -1; + } + memset(info, '\0', sizeof(*info)); + info->keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + info->fit = (void *)fit; + info->node_offset = noffset; + info->algo = image_get_sig_algo(algo_name); + info->fdt_blob = gd_fdt_blob(); + info->required_keynode = required_keynode; + printf("%s:%s", algo_name, info->keyname); + + if (!info->algo) { + *err_msgp = "Unknown signature algorithm"; + return -1; + } + + return 0; +} + +int fit_image_check_sig(const void *fit, int noffset, const void *data, + size_t size, int required_keynode, char **err_msgp) +{ + struct image_sign_info info; + struct image_region region; + uint8_t *fit_value; + int fit_value_len; + + *err_msgp = NULL; + if (fit_image_setup_verify(&info, fit, noffset, required_keynode, + err_msgp)) + return -1; + + if (fit_image_hash_get_value(fit, noffset, &fit_value, + &fit_value_len)) { + *err_msgp = "Can't get hash value property"; + return -1; + } + + region.data = data; + region.size = size; + + if (info.algo->verify(&info, ®ion, 1, fit_value, fit_value_len)) { + *err_msgp = "Verification failed"; + return -1; + } + + return 0; +} + +static int fit_image_verify_sig(const void *fit, int image_noffset, + const char *data, size_t size, const void *sig_blob, + int sig_offset) +{ + int noffset; + int ndepth; + char *err_msg = ""; + int verified = 0; + int ret; + + /* Process all hash subnodes of the component image node */ + for (ndepth = 0, + noffset = fdt_next_subnode(fit, image_noffset, &ndepth); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset, &ndepth)) { + const char *name = fit_get_name(fit, noffset, NULL); + + if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_image_check_sig(fit, noffset, data, + size, -1, &err_msg); + if (ret) { + puts("- "); + } else { + puts("+ "); + verified = 1; + break; + } + } + } + + if (noffset == -FDT_ERR_TRUNCATED || noffset == -FDT_ERR_BADSTRUCTURE) { + err_msg = "Corrupted or truncated tree"; + goto error; + } + + return verified ? 0 : -EPERM; + +error: + printf(" error!\n%s for '%s' hash node in '%s' image node\n", + err_msg, fit_get_name(fit, noffset, NULL), + fit_get_name(fit, image_noffset, NULL)); + return -1; +} + +int fit_image_verify_required_sigs(const void *fit, int image_noffset, + const char *data, size_t size, const void *sig_blob, + int *no_sigsp) +{ + int verify_count = 0; + int ndepth, noffset; + int sig_node; + + /* Work out what we need to verify */ + *no_sigsp = 1; + sig_node = fdt_subnode_offset(sig_blob, 0, FIT_SIG_NODENAME); + if (sig_node < 0) { + debug("%s: No signature node found: %s\n", __func__, + fdt_strerror(sig_node)); + return 0; + } + + for (ndepth = 0, noffset = fdt_next_subnode(sig_blob, sig_node, + &ndepth); + noffset >= 0; + noffset = fdt_next_subnode(sig_blob, noffset, + &ndepth)) { + const char *required; + int ret; + + required = fdt_getprop(sig_blob, noffset, "required", NULL); + if (!required || strcmp(required, "image")) + continue; + ret = fit_image_verify_sig(fit, image_noffset, data, size, + sig_blob, noffset); + if (ret) { + printf("Failed to verify required signature '%s'\n", + fit_get_name(sig_blob, noffset, NULL)); + return ret; + } + verify_count++; + } + + if (verify_count) + *no_sigsp = 0; + + return 0; +} diff --git a/include/image.h b/include/image.h index a1072c0..b8aea83 100644 --- a/include/image.h +++ b/include/image.h @@ -765,6 +765,43 @@ struct image_sig_algo { */ struct image_sig_algo *image_get_sig_algo(const char *name);
+/** + * fit_image_verify_required_sigs() - Verify signatures marked as 'required' + * + * @fit: FIT to check + * @image_noffset: Offset of image node to check + * @data: Image data to check + * @size: Size of image data + * @sig_blob: FDT containing public keys + * @no_sigsp: Returns 1 if no signatures were required, and + * therefore nothing was checked. The caller may wish + * to fall back to other mechanisms, or refuse to + * boot. + * @return 0 if all verified ok, <0 on error + */ +int fit_image_verify_required_sigs(const void *fit, int image_noffset, + const char *data, size_t size, const void *sig_blob, + int *no_sigsp); + +/** + * fit_image_check_sig() - Check a single image signature node + * + * @fit: FIT to check + * @noffset: Offset of signature node to check + * @data: Image data to check + * @size: Size of image data + * @required_keynode: Offset in the control FDT of the required key node, + * if any. If this is given, then the image wil not + * pass verification unless that key is used. If this is + * -1 then any signature will do. + * @err_msgp: In the event of an error, this will be pointed to a + * help error string to display to the user. + * @return 0 if all verified ok, <0 on error + */ +int fit_image_check_sig(const void *fit, int noffset, const void *data, + size_t size, int required_keynode, char **err_msgp); + + #ifndef USE_HOSTCC static inline int fit_image_check_target_arch(const void *fdt, int node) {