Current implementation of ecdsa only supports key len aligned on 8 bits. But the curve secp521r1 use a key of 521 bits which is not aligned on 8 bits. In this commit, we update the key management for ecdsa to support key of any lenght.
Signed-off-by: Philippe Reynes <[email protected]> --- v2: - intitial version lib/ecdsa/ecdsa-libcrypto.c | 21 +++++++++++++++++++-- lib/ecdsa/ecdsa-verify.c | 24 ++++++++++++++++++++++-- lib/fdt-libcrypto.c | 2 +- tools/image-sig-host.c | 2 +- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/lib/ecdsa/ecdsa-libcrypto.c b/lib/ecdsa/ecdsa-libcrypto.c index c4bfb2cec61..6f25ccc783d 100644 --- a/lib/ecdsa/ecdsa-libcrypto.c +++ b/lib/ecdsa/ecdsa-libcrypto.c @@ -45,6 +45,7 @@ static int fdt_get_key(struct ecdsa_public_key *key, const void *fdt, int node) { int x_len; int y_len; + int expected_len; key->curve_name = fdt_getprop(fdt, node, "ecdsa,curve", NULL); if (!key->curve_name) @@ -54,6 +55,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,7 +66,19 @@ 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 if + * 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 = (key->size_bits + 7) / 8; + if (x_len > expected_len) + key->x += x_len - expected_len; + if (y_len > expected_len) + key->y += y_len - expected_len; + + if (x_len < expected_len || y_len < expected_len) return -EINVAL; return 0; @@ -89,6 +104,8 @@ 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); return -EINVAL; @@ -111,7 +128,7 @@ static int read_key_from_fdt(struct signer *ctx, const void *fdt, int node) return -ENOMEM; } - len = pubkey.size_bits / 8; + len = (pubkey.size_bits + 7) / 8; uint8_t buf[1 + len * 2]; diff --git a/lib/ecdsa/ecdsa-verify.c b/lib/ecdsa/ecdsa-verify.c index 629b662cf6c..d87d95a5002 100644 --- a/lib/ecdsa/ecdsa-verify.c +++ b/lib/ecdsa/ecdsa-verify.c @@ -24,13 +24,15 @@ 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; key->curve_name = fdt_getprop(fdt, node, "ecdsa,curve", NULL); if (!key->curve_name) { @@ -50,7 +52,19 @@ 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 if + * 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 = (key->size_bits + 7) / 8; + if (x_len > expected_len) + key->x += x_len - expected_len; + if (y_len > expected_len) + key->y += y_len - expected_len; + + 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; @@ -135,6 +149,12 @@ U_BOOT_CRYPTO_ALGO(ecdsa384) = { .verify = ecdsa_verify, }; +U_BOOT_CRYPTO_ALGO(ecdsa521) = { + .name = "ecdsa521", + .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..a2272b196e7 100644 --- a/tools/image-sig-host.c +++ b/tools/image-sig-host.c @@ -84,7 +84,7 @@ struct crypto_algo crypto_algos[] = { .verify = ecdsa_verify, }, { - .name = "secp521r1", + .name = "ecdsa521", .key_len = ECDSA521_BYTES, .sign = ecdsa_sign, .add_verify_data = ecdsa_add_verify_data, -- 2.43.0

