]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - lib/rsa/rsa-verify.c
rsa: Use checksum algorithms from struct hash_algo
[karo-tx-uboot.git] / lib / rsa / rsa-verify.c
index 587da5b470685de78ebc86bf611c0abf8fd0c387..60126d22884b6e29dd18887ee7fbad90869ed049 100644 (file)
 #include <asm/errno.h>
 #include <asm/types.h>
 #include <asm/unaligned.h>
+#include <dm.h>
 #else
 #include "fdt_host.h"
 #include "mkimage.h"
 #include <fdt_support.h>
 #endif
-#include <rsa.h>
-#include <sha1.h>
-#include <sha256.h>
+#include <u-boot/rsa-mod-exp.h>
+#include <u-boot/rsa.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))
-
-/**
- * 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[])
-{
-       uint32_t i;
-
-       for (i = 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);
-}
+/* Default public exponent for backward compatibility */
+#define RSA_DEFAULT_PUBEXP     65537
 
 /**
- * montgomery_mul() - Perform montgomery mutitply
- *
- * Operation: montgomery result[] = a[] * b[] / n0inv % modulus
+ * rsa_verify_key() - Verify a signature against some data using RSA Key
  *
- * @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);
-}
-
-/**
- * pow_mod() - in-place public exponentiation
+ * Verify a RSA PKCS1.5 signature against an expected hash using
+ * the RSA Key properties in prop structure.
  *
- * @key:       RSA key
- * @inout:     Big-endian word array containing value and result
+ * @prop:      Specifies key
+ * @sig:       Signature
+ * @sig_len:   Number of bytes in signature
+ * @hash:      Pointer to the expected hash
+ * @algo:      Checksum algo structure having information on RSA padding etc.
+ * @return 0 if verified, -ve on error
  */
-static int pow_mod(const struct rsa_public_key *key, uint32_t *inout)
-{
-       uint32_t *result, *ptr;
-       uint i;
-
-       /* 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];
-       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 */
-       }
-       montgomery_mul(key, result, acc, val);  /* result = XX * a / R mod M */
-
-       /* 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 *prop, const uint8_t *sig,
                          const uint32_t sig_len, const uint8_t *hash,
                          struct checksum_algo *algo)
 {
        const uint8_t *padding;
        int pad_len;
        int ret;
+#if !defined(USE_HOSTCC)
+       struct udevice *mod_exp_dev;
+#endif
 
-       if (!key || !sig || !hash || !algo)
+       if (!prop || !sig || !hash || !algo)
                return -EIO;
 
-       if (sig_len != (key->len * sizeof(uint32_t))) {
+       if (sig_len != (prop->num_bits / 8)) {
                debug("Signature is of incorrect length %d\n", sig_len);
                return -EINVAL;
        }
@@ -189,13 +65,23 @@ 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)];
+       uint8_t buf[sig_len];
 
-       memcpy(buf, sig, sig_len);
+#if !defined(USE_HOSTCC)
+       ret = uclass_get_device(UCLASS_MOD_EXP, 0, &mod_exp_dev);
+       if (ret) {
+               printf("RSA: Can't find Modular Exp implementation\n");
+               return -EINVAL;
+       }
 
-       ret = pow_mod(key, buf);
-       if (ret)
+       ret = rsa_mod_exp(mod_exp_dev, sig, sig_len, prop, buf);
+#else
+       ret = rsa_mod_exp_sw(sig, sig_len, prop, buf);
+#endif
+       if (ret) {
+               debug("Error in Modular exponentation\n");
                return ret;
+       }
 
        padding = algo->rsa_padding;
        pad_len = algo->pad_len - algo->checksum_len;
@@ -215,65 +101,57 @@ 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]);
-}
-
+/**
+ * rsa_verify_with_keynode() - Verify a signature against some data using
+ * information in node with prperties of RSA Key like modulus, exponent etc.
+ *
+ * Parse sign-node and fill a key_prop structure with properties of the
+ * key.  Verify a RSA PKCS1.5 signature against an expected hash using
+ * the properties parsed
+ *
+ * @info:      Specifies key and FIT information
+ * @hash:      Pointer to the expected hash
+ * @sig:       Signature
+ * @sig_len:   Number of bytes in signature
+ * @node:      Node having the RSA Key properties
+ * @return 0 if verified, -ve on error
+ */
 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;
-       int ret;
+       struct key_prop prop;
+       int length;
+       int ret = 0;
 
        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);
-       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;
-       }
+       ret = rsa_verify_key(&prop, sig, sig_len, hash, info->algo->checksum);
 
-       return 0;
+       return ret;
 }
 
 int rsa_verify(struct image_sign_info *info,
@@ -306,7 +184,12 @@ int rsa_verify(struct image_sign_info *info,
        }
 
        /* Calculate checksum with checksum-algorithm */
-       info->algo->checksum->calculate(region, region_count, hash);
+       ret = info->algo->checksum->calculate(info->algo->checksum->name,
+                                       region, region_count, hash);
+       if (ret < 0) {
+               debug("%s: Error in checksum calculation\n", __func__);
+               return -EINVAL;
+       }
 
        /* See if we must use a particular key */
        if (info->required_keynode != -1) {