Merge https://source.denx.de/u-boot/custodians/u-boot-sunxi
[platform/kernel/u-boot.git] / lib / rsa / rsa-verify.c
index 0d548f8..3840764 100644 (file)
@@ -1,12 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (c) 2013, Google Inc.
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #ifndef USE_HOSTCC
 #include <common.h>
 #include <fdtdec.h>
+#include <log.h>
+#include <malloc.h>
 #include <asm/types.h>
 #include <asm/byteorder.h>
 #include <linux/errno.h>
 #include "mkimage.h"
 #include <fdt_support.h>
 #endif
+#include <linux/kconfig.h>
 #include <u-boot/rsa-mod-exp.h>
 #include <u-boot/rsa.h>
 
+#ifndef __UBOOT__
+/*
+ * NOTE:
+ * Since host tools, like mkimage, make use of openssl library for
+ * RSA encryption, rsa_verify_with_pkey()/rsa_gen_key_prop() are
+ * of no use and should not be compiled in.
+ * So just turn off CONFIG_RSA_VERIFY_WITH_PKEY.
+ */
+
+#undef CONFIG_RSA_VERIFY_WITH_PKEY
+#endif
+
 /* Default public exponent for backward compatibility */
 #define RSA_DEFAULT_PUBEXP     65537
 
@@ -58,31 +72,261 @@ static int rsa_verify_padding(const uint8_t *msg, const int pad_len,
        return ret;
 }
 
+int padding_pkcs_15_verify(struct image_sign_info *info,
+                          uint8_t *msg, int msg_len,
+                          const uint8_t *hash, int hash_len)
+{
+       struct checksum_algo *checksum = info->checksum;
+       int ret, pad_len = msg_len - checksum->checksum_len;
+
+       /* Check pkcs1.5 padding bytes. */
+       ret = rsa_verify_padding(msg, pad_len, checksum);
+       if (ret) {
+               debug("In RSAVerify(): Padding check failed!\n");
+               return -EINVAL;
+       }
+
+       /* Check hash. */
+       if (memcmp((uint8_t *)msg + pad_len, hash, msg_len - pad_len)) {
+               debug("In RSAVerify(): Hash check failed!\n");
+               return -EACCES;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_FIT_RSASSA_PSS
+static void u32_i2osp(uint32_t val, uint8_t *buf)
+{
+       buf[0] = (uint8_t)((val >> 24) & 0xff);
+       buf[1] = (uint8_t)((val >> 16) & 0xff);
+       buf[2] = (uint8_t)((val >>  8) & 0xff);
+       buf[3] = (uint8_t)((val >>  0) & 0xff);
+}
+
+/**
+ * mask_generation_function1() - generate an octet string
+ *
+ * Generate an octet string used to check rsa signature.
+ * It use an input octet string and a hash function.
+ *
+ * @checksum:  A Hash function
+ * @seed:      Specifies an input variable octet string
+ * @seed_len:  Size of the input octet string
+ * @output:    Specifies the output octet string
+ * @output_len:        Size of the output octet string
+ * @return 0 if the octet string was correctly generated, others on error
+ */
+static int mask_generation_function1(struct checksum_algo *checksum,
+                                    uint8_t *seed, int seed_len,
+                                    uint8_t *output, int output_len)
+{
+       struct image_region region[2];
+       int ret = 0, i, i_output = 0, region_count = 2;
+       uint32_t counter = 0;
+       uint8_t buf_counter[4], *tmp;
+       int hash_len = checksum->checksum_len;
+
+       memset(output, 0, output_len);
+
+       region[0].data = seed;
+       region[0].size = seed_len;
+       region[1].data = &buf_counter[0];
+       region[1].size = 4;
+
+       tmp = malloc(hash_len);
+       if (!tmp) {
+               debug("%s: can't allocate array tmp\n", __func__);
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       while (i_output < output_len) {
+               u32_i2osp(counter, &buf_counter[0]);
+
+               ret = checksum->calculate(checksum->name,
+                                         region, region_count,
+                                         tmp);
+               if (ret < 0) {
+                       debug("%s: Error in checksum calculation\n", __func__);
+                       goto out;
+               }
+
+               i = 0;
+               while ((i_output < output_len) && (i < hash_len)) {
+                       output[i_output] = tmp[i];
+                       i_output++;
+                       i++;
+               }
+
+               counter++;
+       }
+
+out:
+       free(tmp);
+
+       return ret;
+}
+
+static int compute_hash_prime(struct checksum_algo *checksum,
+                             uint8_t *pad, int pad_len,
+                             uint8_t *hash, int hash_len,
+                             uint8_t *salt, int salt_len,
+                             uint8_t *hprime)
+{
+       struct image_region region[3];
+       int ret, region_count = 3;
+
+       region[0].data = pad;
+       region[0].size = pad_len;
+       region[1].data = hash;
+       region[1].size = hash_len;
+       region[2].data = salt;
+       region[2].size = salt_len;
+
+       ret = checksum->calculate(checksum->name, region, region_count, hprime);
+       if (ret < 0) {
+               debug("%s: Error in checksum calculation\n", __func__);
+               goto out;
+       }
+
+out:
+       return ret;
+}
+
+/*
+ * padding_pss_verify() - verify the pss padding of a signature
+ *
+ * Only works with a rsa_pss_saltlen:-2 (default value) right now
+ * saltlen:-1 "set the salt length to the digest length" is currently
+ * not supported.
+ *
+ * @info:      Specifies key and FIT information
+ * @msg:       byte array of message, len equal to msg_len
+ * @msg_len:   Message length
+ * @hash:      Pointer to the expected hash
+ * @hash_len:  Length of the hash
+ */
+int padding_pss_verify(struct image_sign_info *info,
+                      uint8_t *msg, int msg_len,
+                      const uint8_t *hash, int hash_len)
+{
+       uint8_t *masked_db = NULL;
+       int masked_db_len = msg_len - hash_len - 1;
+       uint8_t *h = NULL, *hprime = NULL;
+       int h_len = hash_len;
+       uint8_t *db_mask = NULL;
+       int db_mask_len = masked_db_len;
+       uint8_t *db = NULL, *salt = NULL;
+       int db_len = masked_db_len, salt_len = msg_len - hash_len - 2;
+       uint8_t pad_zero[8] = { 0 };
+       int ret, i, leftmost_bits = 1;
+       uint8_t leftmost_mask;
+       struct checksum_algo *checksum = info->checksum;
+
+       /* first, allocate everything */
+       masked_db = malloc(masked_db_len);
+       h = malloc(h_len);
+       db_mask = malloc(db_mask_len);
+       db = malloc(db_len);
+       salt = malloc(salt_len);
+       hprime = malloc(hash_len);
+       if (!masked_db || !h || !db_mask || !db || !salt || !hprime) {
+               printf("%s: can't allocate some buffer\n", __func__);
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       /* step 4: check if the last byte is 0xbc */
+       if (msg[msg_len - 1] != 0xbc) {
+               printf("%s: invalid pss padding (0xbc is missing)\n", __func__);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /* step 5 */
+       memcpy(masked_db, msg, masked_db_len);
+       memcpy(h, msg + masked_db_len, h_len);
+
+       /* step 6 */
+       leftmost_mask = (0xff >> (8 - leftmost_bits)) << (8 - leftmost_bits);
+       if (masked_db[0] & leftmost_mask) {
+               printf("%s: invalid pss padding ", __func__);
+               printf("(leftmost bit of maskedDB not zero)\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /* step 7 */
+       mask_generation_function1(checksum, h, h_len, db_mask, db_mask_len);
+
+       /* step 8 */
+       for (i = 0; i < db_len; i++)
+               db[i] = masked_db[i] ^ db_mask[i];
+
+       /* step 9 */
+       db[0] &= 0xff >> leftmost_bits;
+
+       /* step 10 */
+       if (db[0] != 0x01) {
+               printf("%s: invalid pss padding ", __func__);
+               printf("(leftmost byte of db isn't 0x01)\n");
+               ret = EINVAL;
+               goto out;
+       }
+
+       /* step 11 */
+       memcpy(salt, &db[1], salt_len);
+
+       /* step 12 & 13 */
+       compute_hash_prime(checksum, pad_zero, 8,
+                          (uint8_t *)hash, hash_len,
+                          salt, salt_len, hprime);
+
+       /* step 14 */
+       ret = memcmp(h, hprime, hash_len);
+
+out:
+       free(hprime);
+       free(salt);
+       free(db);
+       free(db_mask);
+       free(h);
+       free(masked_db);
+
+       return ret;
+}
+#endif
+
+#if CONFIG_IS_ENABLED(FIT_SIGNATURE) || CONFIG_IS_ENABLED(RSA_VERIFY_WITH_PKEY)
 /**
  * rsa_verify_key() - Verify a signature against some data using RSA Key
  *
  * Verify a RSA PKCS1.5 signature against an expected hash using
  * the RSA Key properties in prop structure.
  *
+ * @info:      Specifies key and FIT information
  * @prop:      Specifies key
  * @sig:       Signature
  * @sig_len:   Number of bytes in signature
  * @hash:      Pointer to the expected hash
  * @key_len:   Number of bytes in rsa key
- * @algo:      Checksum algo structure having information on DER encoding etc.
  * @return 0 if verified, -ve on error
  */
-static int rsa_verify_key(struct key_prop *prop, const uint8_t *sig,
+static int rsa_verify_key(struct image_sign_info *info,
+                         struct key_prop *prop, const uint8_t *sig,
                          const uint32_t sig_len, const uint8_t *hash,
-                         const uint32_t key_len, struct checksum_algo *algo)
+                         const uint32_t key_len)
 {
-       int pad_len;
        int ret;
 #if !defined(USE_HOSTCC)
        struct udevice *mod_exp_dev;
 #endif
+       struct checksum_algo *checksum = info->checksum;
+       struct padding_algo *padding = info->padding;
+       int hash_len;
 
-       if (!prop || !sig || !hash || !algo)
+       if (!prop || !sig || !hash || !checksum)
                return -EIO;
 
        if (sig_len != (prop->num_bits / 8)) {
@@ -90,7 +334,7 @@ static int rsa_verify_key(struct key_prop *prop, const uint8_t *sig,
                return -EINVAL;
        }
 
-       debug("Checksum algorithm: %s", algo->name);
+       debug("Checksum algorithm: %s", checksum->name);
 
        /* Sanity check for stack size */
        if (sig_len > RSA_MAX_SIG_BITS / 8) {
@@ -100,6 +344,7 @@ static int rsa_verify_key(struct key_prop *prop, const uint8_t *sig,
        }
 
        uint8_t buf[sig_len];
+       hash_len = checksum->checksum_len;
 
 #if !defined(USE_HOSTCC)
        ret = uclass_get_device(UCLASS_MOD_EXP, 0, &mod_exp_dev);
@@ -117,24 +362,60 @@ static int rsa_verify_key(struct key_prop *prop, const uint8_t *sig,
                return ret;
        }
 
-       pad_len = key_len - algo->checksum_len;
-
-       /* Check pkcs1.5 padding bytes. */
-       ret = rsa_verify_padding(buf, pad_len, algo);
+       ret = padding->verify(info, buf, key_len, hash, hash_len);
        if (ret) {
-               debug("In RSAVerify(): Padding check failed!\n");
-               return -EINVAL;
+               debug("In RSAVerify(): padding check failed!\n");
+               return ret;
        }
 
-       /* Check hash. */
-       if (memcmp((uint8_t *)buf + pad_len, hash, sig_len - pad_len)) {
-               debug("In RSAVerify(): Hash check failed!\n");
-               return -EACCES;
+       return 0;
+}
+#endif
+
+#if CONFIG_IS_ENABLED(RSA_VERIFY_WITH_PKEY)
+/**
+ * rsa_verify_with_pkey() - Verify a signature against some data using
+ * only modulus and exponent as RSA key properties.
+ * @info:      Specifies key information
+ * @hash:      Pointer to the expected hash
+ * @sig:       Signature
+ * @sig_len:   Number of bytes in signature
+ *
+ * Parse a RSA public key blob in DER format pointed to in @info and fill
+ * a key_prop structure with properties of the key. Then verify a RSA PKCS1.5
+ * signature against an expected hash using the calculated properties.
+ *
+ * Return      0 if verified, -ve on error
+ */
+int rsa_verify_with_pkey(struct image_sign_info *info,
+                        const void *hash, uint8_t *sig, uint sig_len)
+{
+       struct key_prop *prop;
+       int ret;
+
+       /* Public key is self-described to fill key_prop */
+       ret = rsa_gen_key_prop(info->key, info->keylen, &prop);
+       if (ret) {
+               debug("Generating necessary parameter for decoding failed\n");
+               return ret;
        }
 
-       return 0;
+       ret = rsa_verify_key(info, prop, sig, sig_len, hash,
+                            info->crypto->key_len);
+
+       rsa_free_key_prop(prop);
+
+       return ret;
+}
+#else
+int rsa_verify_with_pkey(struct image_sign_info *info,
+                        const void *hash, uint8_t *sig, uint sig_len)
+{
+       return -EACCES;
 }
+#endif
 
+#if CONFIG_IS_ENABLED(FIT_SIGNATURE)
 /**
  * rsa_verify_with_keynode() - Verify a signature against some data using
  * information in node with prperties of RSA Key like modulus, exponent etc.
@@ -158,12 +439,20 @@ static int rsa_verify_with_keynode(struct image_sign_info *info,
        struct key_prop prop;
        int length;
        int ret = 0;
+       const char *algo;
 
        if (node < 0) {
                debug("%s: Skipping invalid node", __func__);
                return -EBADF;
        }
 
+       algo = fdt_getprop(blob, node, "algo", NULL);
+       if (strcmp(info->name, algo)) {
+               debug("%s: Wrong algo: have %s, expected %s", __func__,
+                     info->name, algo);
+               return -EFAULT;
+       }
+
        prop.num_bits = fdtdec_get_int(blob, node, "rsa,num-bits", 0);
 
        prop.n0inv = fdtdec_get_int(blob, node, "rsa,n0-inverse", 0);
@@ -178,13 +467,77 @@ static int rsa_verify_with_keynode(struct image_sign_info *info,
 
        prop.rr = fdt_getprop(blob, node, "rsa,r-squared", NULL);
 
-       if (!prop.num_bits || !prop.modulus) {
+       if (!prop.num_bits || !prop.modulus || !prop.rr) {
                debug("%s: Missing RSA key info", __func__);
                return -EFAULT;
        }
 
-       ret = rsa_verify_key(&prop, sig, sig_len, hash,
-                            info->crypto->key_len, info->checksum);
+       ret = rsa_verify_key(info, &prop, sig, sig_len, hash,
+                            info->crypto->key_len);
+
+       return ret;
+}
+#else
+static int rsa_verify_with_keynode(struct image_sign_info *info,
+                                  const void *hash, uint8_t *sig,
+                                  uint sig_len, int node)
+{
+       return -EACCES;
+}
+#endif
+
+int rsa_verify_hash(struct image_sign_info *info,
+                   const uint8_t *hash, uint8_t *sig, uint sig_len)
+{
+       int ret = -EACCES;
+
+       if (CONFIG_IS_ENABLED(RSA_VERIFY_WITH_PKEY) && !info->fdt_blob) {
+               /* don't rely on fdt properties */
+               ret = rsa_verify_with_pkey(info, hash, sig, sig_len);
+
+               return ret;
+       }
+
+       if (CONFIG_IS_ENABLED(FIT_SIGNATURE)) {
+               const void *blob = info->fdt_blob;
+               int ndepth, noffset;
+               int sig_node, node;
+               char name[100];
+
+               sig_node = fdt_subnode_offset(blob, 0, FIT_SIG_NODENAME);
+               if (sig_node < 0) {
+                       debug("%s: No signature node found\n", __func__);
+                       return -ENOENT;
+               }
+
+               /* See if we must use a particular key */
+               if (info->required_keynode != -1) {
+                       ret = rsa_verify_with_keynode(info, hash, sig, sig_len,
+                                                     info->required_keynode);
+                       return ret;
+               }
+
+               /* Look for a key that matches our hint */
+               snprintf(name, sizeof(name), "key-%s", info->keyname);
+               node = fdt_subnode_offset(blob, sig_node, name);
+               ret = rsa_verify_with_keynode(info, hash, sig, sig_len, node);
+               if (!ret)
+                       return ret;
+
+               /* No luck, so try each of the keys in turn */
+               for (ndepth = 0, noffset = fdt_next_node(blob, sig_node,
+                                                        &ndepth);
+                    (noffset >= 0) && (ndepth > 0);
+                    noffset = fdt_next_node(blob, noffset, &ndepth)) {
+                       if (ndepth == 1 && noffset != node) {
+                               ret = rsa_verify_with_keynode(info, hash,
+                                                             sig, sig_len,
+                                                             noffset);
+                               if (!ret)
+                                       break;
+                       }
+               }
+       }
 
        return ret;
 }
@@ -193,12 +546,8 @@ int rsa_verify(struct image_sign_info *info,
               const struct image_region region[], int region_count,
               uint8_t *sig, uint sig_len)
 {
-       const void *blob = info->fdt_blob;
        /* Reserve memory for maximum checksum-length */
        uint8_t hash[info->crypto->key_len];
-       int ndepth, noffset;
-       int sig_node, node;
-       char name[100];
        int ret;
 
        /*
@@ -207,17 +556,11 @@ int rsa_verify(struct image_sign_info *info,
         */
        if (info->checksum->checksum_len >
            info->crypto->key_len) {
-               debug("%s: invlaid checksum-algorithm %s for %s\n",
+               debug("%s: invalid checksum-algorithm %s for %s\n",
                      __func__, info->checksum->name, info->crypto->name);
                return -EINVAL;
        }
 
-       sig_node = fdt_subnode_offset(blob, 0, FIT_SIG_NODENAME);
-       if (sig_node < 0) {
-               debug("%s: No signature node found\n", __func__);
-               return -ENOENT;
-       }
-
        /* Calculate checksum with checksum-algorithm */
        ret = info->checksum->calculate(info->checksum->name,
                                        region, region_count, hash);
@@ -226,32 +569,21 @@ int rsa_verify(struct image_sign_info *info,
                return -EINVAL;
        }
 
-       /* See if we must use a particular key */
-       if (info->required_keynode != -1) {
-               ret = rsa_verify_with_keynode(info, hash, sig, sig_len,
-                       info->required_keynode);
-               if (!ret)
-                       return ret;
-       }
+       return rsa_verify_hash(info, hash, sig, sig_len);
+}
 
-       /* Look for a key that matches our hint */
-       snprintf(name, sizeof(name), "key-%s", info->keyname);
-       node = fdt_subnode_offset(blob, sig_node, name);
-       ret = rsa_verify_with_keynode(info, hash, sig, sig_len, node);
-       if (!ret)
-               return ret;
+#ifndef USE_HOSTCC
 
-       /* No luck, so try each of the keys in turn */
-       for (ndepth = 0, noffset = fdt_next_node(info->fit, sig_node, &ndepth);
-                       (noffset >= 0) && (ndepth > 0);
-                       noffset = fdt_next_node(info->fit, noffset, &ndepth)) {
-               if (ndepth == 1 && noffset != node) {
-                       ret = rsa_verify_with_keynode(info, hash, sig, sig_len,
-                                                     noffset);
-                       if (!ret)
-                               break;
-               }
-       }
+U_BOOT_CRYPTO_ALGO(rsa2048) = {
+       .name = "rsa2048",
+       .key_len = RSA2048_BYTES,
+       .verify = rsa_verify,
+};
 
-       return ret;
-}
+U_BOOT_CRYPTO_ALGO(rsa4096) = {
+       .name = "rsa4096",
+       .key_len = RSA4096_BYTES,
+       .verify = rsa_verify,
+};
+
+#endif