lib: ecdsa: Implement UCLASS_ECDSA verification on target
authorAlexandru Gagniuc <mr.nuke.me@gmail.com>
Thu, 29 Jul 2021 16:47:16 +0000 (11:47 -0500)
committerPatrice Chotard <patrice.chotard@foss.st.com>
Mon, 16 Aug 2021 08:49:35 +0000 (10:49 +0200)
Implement the crypto_algo .verify() function for ecdsa256. Because
it backends on UCLASS_ECDSA, this change is focused on parsing the
keys from devicetree and passing this information to the specific
UCLASS driver.

Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Patrick Delaunay <patrick.delaunay@foss.st.com>
lib/Kconfig
lib/Makefile
lib/ecdsa/Kconfig [new file with mode: 0644]
lib/ecdsa/Makefile [new file with mode: 0644]
lib/ecdsa/ecdsa-verify.c [new file with mode: 0644]

index 7b445d0..c535147 100644 (file)
@@ -303,6 +303,7 @@ config AES
          supported by the algorithm but only a 128-bit key is supported at
          present.
 
+source lib/ecdsa/Kconfig
 source lib/rsa/Kconfig
 source lib/crypto/Kconfig
 source lib/crypt/Kconfig
index 07c2ccd..8ba745f 100644 (file)
@@ -60,6 +60,7 @@ endif
 
 obj-$(CONFIG_$(SPL_)ACPIGEN) += acpi/
 obj-$(CONFIG_$(SPL_)MD5) += md5.o
+obj-$(CONFIG_ECDSA) += ecdsa/
 obj-$(CONFIG_$(SPL_)RSA) += rsa/
 obj-$(CONFIG_HASH) += hash-checksum.o
 obj-$(CONFIG_SHA1) += sha1.o
