[U-Boot] [PATCH v2] Implement generalised RSA public exponents for verified boot
Simon Glass
sjg at chromium.org
Fri May 30 21:04:22 CEST 2014
Hi Michael,
On 26 May 2014 08:56, <michael at smart-africa.com> wrote:
> From: Michael van der Westhuizen <michael at 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.
This looks good. I mostly have some nits below.
>
> Tested with the following public exponents: 3, 5, 17, 257, 39981,
> 50457, 65537 and 4294967297.
>
> Signed-off-by: Andrew Bott <Andrew.Bott at ipaccess.com>
> Signed-off-by: Andrew Wishart <Andrew.Wishart at ipaccess.com>
> Signed-off-by: Neil Piercy <Neil.Piercy at ipaccess.com>
> Signed-off-by: Michael van der Westhuizen <michael at 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)
rsa_get_exponent() might be better. Also s/e/exponent/ or perhaps exp
(although I see I made the same mistake in the following function).
> +{
> + int ret;
> + BIGNUM *bn_te;
> + uint64_t te;
> +
> + ret = -1;
-EINVAL
> + 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)
If you don't mind, you could change 'e' to 'exp' or 'exponent' in the
same patch.
> {
> 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;
And here.
> 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
Then why use hex? Can we decide on either decimal or hex?
> +
> /**
> * 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)
s/k/keyp/ or something like that.
Also is this function able to just use ffs() or similar?
> +{
> + uint64_t e;
s/e/exponent/ or exp
> + const uint nbits = (sizeof(e) * 8);
> +
> + *k = 0;
How about a local variable for this instead of using *k everywhere?
> + 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);
If this is 64-but, then perhaps 1ULL << 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);
What is invalid about it? Perhaps expand the message?
> + 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 */
Join these two comments, also the style should be:
/*
* The bit at ...
* ...
*/
> + memcpy(a_scaled, acc, key->len * 4);
What is 4? is it sizeof(uint32_t?). I wonder if we could replace the
line immediately above and remove this memcpy()?
For example, if you did:
montgomery_mul(key, result, val, key->rr); /* acc = a * RR / R mod n */
> +
> + 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);
Do you need this special case, or if bit 0 of e is always 1, perhaps
you can just extend the loop by one more step?
>
> /* 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;
s/pe/public_exponent/ or similar?
> + 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
Do we need this, or can we just do:
if [ -n "${PUBLIC_EXPONENT}" ]; then
> +
> # 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
>
Regards,
Simon
More information about the U-Boot
mailing list