[PATCH v5 01/15] ecdsa: fix support of secp521r1
Philippe Reynes
philippe.reynes at softathome.com
Tue Apr 21 23:09:40 CEST 2026
Current implementation of ecdsa only supports key len aligned on
8 bits. But the curve secp521r1 uses a key of 521 bits which is not
aligned on 8 bits. In this commit, we update the keys management
for ecdsa to support keys that are not aligned on 8 bits.
Signed-off-by: Philippe Reynes <philippe.reynes at softathome.com>
---
v2:
- intitial version
v3:
- fix typo in comments
v4:
- fix commit message
- clean code with DIV_ROUND_UP-
- duplicate data before shifting
- support ecdsa521 and secp521r1
- clean code
v5:
- check that x and y are not null before using them
- free keys when keys not found
lib/ecdsa/ecdsa-libcrypto.c | 63 ++++++++++++++++++++++++++++++-
lib/ecdsa/ecdsa-verify.c | 75 +++++++++++++++++++++++++++++++++----
lib/fdt-libcrypto.c | 2 +-
tools/image-sig-host.c | 7 ++++
4 files changed, 137 insertions(+), 10 deletions(-)
diff --git a/lib/ecdsa/ecdsa-libcrypto.c b/lib/ecdsa/ecdsa-libcrypto.c
index c4bfb2cec61..41590d22a51 100644
--- a/lib/ecdsa/ecdsa-libcrypto.c
+++ b/lib/ecdsa/ecdsa-libcrypto.c
@@ -26,6 +26,8 @@
#include <openssl/ec.h>
#include <openssl/bn.h>
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+
/* Image signing context for openssl-libcrypto */
struct signer {
EVP_PKEY *evp_key; /* Pointer to EVP_PKEY object */
@@ -41,10 +43,26 @@ struct ecdsa_public_key {
int size_bits;
};
+static char *memdup(char *buf, size_t size)
+{
+ char *dup;
+
+ dup = malloc(size);
+ if (dup)
+ memcpy(dup, buf, size);
+
+ return dup;
+}
+
static int fdt_get_key(struct ecdsa_public_key *key, const void *fdt, int node)
{
+ const char *x;
+ const char *y;
int x_len;
int y_len;
+ int expected_len;
+
+ memset(key, 0, sizeof(*key));
key->curve_name = fdt_getprop(fdt, node, "ecdsa,curve", NULL);
if (!key->curve_name)
@@ -54,6 +72,8 @@ static int fdt_get_key(struct ecdsa_public_key *key, const void *fdt, int node)
key->size_bits = 256;
else if (!strcmp(key->curve_name, "secp384r1"))
key->size_bits = 384;
+ else if (!strcmp(key->curve_name, "secp521r1"))
+ key->size_bits = 521;
else
return -EINVAL;
@@ -63,12 +83,43 @@ static int fdt_get_key(struct ecdsa_public_key *key, const void *fdt, int node)
if (!key->x || !key->y)
return -EINVAL;
- if (x_len != key->size_bits / 8 || y_len != key->size_bits / 8)
+ /*
+ * The public key is stored as an array of u32, so if the key size is
+ * not a multiple of 32 (for example 521), we may have extra bytes.
+ * To avoid any issue later, we shift the x and y pointer to the first
+ * useful byte.
+ */
+ expected_len = DIV_ROUND_UP(key->size_bits, 8);
+
+ if (x_len < expected_len || y_len < expected_len)
return -EINVAL;
+ x = memdup((char *)key->x + (x_len - expected_len), expected_len);
+ if (!x) {
+ fprintf(stderr, "Cannot allocate memory for point X");
+ return -ENOMEM;
+ }
+ key->x = (const uint8_t *)x;
+
+ y = memdup((char *)key->y + (y_len - expected_len), expected_len);
+ if (!y) {
+ fprintf(stderr, "Cannot allocate memory for point Y");
+ free((char *)x);
+ return -ENOMEM;
+ }
+ key->y = (const uint8_t *)y;
+
return 0;
}
+static void fdt_free_key(struct ecdsa_public_key *key)
+{
+ if (!key)
+ return;
+ free((char *)key->x);
+ free((char *)key->y);
+}
+
static int read_key_from_fdt(struct signer *ctx, const void *fdt, int node)
{
struct ecdsa_public_key pubkey;
@@ -89,8 +140,11 @@ static int read_key_from_fdt(struct signer *ctx, const void *fdt, int node)
nid = NID_X9_62_prime256v1;
} else if (!strcmp(pubkey.curve_name, "secp384r1")) {
nid = NID_secp384r1;
+ } else if (!strcmp(pubkey.curve_name, "secp521r1")) {
+ nid = NID_secp521r1;
} else {
fprintf(stderr, "Unsupported curve name: '%s'\n", pubkey.curve_name);
+ fdt_free_key(&pubkey);
return -EINVAL;
}
@@ -100,6 +154,7 @@ static int read_key_from_fdt(struct signer *ctx, const void *fdt, int node)
ec_key = EC_KEY_new_by_curve_name(nid);
if (!ec_key) {
fprintf(stderr, "Failed to allocate EC_KEY for curve %s\n", pubkey.curve_name);
+ fdt_free_key(&pubkey);
return -ENOMEM;
}
@@ -108,10 +163,11 @@ static int read_key_from_fdt(struct signer *ctx, const void *fdt, int node)
if (!point) {
fprintf(stderr, "Failed to allocate EC_POINT\n");
EC_KEY_free(ec_key);
+ fdt_free_key(&pubkey);
return -ENOMEM;
}
- len = pubkey.size_bits / 8;
+ len = DIV_ROUND_UP(pubkey.size_bits, 8);
uint8_t buf[1 + len * 2];
@@ -123,6 +179,7 @@ static int read_key_from_fdt(struct signer *ctx, const void *fdt, int node)
fprintf(stderr, "Failed to convert (x,y) point to EC_POINT\n");
EC_POINT_free(point);
EC_KEY_free(ec_key);
+ fdt_free_key(&pubkey);
return -EINVAL;
}
@@ -130,11 +187,13 @@ static int read_key_from_fdt(struct signer *ctx, const void *fdt, int node)
fprintf(stderr, "Failed to set EC_POINT as public key\n");
EC_POINT_free(point);
EC_KEY_free(ec_key);
+ fdt_free_key(&pubkey);
return -EINVAL;
}
fprintf(stderr, "Successfully loaded ECDSA key from FDT node %d\n", node);
EC_POINT_free(point);
+ fdt_free_key(&pubkey);
ctx->ecdsa_key = ec_key;
return 0;
diff --git a/lib/ecdsa/ecdsa-verify.c b/lib/ecdsa/ecdsa-verify.c
index 629b662cf6c..e503627f490 100644
--- a/lib/ecdsa/ecdsa-verify.c
+++ b/lib/ecdsa/ecdsa-verify.c
@@ -10,6 +10,7 @@
#include <crypto/ecdsa-uclass.h>
#include <dm/uclass.h>
+#include <malloc.h>
#include <u-boot/ecdsa.h>
/*
@@ -24,13 +25,19 @@ static int ecdsa_key_size(const char *curve_name)
return 256;
else if (!strcmp(curve_name, "secp384r1"))
return 384;
+ else if (!strcmp(curve_name, "secp521r1"))
+ return 521;
return 0;
}
static int fdt_get_key(struct ecdsa_public_key *key, const void *fdt, int node)
{
- int x_len, y_len;
+ int expected_len, x_len, y_len;
+ const char *x;
+ const char *y;
+
+ memset(key, 0, sizeof(*key));
key->curve_name = fdt_getprop(fdt, node, "ecdsa,curve", NULL);
if (!key->curve_name) {
@@ -50,15 +57,46 @@ static int fdt_get_key(struct ecdsa_public_key *key, const void *fdt, int node)
if (!key->x || !key->y)
return -EINVAL;
- if (x_len != (key->size_bits / 8) || y_len != (key->size_bits / 8)) {
+ /*
+ * The public key is stored as an array of u32, so if the key size is
+ * not a multiple of 32 (for example 521), we may have extra bytes.
+ * To avoid any issue later, we shift the x and y pointer to the first
+ * useful byte.
+ */
+ expected_len = DIV_ROUND_UP(key->size_bits, 8);
+
+ if (x_len < expected_len || y_len < expected_len) {
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;
}
+ x = memdup((char *)key->x + (x_len - expected_len), expected_len);
+ if (!x) {
+ debug("Cannot allocate memory for point X");
+ return -ENOMEM;
+ }
+ key->x = (const uint8_t *)x;
+
+ y = memdup((char *)key->y + (y_len - expected_len), expected_len);
+ if (!y) {
+ debug("Cannot allocate memory for point Y");
+ free((char *)x);
+ return -ENOMEM;
+ }
+ key->y = (const uint8_t *)y;
+
return 0;
}
+static void fdt_free_key(struct ecdsa_public_key *key)
+{
+ if (!key)
+ return;
+ free((char *)key->x);
+ free((char *)key->y);
+}
+
static int ecdsa_verify_hash(struct udevice *dev,
const struct image_sign_info *info,
const void *hash, const void *sig, uint sig_len)
@@ -73,11 +111,16 @@ static int ecdsa_verify_hash(struct udevice *dev,
if (info->required_keynode > 0) {
ret = fdt_get_key(&key, info->fdt_blob, info->required_keynode);
- if (ret < 0)
+ if (ret < 0) {
+ fdt_free_key(&key);
return ret;
+ }
+
+ ret = ops->verify(dev, &key, hash, algo->checksum_len,
+ sig, sig_len);
+ fdt_free_key(&key);
- return ops->verify(dev, &key, hash, algo->checksum_len,
- sig, sig_len);
+ return ret;
}
sig_node = fdt_subnode_offset(info->fdt_blob, 0, FIT_SIG_NODENAME);
@@ -87,15 +130,21 @@ static int ecdsa_verify_hash(struct udevice *dev,
/* 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)
+ if (ret < 0) {
+ fdt_free_key(&key);
continue;
+ }
ret = ops->verify(dev, &key, hash, algo->checksum_len,
sig, sig_len);
/* On success, don't worry about remaining keys */
- if (!ret)
+ if (!ret) {
+ fdt_free_key(&key);
return 0;
+ }
+
+ fdt_free_key(&key);
}
return -EPERM;
@@ -135,6 +184,18 @@ U_BOOT_CRYPTO_ALGO(ecdsa384) = {
.verify = ecdsa_verify,
};
+U_BOOT_CRYPTO_ALGO(ecdsa521) = {
+ .name = "ecdsa521",
+ .key_len = ECDSA521_BYTES,
+ .verify = ecdsa_verify,
+};
+
+U_BOOT_CRYPTO_ALGO(secp521r1) = {
+ .name = "secp521r1",
+ .key_len = ECDSA521_BYTES,
+ .verify = ecdsa_verify,
+};
+
/*
* uclass definition for ECDSA API
*
diff --git a/lib/fdt-libcrypto.c b/lib/fdt-libcrypto.c
index ecb0344c8f6..090246b44e9 100644
--- a/lib/fdt-libcrypto.c
+++ b/lib/fdt-libcrypto.c
@@ -10,7 +10,7 @@
int fdt_add_bignum(void *blob, int noffset, const char *prop_name,
BIGNUM *num, int num_bits)
{
- int nwords = num_bits / 32;
+ int nwords = (num_bits + 31) / 32;
int size;
uint32_t *buf, *ptr;
BIGNUM *tmp, *big2, *big32, *big2_32;
diff --git a/tools/image-sig-host.c b/tools/image-sig-host.c
index 5285263c616..285547994ca 100644
--- a/tools/image-sig-host.c
+++ b/tools/image-sig-host.c
@@ -83,6 +83,13 @@ struct crypto_algo crypto_algos[] = {
.add_verify_data = ecdsa_add_verify_data,
.verify = ecdsa_verify,
},
+ {
+ .name = "ecdsa521",
+ .key_len = ECDSA521_BYTES,
+ .sign = ecdsa_sign,
+ .add_verify_data = ecdsa_add_verify_data,
+ .verify = ecdsa_verify,
+ },
{
.name = "secp521r1",
.key_len = ECDSA521_BYTES,
--
2.43.0
More information about the U-Boot
mailing list