diff --git a/lib/ecdsa/Kconfig b/lib/ecdsa/Kconfig
new file mode 100644 (file)
index 0000000..a95c4ff
--- /dev/null
@@ -0,0 +1,23 @@
+config ECDSA
+       bool "Enable ECDSA support"
+       depends on DM
+       help
+         This enables the ECDSA (elliptic curve signature) algorithm for FIT
+         image verification in U-Boot. The ECDSA algorithm is implemented
+         using the driver model, so CONFIG_DM is required by this library.
+         See doc/uImage.FIT/signature.txt for more details.
+         ECDSA is enabled for mkimage regardless of this option.
+
+if ECDSA
+
+config ECDSA_VERIFY
+       bool "Enable ECDSA verification support in U-Boot."
+       help
+         Allow ECDSA signatures to be recognized and verified in U-Boot.
+
+config SPL_ECDSA_VERIFY
+       bool "Enable ECDSA verification support in SPL"
+       help
+         Allow ECDSA signatures to be recognized and verified in SPL.
+
+endif
diff --git a/lib/ecdsa/Makefile b/lib/ecdsa/Makefile
new file mode 100644 (file)
index 0000000..771d6d3
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_$(SPL_)ECDSA_VERIFY) += ecdsa-verify.o
diff --git a/lib/ecdsa/ecdsa-verify.c b/lib/ecdsa/ecdsa-verify.c
new file mode 100644 (file)
index 0000000..0601700
--- /dev/null
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ECDSA signature verification for u-boot
+ *
+ * This implements the firmware-side wrapper for ECDSA verification. It bridges
+ * the struct crypto_algo API to the ECDSA uclass implementations.
+ *
+ * Copyright (c) 2020, Alexandru Gagniuc <mr.nuke.me@gmail.com>
+ */
+
+#include <crypto/ecdsa-uclass.h>
+#include <dm/uclass.h>
+#include <u-boot/ecdsa.h>
+
+/*
+ * Derive size of an ECDSA key from the curve name
+ *
+ * While it's possible to extract the key size by using string manipulation,
+ * use a list of known curves for the time being.
+ */
+static int ecdsa_key_size(const char *curve_name)
+{
+       if (!strcmp(curve_name, "prime256v1"))
+               return 256;
+       else
+               return 0;
+}
+
+static int fdt_get_key(struct ecdsa_public_key *key, const void *fdt, int node)
+{
+       int x_len, y_len;
+
+       key->curve_name = fdt_getprop(fdt, node, "ecdsa,curve", NULL);
+       key->size_bits = ecdsa_key_size(key->curve_name);
+       if (key->size_bits == 0) {
+               debug("Unknown ECDSA curve '%s'", key->curve_name);
+               return -EINVAL;
+       }
+
+       key->x = fdt_getprop(fdt, node, "ecdsa,x-point", &x_len);
+       key->y = fdt_getprop(fdt, node, "ecdsa,y-point", &y_len);
+
+       if (!key->x || !key->y)
+               return -EINVAL;
+
+       if (x_len != (key->size_bits / 8) || y_len != (key->size_bits / 8)) {
+               printf("%s: node=%d, curve@%p x@%p+%i y@%p+%i\n", __func__,
+                      node, key->curve_name, key->x, x_len, key->y, y_len);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ecdsa_verify_hash(struct udevice *dev,
+                            const struct image_sign_info *info,
+                            const void *hash, const void *sig, uint sig_len)
+{
+       const struct ecdsa_ops *ops = device_get_ops(dev);
+       const struct checksum_algo *algo = info->checksum;
+       struct ecdsa_public_key key;
+       int sig_node, key_node, ret;
+
+       if (!ops || !ops->verify)
+               return -ENODEV;
+
+       if (info->required_keynode > 0) {
+               ret = fdt_get_key(&key, info->fdt_blob, info->required_keynode);
+               if (ret < 0)
+                       return ret;
+
+               return ops->verify(dev, &key, hash, algo->checksum_len,
+                                  sig, sig_len);
+       }
+
+       sig_node = fdt_subnode_offset(info->fdt_blob, 0, FIT_SIG_NODENAME);
+       if (sig_node < 0)
+               return -ENOENT;
+
+       /* Try all possible keys under the "/signature" node */
+       fdt_for_each_subnode(key_node, info->fdt_blob, sig_node) {
+               ret = fdt_get_key(&key, info->fdt_blob, key_node);
+               if (ret < 0)
+                       continue;
+
+               ret = ops->verify(dev, &key, hash, algo->checksum_len,
+                                 sig, sig_len);
+
+               /* On success, don't worry about remaining keys */
+               if (!ret)
+                       return 0;
+       }
+
+       return -EPERM;
+}
+
+int ecdsa_verify(struct image_sign_info *info,
+                const struct image_region region[], int region_count,
+                uint8_t *sig, uint sig_len)
+{
+       const struct checksum_algo *algo = info->checksum;
+       uint8_t hash[algo->checksum_len];
+       struct udevice *dev;
+       int ret;
+
+       ret = uclass_first_device_err(UCLASS_ECDSA, &dev);
+       if (ret) {
+               debug("ECDSA: Could not find ECDSA implementation: %d\n", ret);
+               return ret;
+       }
+
+       ret = algo->calculate(algo->name, region, region_count, hash);
+       if (ret < 0)
+               return -EINVAL;
+
+       return ecdsa_verify_hash(dev, info, hash, sig, sig_len);
+}
+
+U_BOOT_CRYPTO_ALGO(ecdsa) = {
+       .name = "ecdsa256",
+       .key_len = ECDSA256_BYTES,
+       .verify = ecdsa_verify,
+};
+
+/*
+ * uclass definition for ECDSA API
+ *
+ * We don't implement any wrappers around ecdsa_ops->verify() because it's
+ * trivial to call ops->verify().
+ */
+UCLASS_DRIVER(ecdsa) = {
+       .id             = UCLASS_ECDSA,
+       .name           = "ecdsa_verifier",
+};