Implement generalised RSA public exponents for verified boot
authorMichael van der Westhuizen <michael@smart-africa.com>
Wed, 2 Jul 2014 08:17:26 +0000 (10:17 +0200)
committerTom Rini <trini@ti.com>
Sat, 9 Aug 2014 15:17:01 +0000 (11:17 -0400)
Remove the verified boot limitation that only allows a single
RSA public exponent of 65537 (F4).  This change 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.

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.Bott@ipaccess.com>
Signed-off-by: Andrew Wishart <Andrew.Wishart@ipaccess.com>
Signed-off-by: Neil Piercy <Neil.Piercy@ipaccess.com>
Signed-off-by: Michael van der Westhuizen <michael@smart-africa.com>
Cc: Simon Glass <sjg@chromium.org>
doc/uImage.FIT/signature.txt
include/u-boot/rsa.h
lib/rsa/rsa-sign.c
lib/rsa/rsa-verify.c
test/vboot/vboot_test.sh

index a6ab543de43c18e6c11c2f3b9a4c893014848636..b2f89fcc65d0fd2a14693b26ce7f93ce0fe3412d 100644 (file)
@@ -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
 
index 325751ab7e1896c548e2c229708ad1a04670c2f5..fd08a617fbb129034375415365b67d573ef9c6a3 100644 (file)
@@ -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
index 83f5e87838950a7fb63f2de0aac6891cf40e2abd..f4d433867a2d28ebcf2827c5ead4a8c8c92d6cc2 100644 (file)
@@ -260,11 +260,58 @@ err_priv:
        return ret;
 }
 
+/*
+ * rsa_get_exponent(): - Get the public exponent from an RSA key
+ */
+static int rsa_get_exponent(RSA *key, uint64_t *e)
+{
+       int ret;
+       BIGNUM *bn_te;
+       uint64_t te;
+
+       ret = -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,
-                  BIGNUM **r_squaredp)
+int rsa_get_params(RSA *key, uint64_t *exponent, uint32_t *n0_invp,
+                  BIGNUM **modulusp, BIGNUM **r_squaredp)
 {
        BIGNUM *big1, *big2, *big32, *big2_32;
        BIGNUM *n, *r, *r_squared, *tmp;
@@ -286,6 +333,9 @@ int rsa_get_params(RSA *key, uint32_t *n0_invp, BIGNUM **modulusp,
                return -ENOMEM;
        }
 
+       if (0 != rsa_get_exponent(key, exponent))
+               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 exponent;
        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, &exponent, &n0_inv, &modulus, &r_squared);
        if (ret)
                return ret;
        bits = BN_num_bits(modulus);
@@ -441,6 +492,9 @@ int rsa_add_verify_data(struct image_sign_info *info, void *keydest)
                ret = fdt_setprop_u32(keydest, node, "rsa,num-bits", bits);
        if (!ret)
                ret = fdt_setprop_u32(keydest, node, "rsa,n0-inverse", n0_inv);
+       if (!ret) {
+               ret = fdt_setprop_u64(keydest, node, "rsa,exponent", exponent);
+       }
        if (!ret) {
                ret = fdt_add_bignum(keydest, node, "rsa,modulus", modulus,
                                     bits);
index bcb906368d0dc273eb462a023a8d0477f2c51795..c5bcdb60e814ee2ad01c6fb5fe548f761397d86f 100644 (file)
@@ -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))
 
+/* Default public exponent for backward compatibility */
+#define RSA_DEFAULT_PUBEXP     65537
+
 /**
  * subtract_modulus() - subtract modulus from the given value
  *
@@ -122,6 +125,48 @@ static void montgomery_mul(const struct rsa_public_key *key,
                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
  *
@@ -132,6 +177,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 +187,48 @@ 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;
        }
-       montgomery_mul(key, result, acc, val);  /* result = XX * a / R mod M */
+
+       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))
@@ -229,6 +305,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 *public_exponent;
+       int length;
        int ret;
 
        if (node < 0) {
@@ -241,6 +319,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);
+       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) {
index 8074fc6adc0708c984297c2172ad41ded4f0995e..6d7abb82bd72b85a861e3524a7b92c34e853817a 100755 (executable)
@@ -54,8 +54,16 @@ echo ${mkimage} -D "${dtc}"
 echo "Build keys"
 mkdir -p ${keys}
 
+PUBLIC_EXPONENT=${1}
+
+if [ -z "${PUBLIC_EXPONENT}" ]; 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