[U-Boot] [PATCH 1/4] [RFC] [v2] rsa: Split the rsa-verify

Public exponentiation which is required in rsa verify functionality is currently tightly integrated with verification code in rsa_verify.c. Currently this implementation is software based. Some platforms having support of the exponentiation in hardware. To enable the rsa verify functionality to use the Modular exponentiation if present in hardware, the patch-set splits the file into two files:
1. rsa-verify.c - The file parses device tree keys node to fill a keyprop structure. The keyprop structure can then be converted to implementation specific formal (struct rsa_pub_key for sw implementation). - The parsed device tree node is then passed to a generic rsa_mod_exp function.
2. rsa-mod-exp.c Move the software specific functions related to exponentiation from rsa-verify.c to this file. The file is compiled if "CONFIG_RSA_MOD_EXP_SW" is defined. In general if both CONFIG_FIT_SIGNATURE and CONFIG_RSA are defined, CONFIG_RSA_MOD_EXP_SW gets automatically defined.
Platforms having hardware implementation for rsa_mod_exp can add a define "CONFIG_RSA_MOD_EXP_HW" to their config files. Adding this defined, undefs the CONFIG_RSA_MOD_EXP_SW and hardware implementation of mod_exp gets compiled.
Signed-off-by: Ruchika Gupta ruchika.gupta@freescale.com CC: Simon Glass sjg@chromium.org --- Changes in v2: Addressed few of Simon Glass's comments: - Kconfig option added for RSA - Comments added for new keyprop struct
Kconfig | 2 + include/config_fallbacks.h | 8 ++ include/u-boot/rsa-mod-exp.h | 34 +++++ lib/Kconfig | 15 +++ lib/rsa/Makefile | 1 + lib/rsa/rsa-mod-exp.c | 308 +++++++++++++++++++++++++++++++++++++++++++ lib/rsa/rsa-verify.c | 307 +++++------------------------------------- tools/Makefile | 2 +- 8 files changed, 399 insertions(+), 278 deletions(-) create mode 100644 include/u-boot/rsa-mod-exp.h create mode 100644 lib/rsa/rsa-mod-exp.c
diff --git a/Kconfig b/Kconfig index 153ee2b..82b14bd 100644 --- a/Kconfig +++ b/Kconfig @@ -101,6 +101,7 @@ config TPL help If you want to build TPL as well as the normal image and SPL, say Y.
+ config FIT bool "Support Flattened Image Tree" depends on !SPL_BUILD @@ -118,6 +119,7 @@ config FIT_VERBOSE config FIT_SIGNATURE bool "Enabel signature verification of FIT uImages" depends on FIT + select RSA help This option enables signature verification of FIT uImages, using a hash signed and verified using RSA. diff --git a/include/config_fallbacks.h b/include/config_fallbacks.h index ddfe045..b46bef9 100644 --- a/include/config_fallbacks.h +++ b/include/config_fallbacks.h @@ -83,6 +83,14 @@ #define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + 128) #endif
+/* This can be removed once all platforms move to defconfig + * option to enable FIT_SIGNATURE + */ +#if defined(CONFIG_FIT_SIGNATURE) && defined(CONFIG_RSA) && \ + !defined(CONFIG_RSA_MOD_EXP_HW) && !defined(CONFIG_RSA_MOD_EXP_SW) +#define CONFIG_RSA_MOD_EXP_SW +#endif + #ifndef CONFIG_FIT_SIGNATURE #define CONFIG_IMAGE_FORMAT_LEGACY #endif diff --git a/include/u-boot/rsa-mod-exp.h b/include/u-boot/rsa-mod-exp.h new file mode 100644 index 0000000..0d2ccd6 --- /dev/null +++ b/include/u-boot/rsa-mod-exp.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014, Ruchika Gupta. + * + * SPDX-License-Identifier: GPL-2.0+ +*/ + +#ifndef _RSA_MOD_EXP_H +#define _RSA_MOD_EXP_H + +#include <errno.h> +#include <image.h> + +/** + * struct key_prop - holder for a public key properties + * + * The struct has pointers to modulus (Typically called N), + * The inverse, R^2, exponent. These can be typecasted and + * used as byte arrays or converted to the required format + * as per requirement of RSA implementation. + * + */ +struct key_prop { + const void *rr; /* R^2 can be treated as byte array */ + const void *modulus; /* modulus as byte array */ + const void *public_exponent; /* public exponent as byte array */ + uint32_t n0inv; /* -1 / modulus[0] mod 2^32 */ + int num_bits; /* Key length in bits */ + uint32_t exp_len; /* Exponent length in number of uint8_t */ +}; + +int rsa_mod_exp(const uint8_t *sig, uint32_t sig_len, + struct key_prop *node, uint8_t *out); + +#endif diff --git a/lib/Kconfig b/lib/Kconfig index 8460439..a921bb3 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -27,4 +27,19 @@ config SYS_HZ get_timer() must operate in milliseconds and this option must be set to 1000.
+config RSA + bool "Use RSA Library" + help + RSA support + +if RSA + +config RSA_MOD_EXP_SW + depends on RSA + bool "Implement RSA Modular Exponentiation in software" + default y + help + Modular Exponentiation support in SW + +endif endmenu diff --git a/lib/rsa/Makefile b/lib/rsa/Makefile index a5a96cb6..ccc6060 100644 --- a/lib/rsa/Makefile +++ b/lib/rsa/Makefile @@ -8,3 +8,4 @@ #
obj-$(CONFIG_FIT_SIGNATURE) += rsa-verify.o rsa-checksum.o +obj-$(CONFIG_RSA_MOD_EXP_SW) += rsa-mod-exp.o diff --git a/lib/rsa/rsa-mod-exp.c b/lib/rsa/rsa-mod-exp.c new file mode 100644 index 0000000..f69ae1b --- /dev/null +++ b/lib/rsa/rsa-mod-exp.c @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2013, Google Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef USE_HOSTCC +#include <common.h> +#include <fdtdec.h> +#include <asm/types.h> +#include <asm/byteorder.h> +#include <asm/errno.h> +#include <asm/types.h> +#include <asm/unaligned.h> +#else +#include "fdt_host.h" +#include "mkimage.h" +#include <fdt_support.h> +#endif +#include <u-boot/rsa.h> +#include <u-boot/rsa-mod-exp.h> + +#define UINT64_MULT32(v, multby) (((uint64_t)(v)) * ((uint32_t)(multby))) + +#define get_unaligned_be32(a) fdt32_to_cpu(*(uint32_t *)a) +#define put_unaligned_be32(a, b) (*(uint32_t *)(b) = cpu_to_fdt32(a)) + +/* Default public exponent for backward compatibility */ +#define RSA_DEFAULT_PUBEXP 65537 + +/** + * subtract_modulus() - subtract modulus from the given value + * + * @key: Key containing modulus to subtract + * @num: Number to subtract modulus from, as little endian word array + */ +static void subtract_modulus(const struct rsa_public_key *key, uint32_t num[]) +{ + int64_t acc = 0; + uint i; + + for (i = 0; i < key->len; i++) { + acc += (uint64_t)num[i] - key->modulus[i]; + num[i] = (uint32_t)acc; + acc >>= 32; + } +} + +/** + * greater_equal_modulus() - check if a value is >= modulus + * + * @key: Key containing modulus to check + * @num: Number to check against modulus, as little endian word array + * @return 0 if num < modulus, 1 if num >= modulus + */ +static int greater_equal_modulus(const struct rsa_public_key *key, + uint32_t num[]) +{ + int i; + + for (i = (int)key->len - 1; i >= 0; i--) { + if (num[i] < key->modulus[i]) + return 0; + if (num[i] > key->modulus[i]) + return 1; + } + + return 1; /* equal */ +} + +/** + * montgomery_mul_add_step() - Perform montgomery multiply-add step + * + * Operation: montgomery result[] += a * b[] / n0inv % modulus + * + * @key: RSA key + * @result: Place to put result, as little endian word array + * @a: Multiplier + * @b: Multiplicand, as little endian word array + */ +static void montgomery_mul_add_step(const struct rsa_public_key *key, + uint32_t result[], const uint32_t a, const uint32_t b[]) +{ + uint64_t acc_a, acc_b; + uint32_t d0; + uint i; + + acc_a = (uint64_t)a * b[0] + result[0]; + d0 = (uint32_t)acc_a * key->n0inv; + acc_b = (uint64_t)d0 * key->modulus[0] + (uint32_t)acc_a; + for (i = 1; i < key->len; i++) { + acc_a = (acc_a >> 32) + (uint64_t)a * b[i] + result[i]; + acc_b = (acc_b >> 32) + (uint64_t)d0 * key->modulus[i] + + (uint32_t)acc_a; + result[i - 1] = (uint32_t)acc_b; + } + + acc_a = (acc_a >> 32) + (acc_b >> 32); + + result[i - 1] = (uint32_t)acc_a; + + if (acc_a >> 32) + subtract_modulus(key, result); +} + +/** + * montgomery_mul() - Perform montgomery mutitply + * + * Operation: montgomery result[] = a[] * b[] / n0inv % modulus + * + * @key: RSA key + * @result: Place to put result, as little endian word array + * @a: Multiplier, as little endian word array + * @b: Multiplicand, as little endian word array + */ +static void montgomery_mul(const struct rsa_public_key *key, + uint32_t result[], uint32_t a[], const uint32_t b[]) +{ + uint i; + + for (i = 0; i < key->len; ++i) + result[i] = 0; + for (i = 0; i < key->len; ++i) + montgomery_mul_add_step(key, result, a[i], b); +} + +/** + * num_pub_exponent_bits() - Number of bits in the public exponent + * + * @key: RSA key + * @num_bits: Storage for the number of public exponent bits + */ +static int num_public_exponent_bits(const struct rsa_public_key *key, + int *num_bits) +{ + uint64_t exponent; + int exponent_bits; + const uint max_bits = (sizeof(exponent) * 8); + + exponent = key->exponent; + exponent_bits = 0; + + if (!exponent) { + *num_bits = exponent_bits; + return 0; + } + + for (exponent_bits = 1; exponent_bits < max_bits + 1; ++exponent_bits) + if (!(exponent >>= 1)) { + *num_bits = exponent_bits; + return 0; + } + + return -EINVAL; +} + +/** + * is_public_exponent_bit_set() - Check if a bit in the public exponent is set + * + * @key: RSA key + * @pos: The bit position to check + */ +static int is_public_exponent_bit_set(const struct rsa_public_key *key, + int pos) +{ + return key->exponent & (1ULL << pos); +} + +/** + * pow_mod() - in-place public exponentiation + * + * @key: RSA key + * @inout: Big-endian word array containing value and result + */ +static int pow_mod(const struct rsa_public_key *key, uint32_t *inout) +{ + uint32_t *result, *ptr; + uint i; + int j, k; + + /* Sanity check for stack size - key->len is in 32-bit words */ + if (key->len > RSA_MAX_KEY_BITS / 32) { + debug("RSA key words %u exceeds maximum %d\n", key->len, + RSA_MAX_KEY_BITS / 32); + return -EINVAL; + } + + uint32_t val[key->len], acc[key->len], tmp[key->len]; + uint32_t a_scaled[key->len]; + result = tmp; /* Re-use location. */ + + /* Convert from big endian byte array to little endian word array. */ + for (i = 0, ptr = inout + key->len - 1; i < key->len; i++, ptr--) + val[i] = get_unaligned_be32(ptr); + + if (0 != num_public_exponent_bits(key, &k)) + return -EINVAL; + + if (k < 2) { + debug("Public exponent is too short (%d bits, minimum 2)\n", + k); + return -EINVAL; + } + + if (!is_public_exponent_bit_set(key, 0)) { + debug("LSB of RSA public exponent must be set.\n"); + return -EINVAL; + } + + /* the bit at e[k-1] is 1 by definition, so start with: C := M */ + montgomery_mul(key, acc, val, key->rr); /* acc = a * RR / R mod n */ + /* retain scaled version for intermediate use */ + memcpy(a_scaled, acc, key->len * sizeof(a_scaled[0])); + + for (j = k - 2; j > 0; --j) { + montgomery_mul(key, tmp, acc, acc); /* tmp = acc^2 / R mod n */ + + if (is_public_exponent_bit_set(key, j)) { + /* acc = tmp * val / R mod n */ + montgomery_mul(key, acc, tmp, a_scaled); + } else { + /* e[j] == 0, copy tmp back to acc for next operation */ + memcpy(acc, tmp, key->len * sizeof(acc[0])); + } + } + + /* the bit at e[0] is always 1 */ + montgomery_mul(key, tmp, acc, acc); /* tmp = acc^2 / R mod n */ + montgomery_mul(key, acc, tmp, val); /* acc = tmp * a / R mod M */ + memcpy(result, acc, key->len * sizeof(result[0])); + + /* Make sure result < mod; result is at most 1x mod too large. */ + if (greater_equal_modulus(key, result)) + subtract_modulus(key, result); + + /* Convert to bigendian byte array */ + for (i = key->len - 1, ptr = inout; (int)i >= 0; i--, ptr++) + put_unaligned_be32(result[i], ptr); + return 0; +} + + +static void rsa_convert_big_endian(uint32_t *dst, const uint32_t *src, int len) +{ + int i; + + for (i = 0; i < len; i++) + dst[i] = fdt32_to_cpu(src[len - 1 - i]); +} + +int rsa_mod_exp(const uint8_t *sig, uint32_t sig_len, + struct key_prop *prop, uint8_t *out) +{ + struct rsa_public_key key; + int ret; + + if (!prop) { + debug("%s: Skipping invalid prop", __func__); + return -EBADF; + } + if (!prop->n0inv) { + debug("%s: Missing rsa,n0-inverse", __func__); + return -EFAULT; + } + key.n0inv = prop->n0inv; + key.len = prop->num_bits; + + if (!prop->public_exponent) + key.exponent = RSA_DEFAULT_PUBEXP; + else + key.exponent = + fdt64_to_cpu(*((uint64_t *)(prop->public_exponent))); + + if (!key.len || !prop->modulus || !prop->rr) { + debug("%s: Missing RSA key info", __func__); + return -EFAULT; + } + + /* Sanity check for stack size */ + if (key.len > RSA_MAX_KEY_BITS || key.len < RSA_MIN_KEY_BITS) { + debug("RSA key bits %u outside allowed range %d..%d\n", + key.len, RSA_MIN_KEY_BITS, RSA_MAX_KEY_BITS); + return -EFAULT; + } + key.len /= sizeof(uint32_t) * 8; + uint32_t key1[key.len], key2[key.len]; + + key.modulus = key1; + key.rr = key2; + rsa_convert_big_endian(key.modulus, (uint32_t *)prop->modulus, key.len); + rsa_convert_big_endian(key.rr, (uint32_t *)prop->rr, key.len); + if (!key.modulus || !key.rr) { + debug("%s: Out of memory", __func__); + return -ENOMEM; + } + + uint32_t buf[sig_len / sizeof(uint32_t)]; + + memcpy(buf, sig, sig_len); + + ret = pow_mod(&key, buf); + if (ret) + return ret; + + memcpy(out, buf, sig_len); + + return 0; +} diff --git a/lib/rsa/rsa-verify.c b/lib/rsa/rsa-verify.c index 4ef19b6..583400f 100644 --- a/lib/rsa/rsa-verify.c +++ b/lib/rsa/rsa-verify.c @@ -17,230 +17,13 @@ #include "mkimage.h" #include <fdt_support.h> #endif +#include <u-boot/rsa-mod-exp.h> #include <u-boot/rsa.h> -#include <u-boot/sha1.h> -#include <u-boot/sha256.h> - -#define UINT64_MULT32(v, multby) (((uint64_t)(v)) * ((uint32_t)(multby))) - -#define get_unaligned_be32(a) fdt32_to_cpu(*(uint32_t *)a) -#define put_unaligned_be32(a, b) (*(uint32_t *)(b) = cpu_to_fdt32(a))
/* Default public exponent for backward compatibility */ #define RSA_DEFAULT_PUBEXP 65537
-/** - * subtract_modulus() - subtract modulus from the given value - * - * @key: Key containing modulus to subtract - * @num: Number to subtract modulus from, as little endian word array - */ -static void subtract_modulus(const struct rsa_public_key *key, uint32_t num[]) -{ - int64_t acc = 0; - uint i; - - for (i = 0; i < key->len; i++) { - acc += (uint64_t)num[i] - key->modulus[i]; - num[i] = (uint32_t)acc; - acc >>= 32; - } -} - -/** - * greater_equal_modulus() - check if a value is >= modulus - * - * @key: Key containing modulus to check - * @num: Number to check against modulus, as little endian word array - * @return 0 if num < modulus, 1 if num >= modulus - */ -static int greater_equal_modulus(const struct rsa_public_key *key, - uint32_t num[]) -{ - int i; - - for (i = (int)key->len - 1; i >= 0; i--) { - if (num[i] < key->modulus[i]) - return 0; - if (num[i] > key->modulus[i]) - return 1; - } - - return 1; /* equal */ -} - -/** - * montgomery_mul_add_step() - Perform montgomery multiply-add step - * - * Operation: montgomery result[] += a * b[] / n0inv % modulus - * - * @key: RSA key - * @result: Place to put result, as little endian word array - * @a: Multiplier - * @b: Multiplicand, as little endian word array - */ -static void montgomery_mul_add_step(const struct rsa_public_key *key, - uint32_t result[], const uint32_t a, const uint32_t b[]) -{ - uint64_t acc_a, acc_b; - uint32_t d0; - uint i; - - acc_a = (uint64_t)a * b[0] + result[0]; - d0 = (uint32_t)acc_a * key->n0inv; - acc_b = (uint64_t)d0 * key->modulus[0] + (uint32_t)acc_a; - for (i = 1; i < key->len; i++) { - acc_a = (acc_a >> 32) + (uint64_t)a * b[i] + result[i]; - acc_b = (acc_b >> 32) + (uint64_t)d0 * key->modulus[i] + - (uint32_t)acc_a; - result[i - 1] = (uint32_t)acc_b; - } - - acc_a = (acc_a >> 32) + (acc_b >> 32); - - result[i - 1] = (uint32_t)acc_a; - - if (acc_a >> 32) - subtract_modulus(key, result); -} - -/** - * montgomery_mul() - Perform montgomery mutitply - * - * Operation: montgomery result[] = a[] * b[] / n0inv % modulus - * - * @key: RSA key - * @result: Place to put result, as little endian word array - * @a: Multiplier, as little endian word array - * @b: Multiplicand, as little endian word array - */ -static void montgomery_mul(const struct rsa_public_key *key, - uint32_t result[], uint32_t a[], const uint32_t b[]) -{ - uint i; - - for (i = 0; i < key->len; ++i) - result[i] = 0; - for (i = 0; i < key->len; ++i) - montgomery_mul_add_step(key, result, a[i], b); -} - -/** - * num_pub_exponent_bits() - Number of bits in the public exponent - * - * @key: RSA key - * @num_bits: Storage for the number of public exponent bits - */ -static int num_public_exponent_bits(const struct rsa_public_key *key, - int *num_bits) -{ - uint64_t exponent; - int exponent_bits; - const uint max_bits = (sizeof(exponent) * 8); - - exponent = key->exponent; - exponent_bits = 0; - - if (!exponent) { - *num_bits = exponent_bits; - return 0; - } - - for (exponent_bits = 1; exponent_bits < max_bits + 1; ++exponent_bits) - if (!(exponent >>= 1)) { - *num_bits = exponent_bits; - return 0; - } - - return -EINVAL; -} - -/** - * is_public_exponent_bit_set() - Check if a bit in the public exponent is set - * - * @key: RSA key - * @pos: The bit position to check - */ -static int is_public_exponent_bit_set(const struct rsa_public_key *key, - int pos) -{ - return key->exponent & (1ULL << pos); -} - -/** - * pow_mod() - in-place public exponentiation - * - * @key: RSA key - * @inout: Big-endian word array containing value and result - */ -static int pow_mod(const struct rsa_public_key *key, uint32_t *inout) -{ - uint32_t *result, *ptr; - uint i; - int j, k; - - /* Sanity check for stack size - key->len is in 32-bit words */ - if (key->len > RSA_MAX_KEY_BITS / 32) { - debug("RSA key words %u exceeds maximum %d\n", key->len, - RSA_MAX_KEY_BITS / 32); - return -EINVAL; - } - - uint32_t val[key->len], acc[key->len], tmp[key->len]; - uint32_t a_scaled[key->len]; - result = tmp; /* Re-use location. */ - - /* Convert from big endian byte array to little endian word array. */ - for (i = 0, ptr = inout + key->len - 1; i < key->len; i++, ptr--) - val[i] = get_unaligned_be32(ptr); - - if (0 != num_public_exponent_bits(key, &k)) - return -EINVAL; - - if (k < 2) { - debug("Public exponent is too short (%d bits, minimum 2)\n", - k); - return -EINVAL; - } - - if (!is_public_exponent_bit_set(key, 0)) { - debug("LSB of RSA public exponent must be set.\n"); - return -EINVAL; - } - - /* the bit at e[k-1] is 1 by definition, so start with: C := M */ - montgomery_mul(key, acc, val, key->rr); /* acc = a * RR / R mod n */ - /* retain scaled version for intermediate use */ - memcpy(a_scaled, acc, key->len * sizeof(a_scaled[0])); - - for (j = k - 2; j > 0; --j) { - montgomery_mul(key, tmp, acc, acc); /* tmp = acc^2 / R mod n */ - - if (is_public_exponent_bit_set(key, j)) { - /* acc = tmp * val / R mod n */ - montgomery_mul(key, acc, tmp, a_scaled); - } else { - /* e[j] == 0, copy tmp back to acc for next operation */ - memcpy(acc, tmp, key->len * sizeof(acc[0])); - } - } - - /* the bit at e[0] is always 1 */ - montgomery_mul(key, tmp, acc, acc); /* tmp = acc^2 / R mod n */ - montgomery_mul(key, acc, tmp, val); /* acc = tmp * a / R mod M */ - memcpy(result, acc, key->len * sizeof(result[0])); - - /* Make sure result < mod; result is at most 1x mod too large. */ - if (greater_equal_modulus(key, result)) - subtract_modulus(key, result); - - /* Convert to bigendian byte array */ - for (i = key->len - 1, ptr = inout; (int)i >= 0; i--, ptr++) - put_unaligned_be32(result[i], ptr); - return 0; -} - -static int rsa_verify_key(const struct rsa_public_key *key, const uint8_t *sig, +static int rsa_verify_key(struct key_prop *node, const uint8_t *sig, const uint32_t sig_len, const uint8_t *hash, struct checksum_algo *algo) { @@ -248,10 +31,10 @@ static int rsa_verify_key(const struct rsa_public_key *key, const uint8_t *sig, int pad_len; int ret;
- if (!key || !sig || !hash || !algo) + if (!node || !sig || !hash || !algo) return -EIO;
- if (sig_len != (key->len * sizeof(uint32_t))) { + if (sig_len != (node->num_bits / 8)) { debug("Signature is of incorrect length %d\n", sig_len); return -EINVAL; } @@ -265,13 +48,13 @@ static int rsa_verify_key(const struct rsa_public_key *key, const uint8_t *sig, return -EINVAL; }
- uint32_t buf[sig_len / sizeof(uint32_t)]; - - memcpy(buf, sig, sig_len); + uint8_t buf[sig_len];
- ret = pow_mod(key, buf); - if (ret) + ret = rsa_mod_exp(sig, sig_len, node, buf); + if (ret) { + debug("Error in Modular exponentation\n"); return ret; + }
padding = algo->rsa_padding; pad_len = algo->pad_len - algo->checksum_len; @@ -291,72 +74,42 @@ static int rsa_verify_key(const struct rsa_public_key *key, const uint8_t *sig, return 0; }
-static void rsa_convert_big_endian(uint32_t *dst, const uint32_t *src, int len) -{ - int i; - - for (i = 0; i < len; i++) - dst[i] = fdt32_to_cpu(src[len - 1 - i]); -} - static int rsa_verify_with_keynode(struct image_sign_info *info, - const void *hash, uint8_t *sig, uint sig_len, int node) + const void *hash, uint8_t *sig, + uint sig_len, int node) { const void *blob = info->fdt_blob; - struct rsa_public_key key; - const void *modulus, *rr; - const uint64_t *public_exponent; + struct key_prop prop; int length; - int ret;
if (node < 0) { debug("%s: Skipping invalid node", __func__); return -EBADF; } - if (!fdt_getprop(blob, node, "rsa,n0-inverse", NULL)) { - debug("%s: Missing rsa,n0-inverse", __func__); - return -EFAULT; - } - key.len = fdtdec_get_int(blob, node, "rsa,num-bits", 0); - key.n0inv = fdtdec_get_int(blob, node, "rsa,n0-inverse", 0); - public_exponent = fdt_getprop(blob, node, "rsa,exponent", &length); - if (!public_exponent || length < sizeof(*public_exponent)) - key.exponent = RSA_DEFAULT_PUBEXP; - else - key.exponent = fdt64_to_cpu(*public_exponent); - modulus = fdt_getprop(blob, node, "rsa,modulus", NULL); - rr = fdt_getprop(blob, node, "rsa,r-squared", NULL); - if (!key.len || !modulus || !rr) { - debug("%s: Missing RSA key info", __func__); - return -EFAULT; - }
- /* Sanity check for stack size */ - if (key.len > RSA_MAX_KEY_BITS || key.len < RSA_MIN_KEY_BITS) { - debug("RSA key bits %u outside allowed range %d..%d\n", - key.len, RSA_MIN_KEY_BITS, RSA_MAX_KEY_BITS); + prop.num_bits = fdtdec_get_int(blob, node, "rsa,num-bits", 0); + + prop.n0inv = fdtdec_get_int(blob, node, "rsa,n0-inverse", 0); + + prop.public_exponent = fdt_getprop(blob, node, "rsa,exponent", &length); + if (!prop.public_exponent || length < sizeof(uint64_t)) + prop.public_exponent = NULL; + + prop.exp_len = sizeof(uint64_t); + + prop.modulus = fdt_getprop(blob, node, "rsa,modulus", NULL); + + prop.rr = fdt_getprop(blob, node, "rsa,r-squared", NULL); + + if (!prop.num_bits || !prop.modulus) { + debug("%s: Missing RSA key info", __func__); return -EFAULT; } - key.len /= sizeof(uint32_t) * 8; - uint32_t key1[key.len], key2[key.len]; - - key.modulus = key1; - key.rr = key2; - rsa_convert_big_endian(key.modulus, modulus, key.len); - rsa_convert_big_endian(key.rr, rr, key.len); - if (!key.modulus || !key.rr) { - debug("%s: Out of memory", __func__); - return -ENOMEM; - }
- debug("key length %d\n", key.len); - ret = rsa_verify_key(&key, sig, sig_len, hash, info->algo->checksum); - if (ret) { - printf("%s: RSA failed to verify: %d\n", __func__, ret); - return ret; - } + rsa_verify_key(&prop, sig, sig_len, hash, info->algo->checksum);
return 0; + }
int rsa_verify(struct image_sign_info *info, diff --git a/tools/Makefile b/tools/Makefile index a4216a1..f67cd15 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -60,7 +60,7 @@ FIT_SIG_OBJS-$(CONFIG_FIT_SIGNATURE) := common/image-sig.o LIBFDT_OBJS := $(addprefix lib/libfdt/, \ fdt.o fdt_ro.o fdt_rw.o fdt_strerror.o fdt_wip.o) RSA_OBJS-$(CONFIG_FIT_SIGNATURE) := $(addprefix lib/rsa/, \ - rsa-sign.o rsa-verify.o rsa-checksum.o) + rsa-sign.o rsa-verify.o rsa-checksum.o rsa-mod-exp.o)
# common objs for dumpimage and mkimage dumpimage-mkimage-objs := aisimage.o \

Support added for offloading Modular Exponentiation required in RSA Verify functionality to hardware which depends on CONFIG_RSA_MOD_EXP_HW.
Signed-off-by: Ruchika Gupta ruchika.gupta@freescale.com CC: Simon Glass sjg@chromium.org --- Changes in v2: Added Kconfig option for RSA_MOD_HW
drivers/crypto/fsl/Kconfig | 5 +++++ drivers/crypto/fsl/Makefile | 1 + drivers/crypto/fsl/fsl_rsa.c | 44 +++++++++++++++++++++++++++++++++++++++++++ drivers/crypto/fsl/jobdesc.c | 28 +++++++++++++++++++++++++++ drivers/crypto/fsl/jobdesc.h | 5 +++++ drivers/crypto/fsl/rsa_caam.h | 27 ++++++++++++++++++++++++++ lib/Kconfig | 11 +++++++++-- 7 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 drivers/crypto/fsl/Kconfig create mode 100644 drivers/crypto/fsl/fsl_rsa.c create mode 100644 drivers/crypto/fsl/rsa_caam.h
diff --git a/drivers/crypto/fsl/Kconfig b/drivers/crypto/fsl/Kconfig new file mode 100644 index 0000000..45e6683 --- /dev/null +++ b/drivers/crypto/fsl/Kconfig @@ -0,0 +1,5 @@ +config FSL_CAAM + depends on CRYPTO + bool "Freescale Crypto Driver Support" + help + HW Acceleration support for RSA based platforms diff --git a/drivers/crypto/fsl/Makefile b/drivers/crypto/fsl/Makefile index 067d0a9..ef36f67 100644 --- a/drivers/crypto/fsl/Makefile +++ b/drivers/crypto/fsl/Makefile @@ -8,4 +8,5 @@
obj-y += sec.o obj-$(CONFIG_FSL_CAAM) += jr.o fsl_hash.o jobdesc.o error.o +obj-$(CONFIG_RSA_MOD_EXP_HW) += fsl_rsa.o obj-$(CONFIG_CMD_BLOB) += fsl_blob.o diff --git a/drivers/crypto/fsl/fsl_rsa.c b/drivers/crypto/fsl/fsl_rsa.c new file mode 100644 index 0000000..1100316 --- /dev/null +++ b/drivers/crypto/fsl/fsl_rsa.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013, Google Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/types.h> +#include <malloc.h> +#include "jobdesc.h" +#include "desc.h" +#include "jr.h" +#include "rsa_caam.h" +#include <u-boot/rsa-mod-exp.h> + +int rsa_mod_exp(const uint8_t *sig, uint32_t sig_len, + struct key_prop *prop, uint8_t *out) +{ + uint32_t keylen; + struct pk_in_params pkin; + uint32_t desc[MAX_CAAM_DESCSIZE]; + int ret; + + /* Length in bytes */ + keylen = prop->num_bits / 8; + + pkin.a = sig; + pkin.a_siz = sig_len; + pkin.n = prop->modulus; + pkin.n_siz = keylen; + pkin.e = prop->public_exponent; + pkin.e_siz = prop->exp_len; + + inline_cnstr_jobdesc_pkha_rsaexp(desc, &pkin, out, sig_len); + + ret = run_descriptor_jr(desc); + + if (ret) { + debug("%s: RSA failed to verify: %d\n", __func__, ret); + return ret; + } + + return 0; +} diff --git a/drivers/crypto/fsl/jobdesc.c b/drivers/crypto/fsl/jobdesc.c index 1386bae..cc0dced 100644 --- a/drivers/crypto/fsl/jobdesc.c +++ b/drivers/crypto/fsl/jobdesc.c @@ -11,6 +11,7 @@ #include <common.h> #include "desc_constr.h" #include "jobdesc.h" +#include "rsa_caam.h"
#define KEY_BLOB_SIZE 32 #define MAC_SIZE 16 @@ -123,3 +124,30 @@ void inline_cnstr_jobdesc_rng_instantiation(uint32_t *desc) append_operation(desc, OP_TYPE_CLASS1_ALG | OP_ALG_ALGSEL_RNG | OP_ALG_RNG4_SK); } + +/* Change key size to bytes form bits in calling function*/ +void inline_cnstr_jobdesc_pkha_rsaexp(uint32_t *desc, + struct pk_in_params *pkin, uint8_t *out, + uint32_t out_siz) +{ + dma_addr_t dma_addr_e, dma_addr_a, dma_addr_n, dma_addr_out; + + dma_addr_e = virt_to_phys((void *)pkin->e); + dma_addr_a = virt_to_phys((void *)pkin->a); + dma_addr_n = virt_to_phys((void *)pkin->n); + dma_addr_out = virt_to_phys((void *)out); + + init_job_desc(desc, 0); + append_key(desc, dma_addr_e, pkin->e_siz, KEY_DEST_PKHA_E | CLASS_1); + + append_fifo_load(desc, dma_addr_a, + pkin->a_siz, LDST_CLASS_1_CCB | FIFOLD_TYPE_PK_A); + + append_fifo_load(desc, dma_addr_n, + pkin->n_siz, LDST_CLASS_1_CCB | FIFOLD_TYPE_PK_N); + + append_operation(desc, OP_TYPE_PK | OP_ALG_PK | OP_ALG_PKMODE_MOD_EXPO); + + append_fifo_store(desc, dma_addr_out, out_siz, + LDST_CLASS_1_CCB | FIFOST_TYPE_PKHA_B); +} diff --git a/drivers/crypto/fsl/jobdesc.h b/drivers/crypto/fsl/jobdesc.h index 3cf7226..84b3edd 100644 --- a/drivers/crypto/fsl/jobdesc.h +++ b/drivers/crypto/fsl/jobdesc.h @@ -10,6 +10,7 @@
#include <common.h> #include <asm/io.h> +#include "rsa_caam.h"
#define KEY_IDNFR_SZ_BYTES 16
@@ -26,4 +27,8 @@ void inline_cnstr_jobdesc_blob_decap(uint32_t *desc, uint8_t *key_idnfr, uint32_t out_sz);
void inline_cnstr_jobdesc_rng_instantiation(uint32_t *desc); + +void inline_cnstr_jobdesc_pkha_rsaexp(uint32_t *desc, + struct pk_in_params *pkin, uint8_t *out, + uint32_t out_siz); #endif diff --git a/drivers/crypto/fsl/rsa_caam.h b/drivers/crypto/fsl/rsa_caam.h new file mode 100644 index 0000000..d1dc7eb --- /dev/null +++ b/drivers/crypto/fsl/rsa_caam.h @@ -0,0 +1,27 @@ +/* + * Copyright 2014 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __RSA_SEC_H +#define __RSA_SEC_H + +#include <common.h> + +/** + * struct pk_in_params - holder for input to PKHA block in CAAM + * + */ +struct pk_in_params { + const uint8_t *e; /* public exponent as byte array */ + uint32_t e_siz; /* size of e[] in number of bytes */ + const uint8_t *n; /* modulus as byte array */ + uint32_t n_siz; /* size of n[] in number of bytes */ + const uint8_t *a; /* Signature as byte array */ + uint32_t a_siz; /* size of a[] in number of bytes */ + uint8_t *b; /* Result exp. modulus in number of bytes */ + uint32_t b_siz; /* size of b[] in number of bytes */ +}; + +#endif diff --git a/lib/Kconfig b/lib/Kconfig index a921bb3..b52f17f 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -35,11 +35,18 @@ config RSA if RSA
config RSA_MOD_EXP_SW - depends on RSA + depends on RSA && !RSA_MOD_EXP_HW bool "Implement RSA Modular Exponentiation in software" - default y + default n help Modular Exponentiation support in SW
+config RSA_MOD_EXP_HW + depends on RSA && FSL_CAAM + bool "Implement RSA Modular Exp using Freescale Specific Impl" + default y + help + Modular Exponentiation support in HW + endif endmenu

The hash_algo structure has some implementations in which progressive hash API's are not defined. These are basically the hardware based implementations of SHA. An API is added to find the algo which has progressive hash API's defined. This can then be integrated with RSA checksum library which used Progressive Hash API's.
Signed-off-by: Ruchika Gupta ruchika.gupta@freescale.com CC: Simon Glass sjg@chromium.org --- Changes in v2 : Added commit message
common/hash.c | 35 ++++++++++++++++++++++++++--------- include/hash.h | 15 +++++++++++++++ 2 files changed, 41 insertions(+), 9 deletions(-)
diff --git a/common/hash.c b/common/hash.c index 12d6759..87263df 100644 --- a/common/hash.c +++ b/common/hash.c @@ -20,7 +20,7 @@ #include <asm/io.h> #include <asm/errno.h>
-#ifdef CONFIG_CMD_SHA1SUM +#ifdef CONFIG_SHA256 static int hash_init_sha1(struct hash_algo *algo, void **ctxp) { sha1_context *ctx = malloc(sizeof(sha1_context)); @@ -125,12 +125,8 @@ static struct hash_algo hash_algo[] = { CHUNKSZ_SHA256, }, #endif - /* - * This is CONFIG_CMD_SHA1SUM instead of CONFIG_SHA1 since otherwise - * it bloats the code for boards which use SHA1 but not the 'hash' - * or 'sha1sum' commands. - */ -#ifdef CONFIG_CMD_SHA1SUM + +#ifdef CONFIG_SHA1 { "sha1", SHA1_SUM_LEN, @@ -140,7 +136,6 @@ static struct hash_algo hash_algo[] = { hash_update_sha1, hash_finish_sha1, }, -#define MULTI_HASH #endif #ifdef CONFIG_SHA256 { @@ -152,7 +147,6 @@ static struct hash_algo hash_algo[] = { hash_update_sha256, hash_finish_sha256, }, -#define MULTI_HASH #endif { "crc32", @@ -165,6 +159,10 @@ static struct hash_algo hash_algo[] = { }, };
+#if defined(CONFIG_SHA256) || defined(CONFIG_CMD_SHA1SUM) +#define MULTI_HASH +#endif + #if defined(CONFIG_HASH_VERIFY) || defined(CONFIG_CMD_HASH) #define MULTI_HASH #endif @@ -311,6 +309,25 @@ int hash_lookup_algo(const char *algo_name, struct hash_algo **algop) return -EPROTONOSUPPORT; }
+int hash_progressive_lookup_algo(const char *algo_name, + struct hash_algo **algop) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hash_algo); i++) { + if (!strcmp(algo_name, hash_algo[i].name)) { + if (hash_algo[i].hash_init) { + *algop = &hash_algo[i]; + return 0; + } + } + } + + debug("Unknown hash algorithm '%s'\n", algo_name); + return -EPROTONOSUPPORT; +} + + void hash_show(struct hash_algo *algo, ulong addr, ulong len, uint8_t *output) { int i; diff --git a/include/hash.h b/include/hash.h index d8ec4f0..059f84e 100644 --- a/include/hash.h +++ b/include/hash.h @@ -128,6 +128,21 @@ int hash_block(const char *algo_name, const void *data, unsigned int len, int hash_lookup_algo(const char *algo_name, struct hash_algo **algop);
/** + * hash_progressive_lookup_algo() - Look up the hash_algo struct with progressive + * hash support for an algorithm + * + * The function returns the pointer to the struct or -EPROTONOSUPPORT if the + * algorithm is not available with progressive hash support. + * + * @algo_name: Hash algorithm to look up + * @algop: Pointer to the hash_algo struct if found + * + * @return 0 if ok, -EPROTONOSUPPORT for an unknown algorithm. + */ +int hash_progressive_lookup_algo(const char *algo_name, + struct hash_algo **algop); + +/** * hash_show() - Print out a hash algorithm and value * * You will get a message like this (without a newline at the end):

Currently the hash functions used in RSA are called directly from the sha1 and sha256 libraries. Change the RSA checksum library to use the progressive hash API's registered with struct hash_algo. This will allow the checksum library to use the support of hardware accelerated progressive hash API's once available.
Signed-off-by: Ruchika Gupta ruchika.gupta@freescale.com CC: Simon Glass sjg@chromium.org --- Changes in v2: Added generic function hash_calculate. Pass an additional argument as name of algorithm.
common/image-sig.c | 6 ++--- include/image.h | 5 ++-- include/u-boot/rsa-checksum.h | 7 +++--- lib/rsa/rsa-checksum.c | 53 +++++++++++++++++++++++++++++++++++++++---- lib/rsa/rsa-verify.c | 3 ++- 5 files changed, 60 insertions(+), 14 deletions(-)
diff --git a/common/image-sig.c b/common/image-sig.c index 8601eda..2c9f0cd 100644 --- a/common/image-sig.c +++ b/common/image-sig.c @@ -38,7 +38,7 @@ struct checksum_algo checksum_algos[] = { #if IMAGE_ENABLE_SIGN EVP_sha1, #endif - sha1_calculate, + hash_calculate, padding_sha1_rsa2048, }, { @@ -48,7 +48,7 @@ struct checksum_algo checksum_algos[] = { #if IMAGE_ENABLE_SIGN EVP_sha256, #endif - sha256_calculate, + hash_calculate, padding_sha256_rsa2048, }, { @@ -58,7 +58,7 @@ struct checksum_algo checksum_algos[] = { #if IMAGE_ENABLE_SIGN EVP_sha256, #endif - sha256_calculate, + hash_calculate, padding_sha256_rsa4096, }
diff --git a/include/image.h b/include/image.h index af30d60..ec55f23 100644 --- a/include/image.h +++ b/include/image.h @@ -926,8 +926,9 @@ struct checksum_algo { #if IMAGE_ENABLE_SIGN const EVP_MD *(*calculate_sign)(void); #endif - void (*calculate)(const struct image_region region[], - int region_count, uint8_t *checksum); + int (*calculate)(const char *name, + const struct image_region region[], + int region_count, uint8_t *checksum); const uint8_t *rsa_padding; };
diff --git a/include/u-boot/rsa-checksum.h b/include/u-boot/rsa-checksum.h index c996fb3..c546c80 100644 --- a/include/u-boot/rsa-checksum.h +++ b/include/u-boot/rsa-checksum.h @@ -16,9 +16,8 @@ extern const uint8_t padding_sha256_rsa4096[]; extern const uint8_t padding_sha256_rsa2048[]; extern const uint8_t padding_sha1_rsa2048[];
-void sha256_calculate(const struct image_region region[], int region_count, - uint8_t *checksum); -void sha1_calculate(const struct image_region region[], int region_count, - uint8_t *checksum); +int hash_calculate(const char *name, + const struct image_region region[], int region_count, + uint8_t *checksum);
#endif diff --git a/lib/rsa/rsa-checksum.c b/lib/rsa/rsa-checksum.c index 8d8b59f..7f1909a 100644 --- a/lib/rsa/rsa-checksum.c +++ b/lib/rsa/rsa-checksum.c @@ -10,12 +10,13 @@ #include <asm/byteorder.h> #include <asm/errno.h> #include <asm/unaligned.h> +#include <hash.h> #else #include "fdt_host.h" -#endif -#include <u-boot/rsa.h> #include <u-boot/sha1.h> #include <u-boot/sha256.h> +#endif +#include <u-boot/rsa.h>
/* PKCS 1.5 paddings as described in the RSA PKCS#1 v2.1 standard. */
@@ -136,7 +137,33 @@ const uint8_t padding_sha256_rsa4096[RSA4096_BYTES - SHA256_SUM_LEN] = { 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
-void sha1_calculate(const struct image_region region[], int region_count, +#ifndef USE_HOSTCC +int hash_calculate(const char *name, + const struct image_region region[], + int region_count, uint8_t *checksum) +{ + struct hash_algo *algo; + int ret = 0; + void *ctx; + uint32_t i; + i = 0; + + ret = hash_progressive_lookup_algo(name, &algo); + if (ret) + return ret; + + algo->hash_init(algo, &ctx); + for (i = 0; i < region_count - 1; i++) + algo->hash_update(algo, ctx, region[i].data, region[i].size, 0); + + algo->hash_update(algo, ctx, region[i].data, region[i].size, 1); + algo->hash_finish(algo, ctx, checksum, algo->digest_size); + + return 0; +} + +#else +int sha1_calculate(const struct image_region region[], int region_count, uint8_t *checksum) { sha1_context ctx; @@ -147,9 +174,11 @@ void sha1_calculate(const struct image_region region[], int region_count, for (i = 0; i < region_count; i++) sha1_update(&ctx, region[i].data, region[i].size); sha1_finish(&ctx, checksum); + + return 0; }
-void sha256_calculate(const struct image_region region[], int region_count, +int sha256_calculate(const struct image_region region[], int region_count, uint8_t *checksum) { sha256_context ctx; @@ -160,4 +189,20 @@ void sha256_calculate(const struct image_region region[], int region_count, for (i = 0; i < region_count; i++) sha256_update(&ctx, region[i].data, region[i].size); sha256_finish(&ctx, checksum); + + return 0; } + +int hash_calculate(const char *name, + const struct image_region region[], int region_count, + uint8_t *checksum) +{ + if (!strcmp(name, "sha1")) + sha1_calculate(region, region_count, checksum); + + if (!strcmp(name, "sha256")) + sha256_calculate(region, region_count, checksum); + + return 0; +} +#endif diff --git a/lib/rsa/rsa-verify.c b/lib/rsa/rsa-verify.c index 583400f..b7b8933 100644 --- a/lib/rsa/rsa-verify.c +++ b/lib/rsa/rsa-verify.c @@ -142,7 +142,8 @@ int rsa_verify(struct image_sign_info *info, }
/* Calculate checksum with checksum-algorithm */ - info->algo->checksum->calculate(region, region_count, hash); + info->algo->checksum->calculate(info->algo->checksum->name, + region, region_count, hash);
/* See if we must use a particular key */ if (info->required_keynode != -1) {

Hi Ruchika,
On 18 December 2014 at 04:22, Ruchika Gupta ruchika.gupta@freescale.com wrote:
Public exponentiation which is required in rsa verify functionality is currently tightly integrated with verification code in rsa_verify.c. Currently this implementation is software based. Some platforms having support of the exponentiation in hardware. To enable the rsa verify functionality to use the Modular exponentiation if present in hardware, the patch-set splits the file into two files:
- rsa-verify.c
- The file parses device tree keys node to fill a keyprop
structure. The keyprop structure can then be converted to implementation specific formal (struct rsa_pub_key for sw implementation).
- The parsed device tree node is then passed to a generic
rsa_mod_exp function.
- rsa-mod-exp.c
Move the software specific functions related to exponentiation from rsa-verify.c to this file. The file is compiled if "CONFIG_RSA_MOD_EXP_SW" is defined. In general if both CONFIG_FIT_SIGNATURE and CONFIG_RSA are defined, CONFIG_RSA_MOD_EXP_SW gets automatically defined.
Platforms having hardware implementation for rsa_mod_exp can add a define "CONFIG_RSA_MOD_EXP_HW" to their config files. Adding this defined, undefs the CONFIG_RSA_MOD_EXP_SW and hardware implementation of mod_exp gets compiled.
Signed-off-by: Ruchika Gupta ruchika.gupta@freescale.com CC: Simon Glass sjg@chromium.org
Changes in v2: Addressed few of Simon Glass's comments:
- Kconfig option added for RSA
- Comments added for new keyprop struct
Kconfig | 2 + include/config_fallbacks.h | 8 ++ include/u-boot/rsa-mod-exp.h | 34 +++++ lib/Kconfig | 15 +++ lib/rsa/Makefile | 1 + lib/rsa/rsa-mod-exp.c | 308 +++++++++++++++++++++++++++++++++++++++++++ lib/rsa/rsa-verify.c | 307 +++++------------------------------------- tools/Makefile | 2 +- 8 files changed, 399 insertions(+), 278 deletions(-) create mode 100644 include/u-boot/rsa-mod-exp.h create mode 100644 lib/rsa/rsa-mod-exp.c
Here are my comments on the whole series. In general the approach looks fine provided you move it to driver model in a follow-up series. See the 'thermal' uclass for a very simple example.
- Check your multi-line comment style - Make sure you add a full help paragraph for new Kconfigs - Avoid double blank lines - First patch should just move code out of lib/rsa/rsa-verify.c - Add a separate patch to move CONFIG_FIT_SIGNATURE to Kconfig for existing boards - Make sure functions have comments describing their purpose, arguments, return value. Normally these go in the header file except for static functions - For your header file guard #ifdefs, use a symbol that relates to the filename - Word-wrap your commit messages to >70 chars (e.g. 76 or so) - Make sure you check return values of functions that return errors - Make sure the vboot tests still pass make O=sandbox sandbox_defconfig all O=b/sandbox ./test/vboot/vboot_test.sh - Check your hash.c changes, I think you have the #ifdefs wrong.
Please then send a v3 without RFC and we'll try to get final things resolved.
Regards, Simon
participants (2)
-
Ruchika Gupta
-
Simon Glass