From: Michael van der Westhuizen <mich...@smart-africa.com> Remove the verified boot limitation that only allows a single RSA public exponent of 65537 (F4). This allows use with existing PKI infrastructure, and has been tested with HSM-based PKI.
Change the configuration OF tree format to store the RSA public exponent as a 64 bit integer and implement backward compatibility for verified boot configuration trees without this extra field. Parameterise vboot_test.sh to test different public exponents and do a drive-by fix to get vboot_test.sh working again. Mathematics and other hard work by Andrew Bott. Tested with the following public exponents: 3, 5, 17, 257, 39981, 50457, 65537 and 4294967297. Signed-off-by: Andrew Bott <andrew.b...@ipaccess.com> Signed-off-by: Andrew Wishart <andrew.wish...@ipaccess.com> Signed-off-by: Neil Piercy <neil.pie...@ipaccess.com> Signed-off-by: Michael van der Westhuizen <mich...@smart-africa.com> --- Changes for v2: - None. Resend to address line wrapping issues. doc/uImage.FIT/signature.txt | 4 +- include/rsa.h | 1 + lib/rsa/rsa-sign.c | 56 +++++++++++++++++++++++++++- lib/rsa/rsa-verify.c | 88 +++++++++++++++++++++++++++++++++++++++++--- test/vboot/vboot_test.sh | 11 +++++- 5 files changed, 151 insertions(+), 9 deletions(-) diff --git a/doc/uImage.FIT/signature.txt b/doc/uImage.FIT/signature.txt index 9502037..cc314a3 100644 --- a/doc/uImage.FIT/signature.txt +++ b/doc/uImage.FIT/signature.txt @@ -66,7 +66,8 @@ Creating an RSA key and certificate ----------------------------------- To create a new public key, size 2048 bits: -$ openssl genrsa -F4 -out keys/dev.key 2048 +$ openssl genpkey -algorithm RSA -out keys/dev.key \ + -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 To create a certificate for this: @@ -159,6 +160,7 @@ For RSA the following are mandatory: - rsa,num-bits: Number of key bits (e.g. 2048) - rsa,modulus: Modulus (N) as a big-endian multi-word integer +- rsa,exponent: Public exponent (E) as a 64 bit unsigned integer - rsa,r-squared: (2^num-bits)^2 as a big-endian multi-word integer - rsa,n0-inverse: -1 / modulus[0] mod 2^32 diff --git a/include/rsa.h b/include/rsa.h index a5680ab..b84d82a 100644 --- a/include/rsa.h +++ b/include/rsa.h @@ -27,6 +27,7 @@ struct rsa_public_key { uint32_t n0inv; /* -1 / modulus[0] mod 2^32 */ uint32_t *modulus; /* modulus as little endian array */ uint32_t *rr; /* R^2 as little endian array */ + uint64_t exponent; /* public exponent */ }; #if IMAGE_ENABLE_SIGN diff --git a/lib/rsa/rsa-sign.c b/lib/rsa/rsa-sign.c index ca8c120..f20ae28 100644 --- a/lib/rsa/rsa-sign.c +++ b/lib/rsa/rsa-sign.c @@ -261,9 +261,56 @@ err_priv: } /* + * rsa_get_e(): - Get the public exponent from an RSA public key + */ +static int rsa_get_e(RSA *key, uint64_t *e) +{ + int ret; + BIGNUM *bn_te; + uint64_t te; + + ret = -1; + bn_te = NULL; + + if (!e) + goto cleanup; + + if (BN_num_bits(key->e) > 64) + goto cleanup; + + *e = BN_get_word(key->e); + + if (BN_num_bits(key->e) < 33) { + ret = 0; + goto cleanup; + } + + bn_te = BN_dup(key->e); + if (!bn_te) + goto cleanup; + + if (!BN_rshift(bn_te, bn_te, 32)) + goto cleanup; + + if (!BN_mask_bits(bn_te, 32)) + goto cleanup; + + te = BN_get_word(bn_te); + te <<= 32; + *e |= te; + ret = 0; + +cleanup: + if (bn_te) + BN_free(bn_te); + + return ret; +} + +/* * rsa_get_params(): - Get the important parameters of an RSA public key */ -int rsa_get_params(RSA *key, uint32_t *n0_invp, BIGNUM **modulusp, +int rsa_get_params(RSA *key, uint64_t *e, uint32_t *n0_invp, BIGNUM **modulusp, BIGNUM **r_squaredp) { BIGNUM *big1, *big2, *big32, *big2_32; @@ -286,6 +333,9 @@ int rsa_get_params(RSA *key, uint32_t *n0_invp, BIGNUM **modulusp, return -ENOMEM; } + if (0 != rsa_get_e(key, e)) + ret = -1; + if (!BN_copy(n, key->n) || !BN_set_word(big1, 1L) || !BN_set_word(big2, 2L) || !BN_set_word(big32, 32L)) ret = -1; @@ -386,6 +436,7 @@ static int fdt_add_bignum(void *blob, int noffset, const char *prop_name, int rsa_add_verify_data(struct image_sign_info *info, void *keydest) { BIGNUM *modulus, *r_squared; + uint64_t e; uint32_t n0_inv; int parent, node; char name[100]; @@ -397,7 +448,7 @@ int rsa_add_verify_data(struct image_sign_info *info, void *keydest) ret = rsa_get_pub_key(info->keydir, info->keyname, &rsa); if (ret) return ret; - ret = rsa_get_params(rsa, &n0_inv, &modulus, &r_squared); + ret = rsa_get_params(rsa, &e, &n0_inv, &modulus, &r_squared); if (ret) return ret; bits = BN_num_bits(modulus); @@ -431,6 +482,7 @@ int rsa_add_verify_data(struct image_sign_info *info, void *keydest) info->keyname); ret |= fdt_setprop_u32(keydest, node, "rsa,num-bits", bits); ret |= fdt_setprop_u32(keydest, node, "rsa,n0-inverse", n0_inv); + ret |= fdt_setprop_u64(keydest, node, "rsa,exponent", e); ret |= fdt_add_bignum(keydest, node, "rsa,modulus", modulus, bits); ret |= fdt_add_bignum(keydest, node, "rsa,r-squared", r_squared, bits); ret |= fdt_setprop_string(keydest, node, FIT_ALGO_PROP, diff --git a/lib/rsa/rsa-verify.c b/lib/rsa/rsa-verify.c index 587da5b..878f92a 100644 --- a/lib/rsa/rsa-verify.c +++ b/lib/rsa/rsa-verify.c @@ -26,6 +26,9 @@ #define get_unaligned_be32(a) fdt32_to_cpu(*(uint32_t *)a) #define put_unaligned_be32(a, b) (*(uint32_t *)(b) = cpu_to_fdt32(a)) +/* The default public exponent is 65537 */ +#define RSA_DEFAULT_PUBEXP 0x10001 + /** * subtract_modulus() - subtract modulus from the given value * @@ -123,6 +126,42 @@ static void montgomery_mul(const struct rsa_public_key *key, } /** + * num_pub_exponent_bits() - Number of bits in the public exponent + * + * @key: RSA key + * @k: Storage for the number of public exponent bits + */ +static int num_public_exponent_bits(const struct rsa_public_key *key, int *k) +{ + uint64_t e; + const uint nbits = (sizeof(e) * 8); + + *k = 0; + e = key->exponent; + + if (!e) + return 0; + + for (*k = 1; *k < nbits + 1; ++*k) + if (!(e >>= 1)) + 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 & (1 << pos); +} + +/** * pow_mod() - in-place public exponentiation * * @key: RSA key @@ -132,6 +171,7 @@ 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) { @@ -141,18 +181,49 @@ static int pow_mod(const struct rsa_public_key *key, uint32_t *inout) } 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); - montgomery_mul(key, acc, val, key->rr); /* axx = a * RR / R mod M */ - for (i = 0; i < 16; i += 2) { - montgomery_mul(key, tmp, acc, acc); /* tmp = acc^2 / R mod M */ - montgomery_mul(key, acc, tmp, tmp); /* acc = tmp^2 / R mod M */ + 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, k - 1) || + !is_public_exponent_bit_set(key, 0)) { + debug("Invalid RSA public exponent 0x%llx.\n", key->exponent); + 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 * 4); + + 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 * 4); + } } - montgomery_mul(key, result, acc, val); /* result = XX * a / R mod M */ + + /* 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 * 4); /* Make sure result < mod; result is at most 1x mod too large. */ if (greater_equal_modulus(key, result)) @@ -229,6 +300,8 @@ static int rsa_verify_with_keynode(struct image_sign_info *info, const void *blob = info->fdt_blob; struct rsa_public_key key; const void *modulus, *rr; + const uint64_t *pe; + int length; int ret; if (node < 0) { @@ -241,6 +314,11 @@ static int rsa_verify_with_keynode(struct image_sign_info *info, } key.len = fdtdec_get_int(blob, node, "rsa,num-bits", 0); key.n0inv = fdtdec_get_int(blob, node, "rsa,n0-inverse", 0); + pe = fdt_getprop(blob, node, "rsa,exponent", &length); + if (!pe || length < sizeof(*pe)) + key.exponent = RSA_DEFAULT_PUBEXP; + else + key.exponent = fdt64_to_cpu(*pe); modulus = fdt_getprop(blob, node, "rsa,modulus", NULL); rr = fdt_getprop(blob, node, "rsa,r-squared", NULL); if (!key.len || !modulus || !rr) { diff --git a/test/vboot/vboot_test.sh b/test/vboot/vboot_test.sh index 3c6efa7..885e869 100755 --- a/test/vboot/vboot_test.sh +++ b/test/vboot/vboot_test.sh @@ -14,6 +14,7 @@ set -e run_uboot() { echo -n "Test Verified Boot Run: $1: " ${uboot} -d sandbox-u-boot.dtb >${tmp} -c ' +sb bind 0 test.fit; sb load host 0 100 test.fit; fdt addr 100; bootm 100; @@ -54,8 +55,16 @@ echo ${mkimage} -D "${dtc}" echo "Build keys" mkdir -p ${keys} +PUBLIC_EXPONENT=${1} + +if [ x"${PUBLIC_EXPONENT}" = x"" ]; then + PUBLIC_EXPONENT=65537 +fi + # Create an RSA key pair -openssl genrsa -F4 -out ${keys}/dev.key 2048 2>/dev/null +openssl genpkey -algorithm RSA -out ${keys}/dev.key \ + -pkeyopt rsa_keygen_bits:2048 \ + -pkeyopt rsa_keygen_pubexp:${PUBLIC_EXPONENT} 2>/dev/null # Create a certificate containing the public key openssl req -batch -new -x509 -key ${keys}/dev.key -out ${keys}/dev.crt -- 2.0.0.rc0 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot