[U-Boot] [PATCH] rsa: add a more flexible way to support different hash algorithms (e.g. sha256)

andreas at oetken.name andreas at oetken.name
Tue Nov 26 13:37:52 CET 2013


I currently need support for rsa-sha256 signatures in u-boot and found out that
the code for signatures is not very generic. Thus adding of different
hash-algorithms for rsa-signatures is not easy to do without copy-pasting the
rsa-code. I attached a patch for how I think it could be better and included
support for rsa-sha256. This is a fast first shot.

diff --git a/common/image-sig.c b/common/image-sig.c
index 973b06d..db15cb6 100644
--- a/common/image-sig.c
+++ b/common/image-sig.c
@@ -14,15 +14,48 @@ DECLARE_GLOBAL_DATA_PTR;
 #endif /* !USE_HOSTCC*/
 #include <image.h>
 #include <rsa.h>
+#include <rsa-checksum.h>

 #define IMAGE_MAX_HASHED_NODES        100

+
+struct checksum_algo checksum_algos[] = {
+  {
+    "sha1",
+    SHA1_SUM_LEN,
+#if IMAGE_ENABLE_SIGN
+    EVP_sha1,
+#else
+    sha1_calculate,
+    padding_sha1_rsa2048,
+#endif
+  },
+  {
+    "sha256",
+    SHA256_SUM_LEN,
+#if IMAGE_ENABLE_SIGN
+    EVP_sha256,
+#else
+    sha256_calculate,
+    padding_sha256_rsa2048,
+#endif
+  }
+};
+
 struct image_sig_algo image_sig_algos[] = {
     {
         "sha1,rsa2048",
         rsa_sign,
         rsa_add_verify_data,
         rsa_verify,
+        &checksum_algos[0],
+    },
+    {
+        "sha256,rsa2048",
+        rsa_sign,
+        rsa_add_verify_data,
+        rsa_verify,
+        &checksum_algos[1],
     }
 };

diff --git a/include/image.h b/include/image.h
index ee6eb8d..47268aa 100644
--- a/include/image.h
+++ b/include/image.h
@@ -822,7 +822,8 @@ int calculate_hash(const void *data, int data_len, const
char *algo,
 # ifdef USE_HOSTCC
 #  define IMAGE_ENABLE_SIGN    1
 #  define IMAGE_ENABLE_VERIFY    0
-#else
+#  include  <openssl/evp.h>
+# else
 #  define IMAGE_ENABLE_SIGN    0
 #  define IMAGE_ENABLE_VERIFY    1
 # endif
@@ -861,6 +862,22 @@ struct image_region {
     int size;
 };

+#if IMAGE_ENABLE_VERIFY
+# include <rsa-checksum.h>
+#endif
+struct checksum_algo {
+  const char *name;
+  const int checksum_len;
+#if IMAGE_ENABLE_SIGN
+  EVP_MD *(*calculate)(void);
+#else
+#if IMAGE_ENABLE_VERIFY
+  void (*calculate)(const struct image_region region[], int region_count,
uint8_t * checksum);
+  const uint8_t * rsa_padding;
+#endif
+#endif
+};
+
 struct image_sig_algo {
     const char *name;        /* Name of algorithm */

@@ -911,6 +928,10 @@ struct image_sig_algo {
     int (*verify)(struct image_sign_info *info,
               const struct image_region region[], int region_count,
               uint8_t *sig, uint sig_len);
+
+    /* pointer to checksum algorithm */
+    struct checksum_algo * checksum;
+
 };

 /**
diff --git a/include/rsa-checksum.h b/include/rsa-checksum.h
new file mode 100644
index 0000000..a318538
--- /dev/null
+++ b/include/rsa-checksum.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2013, Andreas Oetken.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef _RSA_CHECKSUM_H
+#define _RSA_CHECKSUM_H
+
+#include <errno.h>
+#include <image.h>
+#include <sha1.h>
+#include <sha256.h>
+
+#if IMAGE_ENABLE_VERIFY
+extern const uint8_t padding_sha256_rsa2048[];
+extern const uint8_t padding_sha1_rsa2048[];
+
+void sha256_calculate(const struct image_region region[], int region_count,
uint8_t * checksum);
+void sha1_calculate(const struct image_region region[], int region_count,
uint8_t * checksum);
+#endif
+
+#endif
diff --git a/include/rsa.h b/include/rsa.h
index add4c78..3f6bb2c 100644
--- a/include/rsa.h
+++ b/include/rsa.h
@@ -15,6 +15,22 @@
 #include <errno.h>
 #include <image.h>

+
+/**
+ * struct rsa_public_key - holder for a public key
+ *
+ * An RSA public key consists of a modulus (typically called N), the inverse
+ * and R^2, where R is 2^(# key bits).
+ */
+
+struct rsa_public_key {
+    uint len;        /* Length of modulus[] in number of uint32_t */
+    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 */
+};
+
+
 #if IMAGE_ENABLE_SIGN
 /**
  * sign() - calculate and return signature for given input data
diff --git a/lib/rsa/Makefile b/lib/rsa/Makefile
index decd6e5..bf18cdc 100644
--- a/lib/rsa/Makefile
+++ b/lib/rsa/Makefile
@@ -12,7 +12,7 @@ include $(TOPDIR)/config.mk
 LIB    = $(obj)librsa.o

 ifdef CONFIG_FIT_SIGNATURE
-COBJS-$(CONFIG_RSA) += rsa-verify.o
+COBJS-$(CONFIG_RSA) += rsa-verify.o rsa-checksum.o
 endif

 COBJS    := $(sort $(COBJS-y))
diff --git a/lib/rsa/rsa-checksum.c b/lib/rsa/rsa-checksum.c
new file mode 100644
index 0000000..fca065c
--- /dev/null
+++ b/lib/rsa/rsa-checksum.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2013, Andreas Oetken.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <fdtdec.h>
+#include <rsa.h>
+#include <sha1.h>
+#include <sha256.h>
+#include <asm/byteorder.h>
+#include <asm/errno.h>
+#include <asm/unaligned.h>
+
+#define RSA2048_BYTES 256
+
+const uint8_t padding_sha256_rsa2048[RSA2048_BYTES - SHA256_SUM_LEN] = {
+0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x31, 0x30,
0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
0x00, 0x04, 0x20
+};
+
+const uint8_t padding_sha1_rsa2048[RSA2048_BYTES - SHA1_SUM_LEN] = {
+    0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x21, 0x30,
+    0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a,
+    0x05, 0x00, 0x04, 0x14
+};
+
+
+void sha1_calculate(const struct image_region region[], int region_count,
uint8_t * checksum){
+  sha1_context ctx;
+  uint32_t i;
+  i = 0;
+
+  sha1_starts(&ctx);
+  for (i = 0; i < region_count; i++)
+    sha1_update(&ctx, region[i].data, region[i].size);
+  sha1_finish(&ctx, checksum);
+}
+
+void sha256_calculate(const struct image_region region[], int region_count,
uint8_t * checksum){
+  sha256_context ctx;
+  uint32_t i;
+  i = 0;
+
+  sha256_starts(&ctx);
+  for (i = 0; i < region_count; i++)
+    sha256_update(&ctx, region[i].data, region[i].size);
+  sha256_finish(&ctx, checksum);
+}
+
diff --git a/lib/rsa/rsa-sign.c b/lib/rsa/rsa-sign.c
index 549130e..9886e15 100644
--- a/lib/rsa/rsa-sign.c
+++ b/lib/rsa/rsa-sign.c
@@ -159,7 +159,7 @@ static void rsa_remove(void)
     EVP_cleanup();
 }

-static int rsa_sign_with_key(RSA *rsa, const struct image_region region[],
+static int rsa_sign_with_key(RSA *rsa, struct checksum_algo * checksum_algo,
const struct image_region region[],
         int region_count, uint8_t **sigp, uint *sig_size)
 {
     EVP_PKEY *key;
@@ -192,7 +192,7 @@ static int rsa_sign_with_key(RSA *rsa, const struct
image_region region[],
         goto err_create;
     }
     EVP_MD_CTX_init(context);
-    if (!EVP_SignInit(context, EVP_sha1())) {
+    if (!EVP_SignInit(context, checksum_algo->calculate())) {
         ret = rsa_err("Signer setup failed");
         goto err_sign;
     }
@@ -242,7 +242,7 @@ int rsa_sign(struct image_sign_info *info,
     ret = rsa_get_priv_key(info->keydir, info->keyname, &rsa);
     if (ret)
         goto err_priv;
-    ret = rsa_sign_with_key(rsa, region, region_count, sigp, sig_len);
+    ret = rsa_sign_with_key(rsa, info->algo->checksum, region, region_count,
sigp, sig_len);
     if (ret)
         goto err_sign;

diff --git a/lib/rsa/rsa-verify.c b/lib/rsa/rsa-verify.c
index 02cc4e3..6099418 100644
--- a/lib/rsa/rsa-verify.c
+++ b/lib/rsa/rsa-verify.c
@@ -7,24 +7,10 @@
 #include <common.h>
 #include <fdtdec.h>
 #include <rsa.h>
-#include <sha1.h>
 #include <asm/byteorder.h>
 #include <asm/errno.h>
 #include <asm/unaligned.h>

-/**
- * struct rsa_public_key - holder for a public key
- *
- * An RSA public key consists of a modulus (typically called N), the inverse
- * and R^2, where R is 2^(# key bits).
- */
-struct rsa_public_key {
-    uint len;        /* Length of modulus[] in number of uint32_t */
-    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 */
-};
-
 #define UINT64_MULT32(v, multby)  (((uint64_t)(v)) * ((uint32_t)(multby)))

 #define RSA2048_BYTES    (2048 / 8)
@@ -36,38 +22,6 @@ struct rsa_public_key {
 /* This is the maximum signature length that we support, in bits */
 #define RSA_MAX_SIG_BITS    2048

-static const uint8_t padding_sha1_rsa2048[RSA2048_BYTES - SHA1_SUM_LEN] = {
-    0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-    0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x21, 0x30,
-    0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a,
-    0x05, 0x00, 0x04, 0x14
-};

 /**
  * subtract_modulus() - subtract modulus from the given value
@@ -209,7 +163,7 @@ static int pow_mod(const struct rsa_public_key *key,
uint32_t *inout)
 }

 static int rsa_verify_key(const struct rsa_public_key *key, const uint8_t *sig,
-        const uint32_t sig_len, const uint8_t *hash)
+           const uint32_t sig_len, const uint8_t *hash, struct checksum_algo *
algo)
 {
     const uint8_t *padding;
     int pad_len;
@@ -223,6 +177,8 @@ static int rsa_verify_key(const struct rsa_public_key *key,
const uint8_t *sig,
         return -EINVAL;
     }

+    debug("Checksum algorithm: %s", algo->name);
+
     /* Sanity check for stack size */
     if (sig_len > RSA_MAX_SIG_BITS / 8) {
         debug("Signature length %u exceeds maximum %d\n", sig_len,
@@ -239,8 +195,8 @@ static int rsa_verify_key(const struct rsa_public_key *key,
const uint8_t *sig,
         return ret;

     /* Determine padding to use depending on the signature type. */
-    padding = padding_sha1_rsa2048;
-    pad_len = RSA2048_BYTES - SHA1_SUM_LEN;
+    padding = algo->rsa_padding;
+    pad_len = RSA2048_BYTES - algo->checksum_len;

     /* Check pkcs1.5 padding bytes. */
     if (memcmp(buf, padding, pad_len)) {
@@ -309,7 +265,7 @@ static int rsa_verify_with_keynode(struct image_sign_info
*info,
     }

     debug("key length %d\n", key.len);
-    ret = rsa_verify_key(&key, sig, sig_len, hash);
+    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;
@@ -323,12 +279,18 @@ int rsa_verify(struct image_sign_info *info,
            uint8_t *sig, uint sig_len)
 {
     const void *blob = info->fdt_blob;
-    uint8_t hash[SHA1_SUM_LEN];
+    /* Reserve memory for maximum checksum-length */
+    uint8_t hash[RSA2048_BYTES];
     int ndepth, noffset;
     int sig_node, node;
     char name[100];
-    sha1_context ctx;
-    int ret, i;
+    int ret;
+
+    /* Verify that the checksum-length does not exceed the rsa-signature-length
*/
+    if(info->algo->checksum->checksum_len > RSA2048_BYTES){
+      debug("%s: invlaid checksum-algorithm %s for RSA2048\n", __func__,
info->algo->checksum->name);
+      return -EINVAL;
+    }

     sig_node = fdt_subnode_offset(blob, 0, FIT_SIG_NODENAME);
     if (sig_node < 0) {
@@ -336,10 +298,8 @@ int rsa_verify(struct image_sign_info *info,
         return -ENOENT;
     }

-    sha1_starts(&ctx);
-    for (i = 0; i < region_count; i++)
-        sha1_update(&ctx, region[i].data, region[i].size);
-    sha1_finish(&ctx, hash);
+    /* Calculate checksum with checksum-algorithm */
+    info->algo->checksum->calculate(region, region_count, hash);

     /* See if we must use a particular key */
     if (info->required_keynode != -1) {
diff --git a/test/vboot/sign-configs.its b/test/vboot/sign-configs.its
index db2ed79..5b315f3 100644
--- a/test/vboot/sign-configs.its
+++ b/test/vboot/sign-configs.its
@@ -36,7 +36,7 @@
             kernel = "kernel at 1";
             fdt = "fdt at 1";
             signature at 1 {
-                algo = "sha1,rsa2048";
+                algo = "sha256,rsa2048";
                 key-name-hint = "dev";
                 sign-images = "fdt", "kernel";
             };
diff --git a/test/vboot/sign-images.its b/test/vboot/sign-images.its
index f69326a..e2b0446 100644
--- a/test/vboot/sign-images.its
+++ b/test/vboot/sign-images.its
@@ -27,7 +27,7 @@
             compression = "none";
             fdt-version = <1>;
             signature at 1 {
-                algo = "sha1,rsa2048";
+                algo = "sha256,rsa2048";
                 key-name-hint = "dev";
             };
         };


More information about the U-Boot mailing list