From patchwork Mon Jan 27 10:27:34 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 240268 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Mon, 27 Jan 2020 19:27:34 +0900 Subject: [PATCH v6 1/7] lib: rsa: decouple rsa from FIT image verification In-Reply-To: <20200127102740.26831-1-takahiro.akashi@linaro.org> References: <20200127102740.26831-1-takahiro.akashi@linaro.org> Message-ID: <20200127102740.26831-2-takahiro.akashi@linaro.org> Introduce new configuration, CONFIG_RSA_VERIFY which will decouple building RSA functions from FIT verification and allow for adding a RSA-based signature verification for other file formats, in particular PE file for UEFI secure boot. Signed-off-by: AKASHI Takahiro Reviewed-by: Simon Glass --- Kconfig | 4 + common/Kconfig | 7 + common/Makefile | 3 +- common/image-fit-sig.c | 417 +++++++++++++++++++++++++++++++++++++++++ common/image-fit.c | 6 +- common/image-sig.c | 396 -------------------------------------- include/image.h | 13 +- lib/rsa/Kconfig | 10 + lib/rsa/Makefile | 2 +- lib/rsa/rsa-verify.c | 78 +++++--- tools/Makefile | 2 +- 11 files changed, 501 insertions(+), 437 deletions(-) create mode 100644 common/image-fit-sig.c diff --git a/Kconfig b/Kconfig index 92fc4fc135a4..63164fbda7ca 100644 --- a/Kconfig +++ b/Kconfig @@ -352,6 +352,8 @@ config FIT_SIGNATURE depends on DM select HASH select RSA + select RSA_VERIFY + select IMAGE_SIGN_INFO help This option enables signature verification of FIT uImages, using a hash signed and verified using RSA. If @@ -432,6 +434,8 @@ config SPL_FIT_SIGNATURE depends on SPL_DM select SPL_FIT select SPL_RSA + select SPL_RSA_VERIFY + select IMAGE_SIGN_INFO config SPL_LOAD_FIT bool "Enable SPL loading U-Boot as a FIT (basic fitImage features)" diff --git a/common/Kconfig b/common/Kconfig index a7c5ba278a65..b6e7b81dd021 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -1044,3 +1044,10 @@ config BLOBLIST_ADDR endmenu source "common/spl/Kconfig" + +config IMAGE_SIGN_INFO + bool + select SHA1 + select SHA256 + help + Enable image_sign_info helper functions. diff --git a/common/Makefile b/common/Makefile index 302d8beaf356..8620b1eab766 100644 --- a/common/Makefile +++ b/common/Makefile @@ -112,7 +112,8 @@ obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o obj-$(CONFIG_$(SPL_TPL_)FIT) += image-fit.o obj-$(CONFIG_$(SPL_)MULTI_DTB_FIT) += boot_fit.o common_fit.o -obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += image-sig.o +obj-$(CONFIG_IMAGE_SIGN_INFO) += image-sig.o +obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += image-fit-sig.o obj-$(CONFIG_IO_TRACE) += iotrace.o obj-y += memsize.o obj-y += stdio.o diff --git a/common/image-fit-sig.c b/common/image-fit-sig.c new file mode 100644 index 000000000000..f6caeb0c5901 --- /dev/null +++ b/common/image-fit-sig.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013, Google Inc. + */ + +#ifdef USE_HOSTCC +#include "mkimage.h" +#include +#else +#include +#include +DECLARE_GLOBAL_DATA_PTR; +#endif /* !USE_HOSTCC*/ +#include +#include +#include + +#define IMAGE_MAX_HASHED_NODES 100 + +#ifdef USE_HOSTCC +void *host_blob; + +void image_set_host_blob(void *blob) +{ + host_blob = blob; +} + +void *image_get_host_blob(void) +{ + return host_blob; +} +#endif + +/** + * fit_region_make_list() - Make a list of image regions + * + * Given a list of fdt_regions, create a list of image_regions. This is a + * simple conversion routine since the FDT and image code use different + * structures. + * + * @fit: FIT image + * @fdt_regions: Pointer to FDT regions + * @count: Number of FDT regions + * @region: Pointer to image regions, which must hold @count records. If + * region is NULL, then (except for an SPL build) the array will be + * allocated. + * @return: Pointer to image regions + */ +struct image_region *fit_region_make_list(const void *fit, + struct fdt_region *fdt_regions, + int count, + struct image_region *region) +{ + int i; + + debug("Hash regions:\n"); + debug("%10s %10s\n", "Offset", "Size"); + + /* + * Use malloc() except in SPL (to save code size). In SPL the caller + * must allocate the array. + */ +#ifndef CONFIG_SPL_BUILD + if (!region) + region = calloc(sizeof(*region), count); +#endif + if (!region) + return NULL; + for (i = 0; i < count; i++) { + debug("%10x %10x\n", fdt_regions[i].offset, + fdt_regions[i].size); + region[i].data = fit + fdt_regions[i].offset; + region[i].size = fdt_regions[i].size; + } + + return region; +} + +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; + const char *padding_name; + + if (fdt_totalsize(fit) > CONFIG_FIT_SIGNATURE_MAX_SIZE) { + *err_msgp = "Total size too large"; + return 1; + } + + if (fit_image_hash_get_algo(fit, noffset, &algo_name)) { + *err_msgp = "Can't get hash algo property"; + return -1; + } + + padding_name = fdt_getprop(fit, noffset, "padding", NULL); + if (!padding_name) + padding_name = RSA_DEFAULT_PADDING_NAME; + + memset(info, '\0', sizeof(*info)); + info->keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + info->fit = (void *)fit; + 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 = gd_fdt_blob(); + info->required_keynode = required_keynode; + printf("%s:%s", algo_name, info->keyname); + + if (!info->checksum || !info->crypto || !info->padding) { + *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.crypto->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; + char *err_msg = ""; + int verified = 0; + int ret; + + /* Process all hash subnodes of the component image node */ + fdt_for_each_subnode(noffset, fit, image_noffset) { + 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 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; + } + + fdt_for_each_subnode(noffset, sig_blob, sig_node) { + 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; +} + +int fit_config_check_sig(const void *fit, int noffset, int required_keynode, + char **err_msgp) +{ + char * const exc_prop[] = {"data"}; + const char *prop, *end, *name; + struct image_sign_info info; + const uint32_t *strings; + uint8_t *fit_value; + int fit_value_len; + int max_regions; + int i, prop_len; + char path[200]; + int count; + + debug("%s: fdt=%p, conf='%s', sig='%s'\n", __func__, gd_fdt_blob(), + fit_get_name(fit, noffset, NULL), + fit_get_name(gd_fdt_blob(), required_keynode, NULL)); + *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; + } + + /* Count the number of strings in the property */ + prop = fdt_getprop(fit, noffset, "hashed-nodes", &prop_len); + end = prop ? prop + prop_len : prop; + for (name = prop, count = 0; name < end; name++) + if (!*name) + count++; + if (!count) { + *err_msgp = "Can't get hashed-nodes property"; + return -1; + } + + if (prop && prop_len > 0 && prop[prop_len - 1] != '\0') { + *err_msgp = "hashed-nodes property must be null-terminated"; + return -1; + } + + /* Add a sanity check here since we are using the stack */ + if (count > IMAGE_MAX_HASHED_NODES) { + *err_msgp = "Number of hashed nodes exceeds maximum"; + return -1; + } + + /* Create a list of node names from those strings */ + char *node_inc[count]; + + debug("Hash nodes (%d):\n", count); + for (name = prop, i = 0; name < end; name += strlen(name) + 1, i++) { + debug(" '%s'\n", name); + node_inc[i] = (char *)name; + } + + /* + * Each node can generate one region for each sub-node. Allow for + * 7 sub-nodes (hash-1, signature-1, etc.) and some extra. + */ + max_regions = 20 + count * 7; + struct fdt_region fdt_regions[max_regions]; + + /* Get a list of regions to hash */ + count = fdt_find_regions(fit, node_inc, count, + exc_prop, ARRAY_SIZE(exc_prop), + fdt_regions, max_regions - 1, + path, sizeof(path), 0); + if (count < 0) { + *err_msgp = "Failed to hash configuration"; + return -1; + } + if (count == 0) { + *err_msgp = "No data to hash"; + return -1; + } + if (count >= max_regions - 1) { + *err_msgp = "Too many hash regions"; + return -1; + } + + /* Add the strings */ + strings = fdt_getprop(fit, noffset, "hashed-strings", NULL); + if (strings) { + /* + * The strings region offset must be a static 0x0. + * This is set in tool/image-host.c + */ + fdt_regions[count].offset = fdt_off_dt_strings(fit); + fdt_regions[count].size = fdt32_to_cpu(strings[1]); + count++; + } + + /* Allocate the region list on the stack */ + struct image_region region[count]; + + fit_region_make_list(fit, fdt_regions, count, region); + if (info.crypto->verify(&info, region, count, fit_value, + fit_value_len)) { + *err_msgp = "Verification failed"; + return -1; + } + + return 0; +} + +static int fit_config_verify_sig(const void *fit, int conf_noffset, + const void *sig_blob, int sig_offset) +{ + int noffset; + char *err_msg = ""; + int verified = 0; + int ret; + + /* Process all hash subnodes of the component conf node */ + fdt_for_each_subnode(noffset, fit, conf_noffset) { + const char *name = fit_get_name(fit, noffset, NULL); + + if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_config_check_sig(fit, noffset, sig_offset, + &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' config node\n", + err_msg, fit_get_name(fit, noffset, NULL), + fit_get_name(fit, conf_noffset, NULL)); + return -1; +} + +int fit_config_verify_required_sigs(const void *fit, int conf_noffset, + const void *sig_blob) +{ + int noffset; + int sig_node; + + /* Work out what we need to verify */ + 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; + } + + fdt_for_each_subnode(noffset, sig_blob, sig_node) { + const char *required; + int ret; + + required = fdt_getprop(sig_blob, noffset, "required", NULL); + if (!required || strcmp(required, "conf")) + continue; + ret = fit_config_verify_sig(fit, conf_noffset, sig_blob, + noffset); + if (ret) { + printf("Failed to verify required signature '%s'\n", + fit_get_name(sig_blob, noffset, NULL)); + return ret; + } + } + + return 0; +} + +int fit_config_verify(const void *fit, int conf_noffset) +{ + return fit_config_verify_required_sigs(fit, conf_noffset, + gd_fdt_blob()); +} diff --git a/common/image-fit.c b/common/image-fit.c index c52f94512088..e67b1cc4b8d1 100644 --- a/common/image-fit.c +++ b/common/image-fit.c @@ -1217,7 +1217,7 @@ int fit_image_verify_with_data(const void *fit, int image_noffset, int ret; /* Verify all required signatures */ - if (IMAGE_ENABLE_VERIFY && + if (FIT_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"; @@ -1239,7 +1239,7 @@ int fit_image_verify_with_data(const void *fit, int image_noffset, &err_msg)) goto error; puts("+ "); - } else if (IMAGE_ENABLE_VERIFY && verify_all && + } else if (FIT_IMAGE_ENABLE_VERIFY && verify_all && !strncmp(name, FIT_SIG_NODENAME, strlen(FIT_SIG_NODENAME))) { ret = fit_image_check_sig(fit, noffset, data, @@ -1871,7 +1871,7 @@ int fit_image_load(bootm_headers_t *images, ulong addr, if (image_type == IH_TYPE_KERNEL) images->fit_uname_cfg = fit_base_uname_config; - if (IMAGE_ENABLE_VERIFY && images->verify) { + if (FIT_IMAGE_ENABLE_VERIFY && images->verify) { puts(" Verifying Hash Integrity ... "); if (fit_config_verify(fit, cfg_noffset)) { puts("Bad Data Hash\n"); diff --git a/common/image-sig.c b/common/image-sig.c index 639a1124504f..84b2c0439cf8 100644 --- a/common/image-sig.c +++ b/common/image-sig.c @@ -17,18 +17,6 @@ DECLARE_GLOBAL_DATA_PTR; #define IMAGE_MAX_HASHED_NODES 100 -#ifdef USE_HOSTCC -void *host_blob; -void image_set_host_blob(void *blob) -{ - host_blob = blob; -} -void *image_get_host_blob(void) -{ - return host_blob; -} -#endif - struct checksum_algo checksum_algos[] = { { .name = "sha1", @@ -162,387 +150,3 @@ struct padding_algo *image_get_padding_algo(const char *name) return NULL; } - -/** - * fit_region_make_list() - Make a list of image regions - * - * Given a list of fdt_regions, create a list of image_regions. This is a - * simple conversion routine since the FDT and image code use different - * structures. - * - * @fit: FIT image - * @fdt_regions: Pointer to FDT regions - * @count: Number of FDT regions - * @region: Pointer to image regions, which must hold @count records. If - * region is NULL, then (except for an SPL build) the array will be - * allocated. - * @return: Pointer to image regions - */ -struct image_region *fit_region_make_list(const void *fit, - struct fdt_region *fdt_regions, int count, - struct image_region *region) -{ - int i; - - debug("Hash regions:\n"); - debug("%10s %10s\n", "Offset", "Size"); - - /* - * Use malloc() except in SPL (to save code size). In SPL the caller - * must allocate the array. - */ -#ifndef CONFIG_SPL_BUILD - if (!region) - region = calloc(sizeof(*region), count); -#endif - if (!region) - return NULL; - for (i = 0; i < count; i++) { - debug("%10x %10x\n", fdt_regions[i].offset, - fdt_regions[i].size); - region[i].data = fit + fdt_regions[i].offset; - region[i].size = fdt_regions[i].size; - } - - return region; -} - -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; - const char *padding_name; - - if (fdt_totalsize(fit) > CONFIG_FIT_SIGNATURE_MAX_SIZE) { - *err_msgp = "Total size too large"; - return 1; - } - - if (fit_image_hash_get_algo(fit, noffset, &algo_name)) { - *err_msgp = "Can't get hash algo property"; - return -1; - } - - padding_name = fdt_getprop(fit, noffset, "padding", NULL); - if (!padding_name) - padding_name = RSA_DEFAULT_PADDING_NAME; - - memset(info, '\0', sizeof(*info)); - info->keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); - info->fit = (void *)fit; - 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 = gd_fdt_blob(); - info->required_keynode = required_keynode; - printf("%s:%s", algo_name, info->keyname); - - if (!info->checksum || !info->crypto || !info->padding) { - *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.crypto->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; - char *err_msg = ""; - int verified = 0; - int ret; - - /* Process all hash subnodes of the component image node */ - fdt_for_each_subnode(noffset, fit, image_noffset) { - 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 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; - } - - fdt_for_each_subnode(noffset, sig_blob, sig_node) { - 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; -} - -int fit_config_check_sig(const void *fit, int noffset, int required_keynode, - char **err_msgp) -{ - char * const exc_prop[] = {"data"}; - const char *prop, *end, *name; - struct image_sign_info info; - const uint32_t *strings; - uint8_t *fit_value; - int fit_value_len; - int max_regions; - int i, prop_len; - char path[200]; - int count; - - debug("%s: fdt=%p, conf='%s', sig='%s'\n", __func__, gd_fdt_blob(), - fit_get_name(fit, noffset, NULL), - fit_get_name(gd_fdt_blob(), required_keynode, NULL)); - *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; - } - - /* Count the number of strings in the property */ - prop = fdt_getprop(fit, noffset, "hashed-nodes", &prop_len); - end = prop ? prop + prop_len : prop; - for (name = prop, count = 0; name < end; name++) - if (!*name) - count++; - if (!count) { - *err_msgp = "Can't get hashed-nodes property"; - return -1; - } - - if (prop && prop_len > 0 && prop[prop_len - 1] != '\0') { - *err_msgp = "hashed-nodes property must be null-terminated"; - return -1; - } - - /* Add a sanity check here since we are using the stack */ - if (count > IMAGE_MAX_HASHED_NODES) { - *err_msgp = "Number of hashed nodes exceeds maximum"; - return -1; - } - - /* Create a list of node names from those strings */ - char *node_inc[count]; - - debug("Hash nodes (%d):\n", count); - for (name = prop, i = 0; name < end; name += strlen(name) + 1, i++) { - debug(" '%s'\n", name); - node_inc[i] = (char *)name; - } - - /* - * Each node can generate one region for each sub-node. Allow for - * 7 sub-nodes (hash-1, signature-1, etc.) and some extra. - */ - max_regions = 20 + count * 7; - struct fdt_region fdt_regions[max_regions]; - - /* Get a list of regions to hash */ - count = fdt_find_regions(fit, node_inc, count, - exc_prop, ARRAY_SIZE(exc_prop), - fdt_regions, max_regions - 1, - path, sizeof(path), 0); - if (count < 0) { - *err_msgp = "Failed to hash configuration"; - return -1; - } - if (count == 0) { - *err_msgp = "No data to hash"; - return -1; - } - if (count >= max_regions - 1) { - *err_msgp = "Too many hash regions"; - return -1; - } - - /* Add the strings */ - strings = fdt_getprop(fit, noffset, "hashed-strings", NULL); - if (strings) { - /* - * The strings region offset must be a static 0x0. - * This is set in tool/image-host.c - */ - fdt_regions[count].offset = fdt_off_dt_strings(fit); - fdt_regions[count].size = fdt32_to_cpu(strings[1]); - count++; - } - - /* Allocate the region list on the stack */ - struct image_region region[count]; - - fit_region_make_list(fit, fdt_regions, count, region); - if (info.crypto->verify(&info, region, count, fit_value, - fit_value_len)) { - *err_msgp = "Verification failed"; - return -1; - } - - return 0; -} - -static int fit_config_verify_sig(const void *fit, int conf_noffset, - const void *sig_blob, int sig_offset) -{ - int noffset; - char *err_msg = ""; - int verified = 0; - int ret; - - /* Process all hash subnodes of the component conf node */ - fdt_for_each_subnode(noffset, fit, conf_noffset) { - const char *name = fit_get_name(fit, noffset, NULL); - - if (!strncmp(name, FIT_SIG_NODENAME, - strlen(FIT_SIG_NODENAME))) { - ret = fit_config_check_sig(fit, noffset, sig_offset, - &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' config node\n", - err_msg, fit_get_name(fit, noffset, NULL), - fit_get_name(fit, conf_noffset, NULL)); - return -1; -} - -int fit_config_verify_required_sigs(const void *fit, int conf_noffset, - const void *sig_blob) -{ - int noffset; - int sig_node; - - /* Work out what we need to verify */ - 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; - } - - fdt_for_each_subnode(noffset, sig_blob, sig_node) { - const char *required; - int ret; - - required = fdt_getprop(sig_blob, noffset, "required", NULL); - if (!required || strcmp(required, "conf")) - continue; - ret = fit_config_verify_sig(fit, conf_noffset, sig_blob, - noffset); - if (ret) { - printf("Failed to verify required signature '%s'\n", - fit_get_name(sig_blob, noffset, NULL)); - return ret; - } - } - - return 0; -} - -int fit_config_verify(const void *fit, int conf_noffset) -{ - return fit_config_verify_required_sigs(fit, conf_noffset, - gd_fdt_blob()); -} diff --git a/include/image.h b/include/image.h index f4d2aaf53e87..7eb0b4b53184 100644 --- a/include/image.h +++ b/include/image.h @@ -1086,6 +1086,7 @@ int fit_conf_get_prop_node(const void *fit, int noffset, int fit_check_ramdisk(const void *fit, int os_noffset, uint8_t arch, int verify); +#endif /* IMAGE_ENABLE_FIT */ int calculate_hash(const void *data, int data_len, const char *algo, uint8_t *value, int *value_len); @@ -1098,16 +1099,20 @@ int calculate_hash(const void *data, int data_len, const char *algo, # if defined(CONFIG_FIT_SIGNATURE) # define IMAGE_ENABLE_SIGN 1 # define IMAGE_ENABLE_VERIFY 1 +# define FIT_IMAGE_ENABLE_VERIFY 1 # include # else # define IMAGE_ENABLE_SIGN 0 # define IMAGE_ENABLE_VERIFY 0 +# define FIT_IMAGE_ENABLE_VERIFY 0 # endif #else # define IMAGE_ENABLE_SIGN 0 -# define IMAGE_ENABLE_VERIFY CONFIG_IS_ENABLED(FIT_SIGNATURE) +# define IMAGE_ENABLE_VERIFY CONFIG_IS_ENABLED(RSA_VERIFY) +# define FIT_IMAGE_ENABLE_VERIFY CONFIG_IS_ENABLED(FIT_SIGNATURE) #endif +#if IMAGE_ENABLE_FIT #ifdef USE_HOSTCC void *image_get_host_blob(void); void image_set_host_blob(void *host_blob); @@ -1121,6 +1126,7 @@ void image_set_host_blob(void *host_blob); #else #define IMAGE_ENABLE_BEST_MATCH 0 #endif +#endif /* IMAGE_ENABLE_FIT */ /* Information passed to the signing routines */ struct image_sign_info { @@ -1137,7 +1143,6 @@ struct image_sign_info { const char *require_keys; /* Value for 'required' property */ const char *engine_id; /* Engine to use for signing */ }; -#endif /* Allow struct image_region to always be defined for rsa.h */ /* A part of an image, used for hashing */ struct image_region { @@ -1145,8 +1150,6 @@ struct image_region { int size; }; -#if IMAGE_ENABLE_FIT - #if IMAGE_ENABLE_VERIFY # include #endif @@ -1247,6 +1250,8 @@ struct crypto_algo *image_get_crypto_algo(const char *full_name); */ struct padding_algo *image_get_padding_algo(const char *name); +#if IMAGE_ENABLE_FIT + /** * fit_image_verify_required_sigs() - Verify signatures marked as 'required' * diff --git a/lib/rsa/Kconfig b/lib/rsa/Kconfig index 2b33f323bccc..18a075c17478 100644 --- a/lib/rsa/Kconfig +++ b/lib/rsa/Kconfig @@ -18,6 +18,16 @@ if RSA config SPL_RSA bool "Use RSA Library within SPL" +config SPL_RSA_VERIFY + bool + help + Add RSA signature verification support in SPL. + +config RSA_VERIFY + bool + help + Add RSA signature verification support. + config RSA_SOFTWARE_EXP bool "Enable driver for RSA Modular Exponentiation in software" depends on DM diff --git a/lib/rsa/Makefile b/lib/rsa/Makefile index a51c6e1685fb..c07305188e0c 100644 --- a/lib/rsa/Makefile +++ b/lib/rsa/Makefile @@ -5,5 +5,5 @@ # (C) Copyright 2000-2007 # Wolfgang Denk, DENX Software Engineering, wd at denx.de. -obj-$(CONFIG_$(SPL_)FIT_SIGNATURE) += rsa-verify.o rsa-checksum.o +obj-$(CONFIG_$(SPL_)RSA_VERIFY) += rsa-verify.o rsa-checksum.o obj-$(CONFIG_RSA_SOFTWARE_EXP) += rsa-mod-exp.o diff --git a/lib/rsa/rsa-verify.c b/lib/rsa/rsa-verify.c index 82dc513260e2..d2fd0692fa13 100644 --- a/lib/rsa/rsa-verify.c +++ b/lib/rsa/rsa-verify.c @@ -270,6 +270,7 @@ out: } #endif +#if CONFIG_IS_ENABLED(FIT_SIGNATURE) /** * rsa_verify_key() - Verify a signature against some data using RSA Key * @@ -341,7 +342,9 @@ static int rsa_verify_key(struct image_sign_info *info, return 0; } +#endif +#if CONFIG_IS_ENABLED(FIT_SIGNATURE) /** * rsa_verify_with_keynode() - Verify a signature against some data using * information in node with prperties of RSA Key like modulus, exponent etc. @@ -395,18 +398,22 @@ static int rsa_verify_with_keynode(struct image_sign_info *info, return ret; } +#else +static int rsa_verify_with_keynode(struct image_sign_info *info, + const void *hash, uint8_t *sig, + uint sig_len, int node) +{ + return -EACCES; +} +#endif int rsa_verify(struct image_sign_info *info, const struct image_region region[], int region_count, uint8_t *sig, uint sig_len) { - const void *blob = info->fdt_blob; /* Reserve memory for maximum checksum-length */ uint8_t hash[info->crypto->key_len]; - int ndepth, noffset; - int sig_node, node; - char name[100]; - int ret; + int ret = -EACCES; /* * Verify that the checksum-length does not exceed the @@ -419,12 +426,6 @@ int rsa_verify(struct image_sign_info *info, return -EINVAL; } - sig_node = fdt_subnode_offset(blob, 0, FIT_SIG_NODENAME); - if (sig_node < 0) { - debug("%s: No signature node found\n", __func__); - return -ENOENT; - } - /* Calculate checksum with checksum-algorithm */ ret = info->checksum->calculate(info->checksum->name, region, region_count, hash); @@ -433,29 +434,44 @@ int rsa_verify(struct image_sign_info *info, return -EINVAL; } - /* See if we must use a particular key */ - if (info->required_keynode != -1) { - ret = rsa_verify_with_keynode(info, hash, sig, sig_len, - info->required_keynode); - return ret; - } + if (CONFIG_IS_ENABLED(FIT_SIGNATURE)) { + const void *blob = info->fdt_blob; + int ndepth, noffset; + int sig_node, node; + char name[100]; - /* Look for a key that matches our hint */ - snprintf(name, sizeof(name), "key-%s", info->keyname); - node = fdt_subnode_offset(blob, sig_node, name); - ret = rsa_verify_with_keynode(info, hash, sig, sig_len, node); - if (!ret) - return ret; + sig_node = fdt_subnode_offset(blob, 0, FIT_SIG_NODENAME); + if (sig_node < 0) { + debug("%s: No signature node found\n", __func__); + return -ENOENT; + } - /* No luck, so try each of the keys in turn */ - for (ndepth = 0, noffset = fdt_next_node(info->fit, sig_node, &ndepth); - (noffset >= 0) && (ndepth > 0); - noffset = fdt_next_node(info->fit, noffset, &ndepth)) { - if (ndepth == 1 && noffset != node) { + /* See if we must use a particular key */ + if (info->required_keynode != -1) { ret = rsa_verify_with_keynode(info, hash, sig, sig_len, - noffset); - if (!ret) - break; + info->required_keynode); + return ret; + } + + /* Look for a key that matches our hint */ + snprintf(name, sizeof(name), "key-%s", info->keyname); + node = fdt_subnode_offset(blob, sig_node, name); + ret = rsa_verify_with_keynode(info, hash, sig, sig_len, node); + if (!ret) + return ret; + + /* No luck, so try each of the keys in turn */ + for (ndepth = 0, noffset = fdt_next_node(info->fit, sig_node, + &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(info->fit, noffset, &ndepth)) { + if (ndepth == 1 && noffset != node) { + ret = rsa_verify_with_keynode(info, hash, + sig, sig_len, + noffset); + if (!ret) + break; + } } } diff --git a/tools/Makefile b/tools/Makefile index 345bc84e48db..1f885cca7034 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -58,7 +58,7 @@ hostprogs-$(CONFIG_FIT_SIGNATURE) += fit_info fit_check_sign hostprogs-$(CONFIG_CMD_BOOTEFI_SELFTEST) += file2include FIT_OBJS-$(CONFIG_FIT) := fit_common.o fit_image.o image-host.o common/image-fit.o -FIT_SIG_OBJS-$(CONFIG_FIT_SIGNATURE) := common/image-sig.o +FIT_SIG_OBJS-$(CONFIG_FIT_SIGNATURE) := common/image-sig.o common/image-fit-sig.o # The following files are synced with upstream DTC. # Use synced versions from scripts/dtc/libfdt/. From patchwork Mon Jan 27 10:27:35 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 240267 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Mon, 27 Jan 2020 19:27:35 +0900 Subject: [PATCH v6 2/7] rsa: add CONFIG_RSA_VERIFY_WITH_PKEY config In-Reply-To: <20200127102740.26831-1-takahiro.akashi@linaro.org> References: <20200127102740.26831-1-takahiro.akashi@linaro.org> Message-ID: <20200127102740.26831-3-takahiro.akashi@linaro.org> In the next couple of commits, under new CONFIG_RSA_VERIFY_WITH_PKEY, rsa_verify() will be extended to be able to perform RSA decryption without additional RSA key properties from FIT image, i.e. rr and n0inv. Signed-off-by: AKASHI Takahiro Reviewed-by: Simon Glass --- lib/rsa/Kconfig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/rsa/Kconfig b/lib/rsa/Kconfig index 18a075c17478..89697219db2d 100644 --- a/lib/rsa/Kconfig +++ b/lib/rsa/Kconfig @@ -28,6 +28,20 @@ config RSA_VERIFY help Add RSA signature verification support. +config RSA_VERIFY_WITH_PKEY + bool "Execute RSA verification without key parameters from FDT" + select RSA_VERIFY + help + The standard RSA-signature verification code (FIT_SIGNATURE) uses + pre-calculated key properties, that are stored in fdt blob, in + decrypting a signature. + This does not suit the use case where there is no way defined to + provide such additional key properties in standardized form, + particularly UEFI secure boot. + This options enables RSA signature verification with a public key + directly specified in image_sign_info, where all the necessary + key properties will be calculated on the fly in verification code. + config RSA_SOFTWARE_EXP bool "Enable driver for RSA Modular Exponentiation in software" depends on DM From patchwork Mon Jan 27 10:27:36 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 240269 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Mon, 27 Jan 2020 19:27:36 +0900 Subject: [PATCH v6 3/7] include: image.h: add key info to image_sign_info In-Reply-To: <20200127102740.26831-1-takahiro.akashi@linaro.org> References: <20200127102740.26831-1-takahiro.akashi@linaro.org> Message-ID: <20200127102740.26831-4-takahiro.akashi@linaro.org> For FIT verification, all the properties of a public key come from "control fdt" pointed to by fdt_blob. In UEFI secure boot, on the other hand, a public key is located and retrieved from dedicated signature database stored as UEFI variables. Added two fields may hold values of a public key if fdt_blob is NULL, and will be used in rsa_verify_with_pkey() to verify a signature in UEFI sub-system. Signed-off-by: AKASHI Takahiro Reviewed-by: Simon Glass --- include/image.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/image.h b/include/image.h index 7eb0b4b53184..20e1f95cf2a7 100644 --- a/include/image.h +++ b/include/image.h @@ -1142,6 +1142,13 @@ struct image_sign_info { int required_keynode; /* Node offset of key to use: -1=any */ const char *require_keys; /* Value for 'required' property */ const char *engine_id; /* Engine to use for signing */ + /* + * Note: the following two fields are always valid even w/o + * RSA_VERIFY_WITH_PKEY in order to make sure this structure is + * the same on target and host. Otherwise, vboot test may fail. + */ + const void *key; /* Pointer to public key in DER */ + int keylen; /* Length of public key */ }; /* A part of an image, used for hashing */ From patchwork Mon Jan 27 10:27:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 240270 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Mon, 27 Jan 2020 19:27:37 +0900 Subject: [PATCH v6 4/7] lib: rsa: generate additional parameters for public key In-Reply-To: <20200127102740.26831-1-takahiro.akashi@linaro.org> References: <20200127102740.26831-1-takahiro.akashi@linaro.org> Message-ID: <20200127102740.26831-5-takahiro.akashi@linaro.org> In the current implementation of FIT_SIGNATURE, five parameters for a RSA public key are required while only two of them are essential. (See rsa-mod-exp.h and uImage.FIT/signature.txt) This is a result of considering relatively limited computer power and resources on embedded systems, while such a assumption may not be quite practical for other use cases. In this patch, added is a function, rsa_gen_key_prop(), which will generate additional parameters for other uses, in particular UEFI secure boot, on the fly. Note: the current code uses some "big number" routines from BearSSL for the calculation. Signed-off-by: AKASHI Takahiro --- include/u-boot/rsa-mod-exp.h | 23 ++ lib/rsa/Kconfig | 3 + lib/rsa/Makefile | 1 + lib/rsa/rsa-keyprop.c | 725 +++++++++++++++++++++++++++++++++++ 4 files changed, 752 insertions(+) create mode 100644 lib/rsa/rsa-keyprop.c diff --git a/include/u-boot/rsa-mod-exp.h b/include/u-boot/rsa-mod-exp.h index 8a428c4b6a1a..1da8af1bb83d 100644 --- a/include/u-boot/rsa-mod-exp.h +++ b/include/u-boot/rsa-mod-exp.h @@ -26,6 +26,29 @@ struct key_prop { uint32_t exp_len; /* Exponent length in number of uint8_t */ }; +/** + * rsa_gen_key_prop() - Generate key properties of RSA public key + * @key: Specifies key data in DER format + * @keylen: Length of @key + * @prop: Generated key property + * + * This function takes a blob of encoded RSA public key data in DER + * format, parse it and generate all the relevant properties + * in key_prop structure. + * Return a pointer to struct key_prop in @prop on success. + * + * Return: 0 on success, negative on error + */ +int rsa_gen_key_prop(const void *key, uint32_t keylen, struct key_prop **proc); + +/** + * rsa_free_key_prop() - Free key properties + * @prop: Pointer to struct key_prop + * + * This function frees all the memories allocated by rsa_gen_key_prop(). + */ +void rsa_free_key_prop(struct key_prop *prop); + /** * rsa_mod_exp_sw() - Perform RSA Modular Exponentiation in sw * diff --git a/lib/rsa/Kconfig b/lib/rsa/Kconfig index 89697219db2d..a90d67e5a87b 100644 --- a/lib/rsa/Kconfig +++ b/lib/rsa/Kconfig @@ -31,6 +31,9 @@ config RSA_VERIFY config RSA_VERIFY_WITH_PKEY bool "Execute RSA verification without key parameters from FDT" select RSA_VERIFY + select ASYMMETRIC_KEY_TYPE + select ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select RSA_PUBLIC_KEY_PARSER help The standard RSA-signature verification code (FIT_SIGNATURE) uses pre-calculated key properties, that are stored in fdt blob, in diff --git a/lib/rsa/Makefile b/lib/rsa/Makefile index c07305188e0c..14ed3cb4012b 100644 --- a/lib/rsa/Makefile +++ b/lib/rsa/Makefile @@ -6,4 +6,5 @@ # Wolfgang Denk, DENX Software Engineering, wd at denx.de. obj-$(CONFIG_$(SPL_)RSA_VERIFY) += rsa-verify.o rsa-checksum.o +obj-$(CONFIG_RSA_VERIFY_WITH_PKEY) += rsa-keyprop.o obj-$(CONFIG_RSA_SOFTWARE_EXP) += rsa-mod-exp.o diff --git a/lib/rsa/rsa-keyprop.c b/lib/rsa/rsa-keyprop.c new file mode 100644 index 000000000000..9464df009343 --- /dev/null +++ b/lib/rsa/rsa-keyprop.c @@ -0,0 +1,725 @@ +// SPDX-License-Identifier: GPL-2.0+ and MIT +/* + * RSA library - generate parameters for a public key + * + * Copyright (c) 2019 Linaro Limited + * Author: AKASHI Takahiro + * + * Big number routines in this file come from BearSSL: + * Copyright (c) 2016 Thomas Pornin + */ + +#include +#include +#include +#include +#include +#include + +/** + * br_dec16be() - Convert 16-bit big-endian integer to native + * @src: Pointer to data + * Return: Native-endian integer + */ +static unsigned br_dec16be(const void *src) +{ + return be16_to_cpup(src); +} + +/** + * br_dec32be() - Convert 32-bit big-endian integer to native + * @src: Pointer to data + * Return: Native-endian integer + */ +static uint32_t br_dec32be(const void *src) +{ + return be32_to_cpup(src); +} + +/** + * br_enc32be() - Convert native 32-bit integer to big-endian + * @dst: Pointer to buffer to store big-endian integer in + * @x: Native 32-bit integer + */ +static void br_enc32be(void *dst, uint32_t x) +{ + __be32 tmp; + + tmp = cpu_to_be32(x); + memcpy(dst, &tmp, sizeof(tmp)); +} + +/* from BearSSL's src/inner.h */ + +/* + * Negate a boolean. + */ +static uint32_t NOT(uint32_t ctl) +{ + return ctl ^ 1; +} + +/* + * Multiplexer: returns x if ctl == 1, y if ctl == 0. + */ +static uint32_t MUX(uint32_t ctl, uint32_t x, uint32_t y) +{ + return y ^ (-ctl & (x ^ y)); +} + +/* + * Equality check: returns 1 if x == y, 0 otherwise. + */ +static uint32_t EQ(uint32_t x, uint32_t y) +{ + uint32_t q; + + q = x ^ y; + return NOT((q | -q) >> 31); +} + +/* + * Inequality check: returns 1 if x != y, 0 otherwise. + */ +static uint32_t NEQ(uint32_t x, uint32_t y) +{ + uint32_t q; + + q = x ^ y; + return (q | -q) >> 31; +} + +/* + * Comparison: returns 1 if x > y, 0 otherwise. + */ +static uint32_t GT(uint32_t x, uint32_t y) +{ + /* + * If both x < 2^31 and y < 2^31, then y-x will have its high + * bit set if x > y, cleared otherwise. + * + * If either x >= 2^31 or y >= 2^31 (but not both), then the + * result is the high bit of x. + * + * If both x >= 2^31 and y >= 2^31, then we can virtually + * subtract 2^31 from both, and we are back to the first case. + * Since (y-2^31)-(x-2^31) = y-x, the subtraction is already + * fine. + */ + uint32_t z; + + z = y - x; + return (z ^ ((x ^ y) & (x ^ z))) >> 31; +} + +/* + * Compute the bit length of a 32-bit integer. Returned value is between 0 + * and 32 (inclusive). + */ +static uint32_t BIT_LENGTH(uint32_t x) +{ + uint32_t k, c; + + k = NEQ(x, 0); + c = GT(x, 0xFFFF); x = MUX(c, x >> 16, x); k += c << 4; + c = GT(x, 0x00FF); x = MUX(c, x >> 8, x); k += c << 3; + c = GT(x, 0x000F); x = MUX(c, x >> 4, x); k += c << 2; + c = GT(x, 0x0003); x = MUX(c, x >> 2, x); k += c << 1; + k += GT(x, 0x0001); + return k; +} + +#define GE(x, y) NOT(GT(y, x)) +#define LT(x, y) GT(y, x) +#define MUL(x, y) ((uint64_t)(x) * (uint64_t)(y)) + +/* + * Integers 'i32' + * -------------- + * + * The 'i32' functions implement computations on big integers using + * an internal representation as an array of 32-bit integers. For + * an array x[]: + * -- x[0] contains the "announced bit length" of the integer + * -- x[1], x[2]... contain the value in little-endian order (x[1] + * contains the least significant 32 bits) + * + * Multiplications rely on the elementary 32x32->64 multiplication. + * + * The announced bit length specifies the number of bits that are + * significant in the subsequent 32-bit words. Unused bits in the + * last (most significant) word are set to 0; subsequent words are + * uninitialized and need not exist at all. + * + * The execution time and memory access patterns of all computations + * depend on the announced bit length, but not on the actual word + * values. For modular integers, the announced bit length of any integer + * modulo n is equal to the actual bit length of n; thus, computations + * on modular integers are "constant-time" (only the modulus length may + * leak). + */ + +/* + * Extract one word from an integer. The offset is counted in bits. + * The word MUST entirely fit within the word elements corresponding + * to the announced bit length of a[]. + */ +static uint32_t br_i32_word(const uint32_t *a, uint32_t off) +{ + size_t u; + unsigned j; + + u = (size_t)(off >> 5) + 1; + j = (unsigned)off & 31; + if (j == 0) { + return a[u]; + } else { + return (a[u] >> j) | (a[u + 1] << (32 - j)); + } +} + +/* from BearSSL's src/int/i32_bitlen.c */ + +/* + * Compute the actual bit length of an integer. The argument x should + * point to the first (least significant) value word of the integer. + * The len 'xlen' contains the number of 32-bit words to access. + * + * CT: value or length of x does not leak. + */ +static uint32_t br_i32_bit_length(uint32_t *x, size_t xlen) +{ + uint32_t tw, twk; + + tw = 0; + twk = 0; + while (xlen -- > 0) { + uint32_t w, c; + + c = EQ(tw, 0); + w = x[xlen]; + tw = MUX(c, w, tw); + twk = MUX(c, (uint32_t)xlen, twk); + } + return (twk << 5) + BIT_LENGTH(tw); +} + +/* from BearSSL's src/int/i32_decode.c */ + +/* + * Decode an integer from its big-endian unsigned representation. The + * "true" bit length of the integer is computed, but all words of x[] + * corresponding to the full 'len' bytes of the source are set. + * + * CT: value or length of x does not leak. + */ +static void br_i32_decode(uint32_t *x, const void *src, size_t len) +{ + const unsigned char *buf; + size_t u, v; + + buf = src; + u = len; + v = 1; + for (;;) { + if (u < 4) { + uint32_t w; + + if (u < 2) { + if (u == 0) { + break; + } else { + w = buf[0]; + } + } else { + if (u == 2) { + w = br_dec16be(buf); + } else { + w = ((uint32_t)buf[0] << 16) + | br_dec16be(buf + 1); + } + } + x[v ++] = w; + break; + } else { + u -= 4; + x[v ++] = br_dec32be(buf + u); + } + } + x[0] = br_i32_bit_length(x + 1, v - 1); +} + +/* from BearSSL's src/int/i32_encode.c */ + +/* + * Encode an integer into its big-endian unsigned representation. The + * output length in bytes is provided (parameter 'len'); if the length + * is too short then the integer is appropriately truncated; if it is + * too long then the extra bytes are set to 0. + */ +static void br_i32_encode(void *dst, size_t len, const uint32_t *x) +{ + unsigned char *buf; + size_t k; + + buf = dst; + + /* + * Compute the announced size of x in bytes; extra bytes are + * filled with zeros. + */ + k = (x[0] + 7) >> 3; + while (len > k) { + *buf ++ = 0; + len --; + } + + /* + * Now we use k as index within x[]. That index starts at 1; + * we initialize it to the topmost complete word, and process + * any remaining incomplete word. + */ + k = (len + 3) >> 2; + switch (len & 3) { + case 3: + *buf ++ = x[k] >> 16; + /* fall through */ + case 2: + *buf ++ = x[k] >> 8; + /* fall through */ + case 1: + *buf ++ = x[k]; + k --; + } + + /* + * Encode all complete words. + */ + while (k > 0) { + br_enc32be(buf, x[k]); + k --; + buf += 4; + } +} + +/* from BearSSL's src/int/i32_ninv32.c */ + +/* + * Compute -(1/x) mod 2^32. If x is even, then this function returns 0. + */ +static uint32_t br_i32_ninv32(uint32_t x) +{ + uint32_t y; + + y = 2 - x; + y *= 2 - y * x; + y *= 2 - y * x; + y *= 2 - y * x; + y *= 2 - y * x; + return MUX(x & 1, -y, 0); +} + +/* from BearSSL's src/int/i32_add.c */ + +/* + * Add b[] to a[] and return the carry (0 or 1). If ctl is 0, then a[] + * is unmodified, but the carry is still computed and returned. The + * arrays a[] and b[] MUST have the same announced bit length. + * + * a[] and b[] MAY be the same array, but partial overlap is not allowed. + */ +static uint32_t br_i32_add(uint32_t *a, const uint32_t *b, uint32_t ctl) +{ + uint32_t cc; + size_t u, m; + + cc = 0; + m = (a[0] + 63) >> 5; + for (u = 1; u < m; u ++) { + uint32_t aw, bw, naw; + + aw = a[u]; + bw = b[u]; + naw = aw + bw + cc; + + /* + * Carry is 1 if naw < aw. Carry is also 1 if naw == aw + * AND the carry was already 1. + */ + cc = (cc & EQ(naw, aw)) | LT(naw, aw); + a[u] = MUX(ctl, naw, aw); + } + return cc; +} + +/* from BearSSL's src/int/i32_sub.c */ + +/* + * Subtract b[] from a[] and return the carry (0 or 1). If ctl is 0, + * then a[] is unmodified, but the carry is still computed and returned. + * The arrays a[] and b[] MUST have the same announced bit length. + * + * a[] and b[] MAY be the same array, but partial overlap is not allowed. + */ +static uint32_t br_i32_sub(uint32_t *a, const uint32_t *b, uint32_t ctl) +{ + uint32_t cc; + size_t u, m; + + cc = 0; + m = (a[0] + 63) >> 5; + for (u = 1; u < m; u ++) { + uint32_t aw, bw, naw; + + aw = a[u]; + bw = b[u]; + naw = aw - bw - cc; + + /* + * Carry is 1 if naw > aw. Carry is 1 also if naw == aw + * AND the carry was already 1. + */ + cc = (cc & EQ(naw, aw)) | GT(naw, aw); + a[u] = MUX(ctl, naw, aw); + } + return cc; +} + +/* from BearSSL's src/int/i32_div32.c */ + +/* + * Constant-time division. The dividend hi:lo is divided by the + * divisor d; the quotient is returned and the remainder is written + * in *r. If hi == d, then the quotient does not fit on 32 bits; + * returned value is thus truncated. If hi > d, returned values are + * indeterminate. + */ +static uint32_t br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r) +{ + /* TODO: optimize this */ + uint32_t q; + uint32_t ch, cf; + int k; + + q = 0; + ch = EQ(hi, d); + hi = MUX(ch, 0, hi); + for (k = 31; k > 0; k --) { + int j; + uint32_t w, ctl, hi2, lo2; + + j = 32 - k; + w = (hi << j) | (lo >> k); + ctl = GE(w, d) | (hi >> k); + hi2 = (w - d) >> j; + lo2 = lo - (d << k); + hi = MUX(ctl, hi2, hi); + lo = MUX(ctl, lo2, lo); + q |= ctl << k; + } + cf = GE(lo, d) | hi; + q |= cf; + *r = MUX(cf, lo - d, lo); + return q; +} + +/* + * Wrapper for br_divrem(); the remainder is returned, and the quotient + * is discarded. + */ +static uint32_t br_rem(uint32_t hi, uint32_t lo, uint32_t d) +{ + uint32_t r; + + br_divrem(hi, lo, d, &r); + return r; +} + +/* + * Wrapper for br_divrem(); the quotient is returned, and the remainder + * is discarded. + */ +static uint32_t br_div(uint32_t hi, uint32_t lo, uint32_t d) +{ + uint32_t r; + + return br_divrem(hi, lo, d, &r); +} + +/* from BearSSL's src/int/i32_muladd.c */ + +/* + * Multiply x[] by 2^32 and then add integer z, modulo m[]. This + * function assumes that x[] and m[] have the same announced bit + * length, and the announced bit length of m[] matches its true + * bit length. + * + * x[] and m[] MUST be distinct arrays. + * + * CT: only the common announced bit length of x and m leaks, not + * the values of x, z or m. + */ +static void br_i32_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m) +{ + uint32_t m_bitlen; + size_t u, mlen; + uint32_t a0, a1, b0, hi, g, q, tb; + uint32_t chf, clow, under, over; + uint64_t cc; + + /* + * We can test on the modulus bit length since we accept to + * leak that length. + */ + m_bitlen = m[0]; + if (m_bitlen == 0) { + return; + } + if (m_bitlen <= 32) { + x[1] = br_rem(x[1], z, m[1]); + return; + } + mlen = (m_bitlen + 31) >> 5; + + /* + * Principle: we estimate the quotient (x*2^32+z)/m by + * doing a 64/32 division with the high words. + * + * Let: + * w = 2^32 + * a = (w*a0 + a1) * w^N + a2 + * b = b0 * w^N + b2 + * such that: + * 0 <= a0 < w + * 0 <= a1 < w + * 0 <= a2 < w^N + * w/2 <= b0 < w + * 0 <= b2 < w^N + * a < w*b + * I.e. the two top words of a are a0:a1, the top word of b is + * b0, we ensured that b0 is "full" (high bit set), and a is + * such that the quotient q = a/b fits on one word (0 <= q < w). + * + * If a = b*q + r (with 0 <= r < q), we can estimate q by + * doing an Euclidean division on the top words: + * a0*w+a1 = b0*u + v (with 0 <= v < w) + * Then the following holds: + * 0 <= u <= w + * u-2 <= q <= u + */ + a0 = br_i32_word(x, m_bitlen - 32); + hi = x[mlen]; + memmove(x + 2, x + 1, (mlen - 1) * sizeof *x); + x[1] = z; + a1 = br_i32_word(x, m_bitlen - 32); + b0 = br_i32_word(m, m_bitlen - 32); + + /* + * We estimate a divisor q. If the quotient returned by br_div() + * is g: + * -- If a0 == b0 then g == 0; we want q = 0xFFFFFFFF. + * -- Otherwise: + * -- if g == 0 then we set q = 0; + * -- otherwise, we set q = g - 1. + * The properties described above then ensure that the true + * quotient is q-1, q or q+1. + */ + g = br_div(a0, a1, b0); + q = MUX(EQ(a0, b0), 0xFFFFFFFF, MUX(EQ(g, 0), 0, g - 1)); + + /* + * We subtract q*m from x (with the extra high word of value 'hi'). + * Since q may be off by 1 (in either direction), we may have to + * add or subtract m afterwards. + * + * The 'tb' flag will be true (1) at the end of the loop if the + * result is greater than or equal to the modulus (not counting + * 'hi' or the carry). + */ + cc = 0; + tb = 1; + for (u = 1; u <= mlen; u ++) { + uint32_t mw, zw, xw, nxw; + uint64_t zl; + + mw = m[u]; + zl = MUL(mw, q) + cc; + cc = (uint32_t)(zl >> 32); + zw = (uint32_t)zl; + xw = x[u]; + nxw = xw - zw; + cc += (uint64_t)GT(nxw, xw); + x[u] = nxw; + tb = MUX(EQ(nxw, mw), tb, GT(nxw, mw)); + } + + /* + * If we underestimated q, then either cc < hi (one extra bit + * beyond the top array word), or cc == hi and tb is true (no + * extra bit, but the result is not lower than the modulus). In + * these cases we must subtract m once. + * + * Otherwise, we may have overestimated, which will show as + * cc > hi (thus a negative result). Correction is adding m once. + */ + chf = (uint32_t)(cc >> 32); + clow = (uint32_t)cc; + over = chf | GT(clow, hi); + under = ~over & (tb | (~chf & LT(clow, hi))); + br_i32_add(x, m, over); + br_i32_sub(x, m, under); +} + +/* from BearSSL's src/int/i32_reduce.c */ + +/* + * Reduce an integer (a[]) modulo another (m[]). The result is written + * in x[] and its announced bit length is set to be equal to that of m[]. + * + * x[] MUST be distinct from a[] and m[]. + * + * CT: only announced bit lengths leak, not values of x, a or m. + */ +static void br_i32_reduce(uint32_t *x, const uint32_t *a, const uint32_t *m) +{ + uint32_t m_bitlen, a_bitlen; + size_t mlen, alen, u; + + m_bitlen = m[0]; + mlen = (m_bitlen + 31) >> 5; + + x[0] = m_bitlen; + if (m_bitlen == 0) { + return; + } + + /* + * If the source is shorter, then simply copy all words from a[] + * and zero out the upper words. + */ + a_bitlen = a[0]; + alen = (a_bitlen + 31) >> 5; + if (a_bitlen < m_bitlen) { + memcpy(x + 1, a + 1, alen * sizeof *a); + for (u = alen; u < mlen; u ++) { + x[u + 1] = 0; + } + return; + } + + /* + * The source length is at least equal to that of the modulus. + * We must thus copy N-1 words, and input the remaining words + * one by one. + */ + memcpy(x + 1, a + 2 + (alen - mlen), (mlen - 1) * sizeof *a); + x[mlen] = 0; + for (u = 1 + alen - mlen; u > 0; u --) { + br_i32_muladd_small(x, a[u], m); + } +} + +/** + * rsa_free_key_prop() - Free key properties + * @prop: Pointer to struct key_prop + * + * This function frees all the memories allocated by rsa_gen_key_prop(). + */ +void rsa_free_key_prop(struct key_prop *prop) +{ + if (!prop) + return; + + free((void *)prop->modulus); + free((void *)prop->public_exponent); + free((void *)prop->rr); + + free(prop); +} + +/** + * rsa_gen_key_prop() - Generate key properties of RSA public key + * @key: Specifies key data in DER format + * @keylen: Length of @key + * @prop: Generated key property + * + * This function takes a blob of encoded RSA public key data in DER + * format, parse it and generate all the relevant properties + * in key_prop structure. + * Return a pointer to struct key_prop in @prop on success. + * + * Return: 0 on success, negative on error + */ +int rsa_gen_key_prop(const void *key, uint32_t keylen, struct key_prop **prop) +{ + struct rsa_key rsa_key; + uint32_t *n = NULL, *rr = NULL, *rrtmp = NULL; + const int max_rsa_size = 4096; + int rlen, i, ret; + + *prop = calloc(sizeof(**prop), 1); + n = calloc(sizeof(uint32_t), 1 + (max_rsa_size >> 5)); + rr = calloc(sizeof(uint32_t), 1 + (max_rsa_size >> 5)); + rrtmp = calloc(sizeof(uint32_t), 1 + (max_rsa_size >> 5)); + if (!(*prop) || !n || !rr || !rrtmp) { + ret = -ENOMEM; + goto err; + } + + ret = rsa_parse_pub_key(&rsa_key, key, keylen); + if (ret) + goto err; + + /* modulus */ + /* removing leading 0's */ + for (i = 0; i < rsa_key.n_sz && !rsa_key.n[i]; i++) + ; + (*prop)->num_bits = (rsa_key.n_sz - i) * 8; + (*prop)->modulus = malloc(rsa_key.n_sz - i); + if (!(*prop)->modulus) { + ret = -ENOMEM; + goto err; + } + memcpy((void *)(*prop)->modulus, &rsa_key.n[i], rsa_key.n_sz - i); + + /* exponent */ + (*prop)->public_exponent = calloc(1, sizeof(uint64_t)); + if (!(*prop)->public_exponent) { + ret = -ENOMEM; + goto err; + } + memcpy((void *)(*prop)->public_exponent + sizeof(uint64_t) + - rsa_key.e_sz, + rsa_key.e, rsa_key.e_sz); + (*prop)->exp_len = rsa_key.e_sz; + + /* n0 inverse */ + br_i32_decode(n, &rsa_key.n[i], rsa_key.n_sz - i); + (*prop)->n0inv = br_i32_ninv32(n[1]); + + /* R^2 mod n; R = 2^(num_bits) */ + rlen = (*prop)->num_bits * 2; /* #bits of R^2 = (2^num_bits)^2 */ + rr[0] = 0; + *(uint8_t *)&rr[0] = (1 << (rlen % 8)); + for (i = 1; i < (((rlen + 31) >> 5) + 1); i++) + rr[i] = 0; + br_i32_decode(rrtmp, rr, ((rlen + 7) >> 3) + 1); + br_i32_reduce(rr, rrtmp, n); + + rlen = ((*prop)->num_bits + 7) >> 3; /* #bytes of R^2 mod n */ + (*prop)->rr = malloc(rlen); + if (!(*prop)->rr) { + ret = -ENOMEM; + goto err; + } + br_i32_encode((void *)(*prop)->rr, rlen, rr); + + return 0; + +err: + free(n); + free(rr); + free(rrtmp); + rsa_free_key_prop(*prop); + return ret; +} From patchwork Mon Jan 27 10:27:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 240273 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Mon, 27 Jan 2020 19:27:38 +0900 Subject: [PATCH v6 5/7] lib: rsa: add rsa_verify_with_pkey() In-Reply-To: <20200127102740.26831-1-takahiro.akashi@linaro.org> References: <20200127102740.26831-1-takahiro.akashi@linaro.org> Message-ID: <20200127102740.26831-6-takahiro.akashi@linaro.org> This function, and hence rsa_verify(), will perform RSA verification with two essential parameters for a RSA public key in contract of rsa_verify_with_keynode(), which requires additional three parameters stored in FIT image. It will be used in implementing UEFI secure boot, i.e. image authentication and variable authentication. Signed-off-by: AKASHI Takahiro Reviewed-by: Simon Glass --- lib/rsa/rsa-verify.c | 65 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/lib/rsa/rsa-verify.c b/lib/rsa/rsa-verify.c index d2fd0692fa13..1ea02c02dab3 100644 --- a/lib/rsa/rsa-verify.c +++ b/lib/rsa/rsa-verify.c @@ -17,9 +17,22 @@ #include "mkimage.h" #include #endif +#include #include #include +#ifndef __UBOOT__ +/* + * NOTE: + * Since host tools, like mkimage, make use of openssl library for + * RSA encryption, rsa_verify_with_pkey()/rsa_gen_key_prop() are + * of no use and should not be compiled in. + * So just turn off CONFIG_RSA_VERIFY_WITH_PKEY. + */ + +#undef CONFIG_RSA_VERIFY_WITH_PKEY +#endif + /* Default public exponent for backward compatibility */ #define RSA_DEFAULT_PUBEXP 65537 @@ -270,7 +283,7 @@ out: } #endif -#if CONFIG_IS_ENABLED(FIT_SIGNATURE) +#if CONFIG_IS_ENABLED(FIT_SIGNATURE) || IS_ENABLED(CONFIG_RSA_VERIFY_WITH_PKEY) /** * rsa_verify_key() - Verify a signature against some data using RSA Key * @@ -344,6 +357,49 @@ static int rsa_verify_key(struct image_sign_info *info, } #endif +#ifdef CONFIG_RSA_VERIFY_WITH_PKEY +/** + * rsa_verify_with_pkey() - Verify a signature against some data using + * only modulus and exponent as RSA key properties. + * @info: Specifies key information + * @hash: Pointer to the expected hash + * @sig: Signature + * @sig_len: Number of bytes in signature + * + * Parse a RSA public key blob in DER format pointed to in @info and fill + * a key_prop structure with properties of the key. Then verify a RSA PKCS1.5 + * signature against an expected hash using the calculated properties. + * + * Return 0 if verified, -ve on error + */ +static int rsa_verify_with_pkey(struct image_sign_info *info, + const void *hash, uint8_t *sig, uint sig_len) +{ + struct key_prop *prop; + int ret; + + /* Public key is self-described to fill key_prop */ + ret = rsa_gen_key_prop(info->key, info->keylen, &prop); + if (ret) { + debug("Generating necessary parameter for decoding failed\n"); + return ret; + } + + ret = rsa_verify_key(info, prop, sig, sig_len, hash, + info->crypto->key_len); + + rsa_free_key_prop(prop); + + return ret; +} +#else +static int rsa_verify_with_pkey(struct image_sign_info *info, + const void *hash, uint8_t *sig, uint sig_len) +{ + return -EACCES; +} +#endif + #if CONFIG_IS_ENABLED(FIT_SIGNATURE) /** * rsa_verify_with_keynode() - Verify a signature against some data using @@ -434,6 +490,13 @@ int rsa_verify(struct image_sign_info *info, return -EINVAL; } + if (IS_ENABLED(CONFIG_RSA_VERIFY_WITH_PKEY) && !info->fdt_blob) { + /* don't rely on fdt properties */ + ret = rsa_verify_with_pkey(info, hash, sig, sig_len); + + return ret; + } + if (CONFIG_IS_ENABLED(FIT_SIGNATURE)) { const void *blob = info->fdt_blob; int ndepth, noffset; From patchwork Mon Jan 27 10:27:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 240271 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Mon, 27 Jan 2020 19:27:39 +0900 Subject: [PATCH v6 6/7] test: add rsa_verify() unit test In-Reply-To: <20200127102740.26831-1-takahiro.akashi@linaro.org> References: <20200127102740.26831-1-takahiro.akashi@linaro.org> Message-ID: <20200127102740.26831-7-takahiro.akashi@linaro.org> In this patch, a very simple test is added to verify that rsa_verify() using rsa_verify_with_pkey() work correctly. To keep the code simple, all the test data, either public key and verified binary data, are embedded in the source. Signed-off-by: AKASHI Takahiro --- test/Kconfig | 10 +++ test/lib/Makefile | 1 + test/lib/rsa.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 test/lib/rsa.c diff --git a/test/Kconfig b/test/Kconfig index cb51b4672102..0157e0b060ad 100644 --- a/test/Kconfig +++ b/test/Kconfig @@ -28,6 +28,16 @@ config UT_LIB_ASN1 Enables a test which exercises asn1 compiler and decoder function via various parsers. +config UT_LIB_RSA + bool "Unit test for rsa_verify() function" + depends on RSA + depends on RSA_VERIFY_WITH_PKEY + select IMAGE_SIGN_INFO + default y + help + Enables rsa_verify() test, currently rsa_verify_with_pkey only() + only, at the 'ut lib' command. + endif config UT_TIME diff --git a/test/lib/Makefile b/test/lib/Makefile index 72d2ec74b5f4..2bf6ef3935bb 100644 --- a/test/lib/Makefile +++ b/test/lib/Makefile @@ -8,3 +8,4 @@ obj-y += lmb.o obj-y += string.o obj-$(CONFIG_ERRNO_STR) += test_errno_str.o obj-$(CONFIG_UT_LIB_ASN1) += asn1.o +obj-$(CONFIG_UT_LIB_RSA) += rsa.o diff --git a/test/lib/rsa.c b/test/lib/rsa.c new file mode 100644 index 000000000000..44f8ade226f4 --- /dev/null +++ b/test/lib/rsa.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 Linaro Limited + * Author: AKASHI Takahiro + * + * Unit test for rsa_verify() function + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_RSA_VERIFY_WITH_PKEY +/* + * openssl genrsa 2048 -out private.pem + * openssl rsa -in private.pem -pubout -outform der -out public.der + * dd if=public.der of=public.raw bs=24 skip=1 + */ +static unsigned char public_key[] = { + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xca, 0x25, 0x23, + 0xe0, 0x0a, 0x4d, 0x8f, 0x56, 0xfc, 0xc9, 0x06, 0x4c, 0xcc, 0x94, 0x43, + 0xe0, 0x56, 0x44, 0x6e, 0x37, 0x54, 0x87, 0x12, 0x84, 0xf9, 0x07, 0x4f, + 0xe4, 0x23, 0x40, 0xc3, 0x43, 0x84, 0x37, 0x86, 0xd3, 0x9d, 0x95, 0x1c, + 0xe4, 0x8a, 0x66, 0x02, 0x09, 0xe2, 0x3d, 0xce, 0x2c, 0xc6, 0x02, 0x6a, + 0xd4, 0x65, 0x61, 0xff, 0x85, 0x6f, 0x88, 0x63, 0xba, 0x31, 0x62, 0x1e, + 0xb7, 0x95, 0xe9, 0x08, 0x3c, 0xe9, 0x35, 0xde, 0xfd, 0x65, 0x92, 0xb8, + 0x9e, 0x71, 0xa4, 0xcd, 0x47, 0xfd, 0x04, 0x26, 0xb9, 0x78, 0xbf, 0x05, + 0x0d, 0xfc, 0x00, 0x84, 0x08, 0xfc, 0xc4, 0x4b, 0xea, 0xf5, 0x97, 0x68, + 0x0d, 0x97, 0xd7, 0xff, 0x4f, 0x92, 0x82, 0xd7, 0xbb, 0xef, 0xb7, 0x67, + 0x8e, 0x72, 0x54, 0xe8, 0xc5, 0x9e, 0xfd, 0xd8, 0x38, 0xe9, 0xbe, 0x19, + 0x37, 0x5b, 0x36, 0x8b, 0xbf, 0x49, 0xa1, 0x59, 0x3a, 0x9d, 0xad, 0x92, + 0x08, 0x0b, 0xe3, 0xa4, 0xa4, 0x7d, 0xd3, 0x70, 0xc0, 0xb8, 0xfb, 0xc7, + 0xda, 0xd3, 0x19, 0x86, 0x37, 0x9a, 0xcd, 0xab, 0x30, 0x96, 0xab, 0xa4, + 0xa2, 0x31, 0xa0, 0x38, 0xfb, 0xbf, 0x85, 0xd3, 0x24, 0x39, 0xed, 0xbf, + 0xe1, 0x31, 0xed, 0x6c, 0x39, 0xc1, 0xe5, 0x05, 0x2e, 0x12, 0x30, 0x36, + 0x73, 0x5d, 0x62, 0xf3, 0x82, 0xaf, 0x38, 0xc8, 0xca, 0xfa, 0xa1, 0x99, + 0x57, 0x3c, 0xe1, 0xc1, 0x7b, 0x05, 0x0b, 0xcc, 0x2e, 0xa9, 0x10, 0xc8, + 0x68, 0xbd, 0x27, 0xb6, 0x19, 0x9c, 0xd2, 0xad, 0xb3, 0x1f, 0xca, 0x35, + 0x6e, 0x84, 0x23, 0xa1, 0xe9, 0xa4, 0x4c, 0xab, 0x19, 0x09, 0x79, 0x6e, + 0x3c, 0x7b, 0x74, 0xfc, 0x33, 0x05, 0xcf, 0xa4, 0x2e, 0xeb, 0x55, 0x60, + 0x05, 0xc7, 0xcf, 0x3f, 0x92, 0xac, 0x2d, 0x69, 0x0b, 0x19, 0x16, 0x79, + 0x75, 0x02, 0x03, 0x01, 0x00, 0x01 +}; + +static unsigned int public_key_len = 270; + +/* + * dd if=/dev/urandom of=data.raw bs=512 count=1 + */ +static unsigned char data_raw[] = { + 0x3e, 0x48, 0x6e, 0xef, 0x83, 0xd1, 0x4c, 0xfd, 0x92, 0x47, 0x92, 0xd7, + 0xf6, 0x16, 0x25, 0x0a, 0xdf, 0xe2, 0xb6, 0x6c, 0xe7, 0xe0, 0x55, 0xb2, + 0x70, 0x66, 0xf0, 0xe5, 0xdc, 0xaf, 0xd3, 0x2e, 0xc1, 0x3e, 0x5c, 0x4b, + 0xb5, 0xa7, 0x23, 0x1f, 0x2c, 0xce, 0xf8, 0x83, 0x00, 0x6d, 0xeb, 0xdd, + 0x19, 0x71, 0x13, 0xb4, 0xae, 0x5c, 0xa8, 0xae, 0x52, 0xc8, 0xe1, 0x77, + 0x9e, 0x98, 0x75, 0xbc, 0xef, 0x36, 0x9f, 0x0c, 0x14, 0xed, 0x1a, 0x0a, + 0x4f, 0x6c, 0xa4, 0xb1, 0xbb, 0x0e, 0x43, 0x93, 0x12, 0xfc, 0x2e, 0x82, + 0x93, 0x4e, 0xcb, 0xa2, 0xcd, 0x59, 0x3f, 0xc5, 0x11, 0x38, 0x3a, 0x88, + 0xc3, 0xcf, 0xf9, 0x61, 0xa8, 0x9e, 0x96, 0xb6, 0xbf, 0xa6, 0x5b, 0x0d, + 0xd9, 0xbd, 0x05, 0x4c, 0xbe, 0xed, 0x86, 0xca, 0x10, 0x63, 0x72, 0x75, + 0x4b, 0xbd, 0x86, 0x42, 0x30, 0x9d, 0x54, 0x4e, 0x12, 0xda, 0xf4, 0xb4, + 0xfd, 0xd9, 0x54, 0x95, 0x8f, 0x83, 0xc2, 0x63, 0x44, 0xdd, 0x96, 0x1a, + 0xd0, 0x7c, 0xcf, 0xcb, 0x16, 0xd6, 0xff, 0xa3, 0xbb, 0xeb, 0x24, 0x06, + 0xbf, 0x81, 0xd0, 0x29, 0x76, 0x19, 0x66, 0x84, 0xfc, 0x49, 0xde, 0x7b, + 0x5d, 0xd2, 0x27, 0x58, 0x21, 0x7b, 0xff, 0x4d, 0x64, 0xf3, 0x89, 0xe3, + 0xea, 0xb6, 0x54, 0x4e, 0xb1, 0x62, 0x52, 0x89, 0xe3, 0x22, 0xf2, 0x26, + 0x3e, 0x4f, 0x43, 0x58, 0x78, 0x91, 0x55, 0xbc, 0x1e, 0xd6, 0x97, 0xfc, + 0x0b, 0x85, 0x4c, 0x92, 0x9c, 0xbf, 0xc4, 0xb1, 0x62, 0x93, 0x27, 0xa9, + 0xb2, 0xf4, 0xb4, 0x7a, 0xfb, 0x56, 0xe5, 0x8f, 0xe1, 0x94, 0x4d, 0xfd, + 0xe4, 0x72, 0x8d, 0xa9, 0x71, 0x65, 0xcb, 0x2e, 0x6d, 0x39, 0xd5, 0x95, + 0xe7, 0x3f, 0xab, 0xaa, 0x7a, 0x74, 0x84, 0x25, 0x4b, 0x42, 0x1e, 0xd3, + 0x86, 0xca, 0x47, 0x4a, 0xf0, 0x24, 0x81, 0x24, 0xb0, 0xe1, 0xbb, 0x6c, + 0x3f, 0x2a, 0xa0, 0xb8, 0xeb, 0xd6, 0x01, 0xce, 0x63, 0x51, 0xe1, 0x81, + 0xd2, 0x32, 0x43, 0x56, 0x44, 0x4a, 0x6b, 0x51, 0x24, 0xa2, 0xc7, 0x39, + 0x7c, 0x54, 0xda, 0xf8, 0xd4, 0x93, 0x7c, 0x8e, 0x4e, 0x9d, 0x15, 0x08, + 0xce, 0x27, 0xd8, 0x28, 0xb0, 0x5b, 0x75, 0x32, 0x43, 0xe8, 0xd6, 0xbf, + 0x12, 0xd5, 0xc5, 0x12, 0x8e, 0xeb, 0x77, 0x8f, 0x00, 0xde, 0x45, 0x1e, + 0xdd, 0xf3, 0xef, 0x43, 0x99, 0x79, 0x86, 0xea, 0x01, 0xce, 0xf2, 0x4d, + 0xa0, 0xfe, 0x5a, 0x55, 0xc0, 0x1f, 0xce, 0xe8, 0xbe, 0xc2, 0x66, 0xdb, + 0xcb, 0x3f, 0xa5, 0x48, 0xa1, 0xe2, 0x49, 0xa1, 0x29, 0x65, 0x5b, 0x62, + 0x39, 0xcc, 0xef, 0xbe, 0x86, 0xb7, 0xe3, 0x44, 0x67, 0x04, 0x04, 0xb1, + 0xec, 0xd8, 0xb2, 0xb2, 0x38, 0xbc, 0x10, 0xea, 0x7a, 0x0e, 0xa4, 0xa4, + 0xcb, 0x21, 0xd9, 0xc7, 0xb4, 0x0b, 0xb8, 0x39, 0xb4, 0x07, 0x53, 0x3f, + 0xb9, 0x55, 0x55, 0xa1, 0x6f, 0x11, 0x49, 0xc0, 0x94, 0x77, 0xaf, 0x76, + 0x97, 0x7f, 0x31, 0x08, 0xdd, 0x72, 0x48, 0x72, 0xf8, 0x11, 0x4f, 0x69, + 0x10, 0xef, 0x23, 0x06, 0xf3, 0x34, 0xac, 0xee, 0x97, 0x89, 0x41, 0x1c, + 0x36, 0x38, 0xb1, 0x80, 0x96, 0x7a, 0x9e, 0x72, 0xab, 0x25, 0xeb, 0xce, + 0x7b, 0xb8, 0x5d, 0xc8, 0xef, 0xa4, 0x73, 0xa1, 0xa6, 0x8f, 0x01, 0x54, + 0xce, 0x58, 0x19, 0xe5, 0x7e, 0xfa, 0x77, 0x08, 0x9d, 0x53, 0xc1, 0xcc, + 0x08, 0xe8, 0x1d, 0xe0, 0x82, 0x5e, 0xe1, 0xe6, 0xbd, 0xbb, 0x59, 0x7e, + 0x12, 0x9c, 0x39, 0x60, 0x23, 0xf7, 0xbe, 0x0a, 0x7c, 0x48, 0x12, 0xa0, + 0x84, 0x04, 0x3f, 0xa1, 0x6e, 0x92, 0xcd, 0xa0, 0xac, 0xee, 0x0b, 0xbc, + 0x18, 0x30, 0x28, 0xbd, 0xf5, 0xfa, 0x3a, 0x35 +}; + +static unsigned int data_raw_len = 512; + +/* + * openssl dgst -sha256 -sign private.key -out data.enc data.raw + */ +unsigned char data_enc[] = { + 0xa7, 0x4a, 0x12, 0x8f, 0xee, 0x65, 0x4b, 0xcd, 0x88, 0xca, 0x4d, 0xed, + 0xe3, 0x04, 0xe7, 0x7c, 0x59, 0xbf, 0x2f, 0xad, 0x95, 0x73, 0x5b, 0x2c, + 0x4e, 0xb5, 0xda, 0x5e, 0x3a, 0x6d, 0xb4, 0xc5, 0x84, 0x0c, 0xd2, 0x4a, + 0x62, 0x0d, 0x5f, 0xba, 0x10, 0xee, 0xb1, 0x2a, 0xe1, 0xfe, 0x50, 0x18, + 0x97, 0xcc, 0xea, 0x26, 0x62, 0x33, 0x5a, 0x1d, 0x51, 0x38, 0x52, 0x89, + 0x4d, 0xa7, 0x18, 0xff, 0xa6, 0xc8, 0xd4, 0x7a, 0xc0, 0xa6, 0x22, 0xdf, + 0x41, 0x89, 0x93, 0x9b, 0xe7, 0x9e, 0xc1, 0xc8, 0x80, 0xda, 0x1a, 0x3f, + 0xa4, 0x7a, 0xd0, 0x07, 0xcb, 0x5c, 0xa4, 0x75, 0x12, 0x54, 0x78, 0x67, + 0xbf, 0xe6, 0xae, 0x1e, 0x56, 0x33, 0x9e, 0xe0, 0x6e, 0x33, 0xa7, 0x58, + 0xb0, 0x47, 0x49, 0xa8, 0x37, 0xdb, 0x82, 0x4b, 0xbd, 0x32, 0x9c, 0xdc, + 0xf4, 0x67, 0x17, 0x24, 0x55, 0xfd, 0x83, 0x1e, 0xc8, 0xb4, 0x5c, 0xf9, + 0x15, 0x6c, 0x5e, 0xaa, 0x72, 0x03, 0x9e, 0x7c, 0x17, 0xf5, 0x7c, 0x37, + 0x96, 0x00, 0xb0, 0xd8, 0xaa, 0x05, 0xfa, 0xaa, 0xa1, 0x78, 0x77, 0xd5, + 0x09, 0xdd, 0x05, 0xc7, 0xe2, 0x9f, 0x68, 0xc7, 0xf8, 0xfb, 0x0b, 0x6f, + 0x18, 0x1e, 0xcc, 0x93, 0xd3, 0x3f, 0xc9, 0x26, 0x29, 0x64, 0xe7, 0x15, + 0xdc, 0xb8, 0x19, 0x10, 0x24, 0x55, 0x55, 0x3b, 0x79, 0xa1, 0x65, 0x12, + 0xe0, 0x0b, 0x88, 0x44, 0x4c, 0xea, 0x85, 0x5a, 0x6b, 0x2d, 0x45, 0x6e, + 0xe7, 0x83, 0x6f, 0x3a, 0xaa, 0x1e, 0xf1, 0x9c, 0x8f, 0xdc, 0xb9, 0x37, + 0xa2, 0x15, 0x61, 0x93, 0x06, 0x23, 0xf5, 0xfe, 0xf0, 0xf8, 0x2b, 0xf7, + 0xc0, 0x68, 0x67, 0xf6, 0x4e, 0x08, 0x0d, 0x0d, 0x08, 0xbe, 0xfb, 0x2c, + 0x4c, 0xe7, 0xd7, 0x1a, 0xad, 0xd9, 0x98, 0xa1, 0x8d, 0x94, 0x1c, 0xd1, + 0x89, 0x06, 0xc9, 0x3a +}; + +static unsigned int data_enc_len = 256; + +/** + * lib_rsa_verify_valid() - unit test for rsa_verify() + * + * Test rsa_verify() with valid hash + * + * @uts: unit test state + * Return: 0 = success, 1 = failure + */ +static int lib_rsa_verify_valid(struct unit_test_state *uts) +{ + struct image_sign_info info; + struct image_region reg; + int ret; + + memset(&info, '\0', sizeof(info)); + info.name = "sha256,rsa2048"; + info.padding = image_get_padding_algo("pkcs-1.5"); + info.checksum = image_get_checksum_algo("sha256,rsa2048"); + info.crypto = image_get_crypto_algo(info.name); + + info.key = public_key; + info.keylen = public_key_len; + + reg.data = data_raw; + reg.size = data_raw_len; + ret = rsa_verify(&info, ®, 1, data_enc, data_enc_len); + ut_assertf(ret == 0, "verification unexpectedly failed (%d)\n", ret); + + return CMD_RET_SUCCESS; +} + +LIB_TEST(lib_rsa_verify_valid, 0); + +/** + * lib_rsa_verify_invalid() - unit test for rsa_verify() + * + * Test rsa_verify() with invalid hash + * + * @uts: unit test state + * Return: 0 = success, 1 = failure + */ +static int lib_rsa_verify_invalid(struct unit_test_state *uts) +{ + struct image_sign_info info; + struct image_region reg; + unsigned char ctmp; + int ret; + + memset(&info, '\0', sizeof(info)); + info.name = "sha256,rsa2048"; + info.padding = image_get_padding_algo("pkcs-1.5"); + info.checksum = image_get_checksum_algo("sha256,rsa2048"); + info.crypto = image_get_crypto_algo(info.name); + + info.key = public_key; + info.keylen = public_key_len; + + /* randomly corrupt enc'ed data */ + ctmp = data_enc[data_enc_len - 10]; + data_enc[data_enc_len - 10] = 0x12; + + reg.data = data_raw; + reg.size = data_raw_len; + ret = rsa_verify(&info, ®, 1, data_enc, data_enc_len); + + /* revert a change */ + data_enc[data_enc_len - 10] = ctmp; + + ut_assertf(ret != 0, "verification unexpectedly succeeded\n"); + + return CMD_RET_SUCCESS; +} + +LIB_TEST(lib_rsa_verify_invalid, 0); +#endif /* RSA_VERIFY_WITH_PKEY */ From patchwork Mon Jan 27 10:27:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 240272 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Mon, 27 Jan 2020 19:27:40 +0900 Subject: [PATCH v6 7/7] test: enable RSA library test on sandbox In-Reply-To: <20200127102740.26831-1-takahiro.akashi@linaro.org> References: <20200127102740.26831-1-takahiro.akashi@linaro.org> Message-ID: <20200127102740.26831-8-takahiro.akashi@linaro.org> We want to always run RSA library test on sandbox build in Travis CI. Just adding CONFIG_RSA_VERIFY_WITH_PKEY would be good enough for this purpose. Signed-off-by: AKASHI Takahiro --- configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + configs/sandbox_flattree_defconfig | 1 + configs/sandbox_spl_defconfig | 1 + 4 files changed, 4 insertions(+) diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index cc536ff0ba38..add56a1d1580 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -196,6 +196,7 @@ CONFIG_WDT_SANDBOX=y CONFIG_FS_CBFS=y CONFIG_FS_CRAMFS=y CONFIG_CMD_DHRYSTONE=y +CONFIG_RSA_VERIFY_WITH_PKEY=y CONFIG_TPM=y CONFIG_LZ4=y CONFIG_ERRNO_STR=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 64245f7cdc4b..63bde7ec23a2 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -223,6 +223,7 @@ CONFIG_WDT_SANDBOX=y CONFIG_FS_CBFS=y CONFIG_FS_CRAMFS=y CONFIG_CMD_DHRYSTONE=y +CONFIG_RSA_VERIFY_WITH_PKEY=y CONFIG_TPM=y CONFIG_LZ4=y CONFIG_ERRNO_STR=y diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig index bb31b00904a4..1a7abf224b37 100644 --- a/configs/sandbox_flattree_defconfig +++ b/configs/sandbox_flattree_defconfig @@ -173,6 +173,7 @@ CONFIG_VIDEO_SANDBOX_SDL=y CONFIG_OSD=y CONFIG_SANDBOX_OSD=y CONFIG_CMD_DHRYSTONE=y +CONFIG_RSA_VERIFY_WITH_PKEY=y CONFIG_TPM=y CONFIG_LZ4=y CONFIG_ERRNO_STR=y diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index 61a8ffdef88a..c6c82c468133 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -196,6 +196,7 @@ CONFIG_FS_CBFS=y CONFIG_FS_CRAMFS=y # CONFIG_SPL_USE_TINY_PRINTF is not set CONFIG_CMD_DHRYSTONE=y +CONFIG_RSA_VERIFY_WITH_PKEY=y CONFIG_TPM=y CONFIG_LZ4=y CONFIG_ERRNO_STR=y