ECDSA verification is dispatched through a UCLASS_ECDSA driver but the only implementations in tree are backed by hardware or firmware: the ASPEED Caliptra mailbox and the STM32MP ROM API. Everywhere else enabling CONFIG_ECDSA_VERIFY builds the crypto_algo entries but ecdsa_verify() fails with -ENODEV because uclass_first_device_err() finds nothing.
Add an equivalent software driver built on the MbedTLS ECP library supporting the prime256v1 and secp384r1 curves used by the "ecdsa256" and "ecdsa384" FIT signature algorithms. The public key is passed to MbedTLS as an uncompressed SEC1 point rather than by assigning the point MPIs directly which validates the encoding and avoids reaching into MBEDTLS_PRIVATE() internals. Only verification is implemented so no RNG is needed. Signed-off-by: Ayoub Zaki <[email protected]> --- lib/ecdsa/Kconfig | 12 ++++ lib/ecdsa/Makefile | 1 + lib/ecdsa/ecdsa-mbedtls.c | 141 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 lib/ecdsa/ecdsa-mbedtls.c diff --git a/lib/ecdsa/Kconfig b/lib/ecdsa/Kconfig index ca13b6bfa1f..a5971645148 100644 --- a/lib/ecdsa/Kconfig +++ b/lib/ecdsa/Kconfig @@ -15,6 +15,18 @@ config ECDSA_VERIFY help Allow ECDSA signatures to be recognized and verified in U-Boot. +config ECDSA_VERIFY_MBEDTLS + bool "Verify ECDSA signatures in software using MbedTLS" + depends on ECDSA_VERIFY && MBEDTLS_LIB + select MBEDTLS_LIB_ECDSA + help + Provide a software implementation of ECDSA signature verification, + built on the MbedTLS ECP library. + + Without this ECDSA_VERIFY relies on a platform-specific UCLASS_ECDSA + driver backed by a hardware engine or a ROM API and boards that have + neither cannot verify ECDSA-signed FIT images at all. + config SPL_ECDSA_VERIFY bool "Enable ECDSA verification support in SPL" depends on SPL diff --git a/lib/ecdsa/Makefile b/lib/ecdsa/Makefile index 9db9f7b17b9..65e96bd31b1 100644 --- a/lib/ecdsa/Makefile +++ b/lib/ecdsa/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_$(PHASE_)ECDSA_VERIFY) += ecdsa-verify.o +obj-$(CONFIG_$(PHASE_)ECDSA_VERIFY_MBEDTLS) += ecdsa-mbedtls.o diff --git a/lib/ecdsa/ecdsa-mbedtls.c b/lib/ecdsa/ecdsa-mbedtls.c new file mode 100644 index 00000000000..d0e3199cc1d --- /dev/null +++ b/lib/ecdsa/ecdsa-mbedtls.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Software ECDSA signature verification backed by the MbedTLS ECP library. + * + * This provides a UCLASS_ECDSA implementation for platforms that have no + * hardware or ROM-based ECDSA engine, mirroring what rsa-mod-exp.c does for + * RSA. It only implements verification while signing is done on the host by + * mkimage + * + * Copyright (c) 2026 Embetrix Embedded Systems Solutions <[email protected]> + */ + +#include <dm.h> +#include <log.h> +#include <crypto/ecdsa-uclass.h> +#include <u-boot/ecdsa.h> + +#include <mbedtls/bignum.h> +#include <mbedtls/ecdsa.h> +#include <mbedtls/ecp.h> + +/* Uncompressed point: 0x04 || X || Y, with X and Y at most 384 bits */ +#define ECDSA_MAX_KEY_BYTES (ECDSA384_BYTES) +#define ECDSA_POINT_BYTES (1 + 2 * ECDSA_MAX_KEY_BYTES) + +/* + * Map the "ecdsa,curve" property to an MbedTLS group id. + * + * Only curves that ecdsa_key_size() in ecdsa-verify.c knows about are listed + * a key using any other curve is rejected before reaching us. + */ +static mbedtls_ecp_group_id ecdsa_mbedtls_group_id(const char *curve_name) +{ + if (!strcmp(curve_name, "prime256v1")) + return MBEDTLS_ECP_DP_SECP256R1; + else if (!strcmp(curve_name, "secp384r1")) + return MBEDTLS_ECP_DP_SECP384R1; + + return MBEDTLS_ECP_DP_NONE; +} + +static int ecdsa_mbedtls_verify(struct udevice *dev, + const struct ecdsa_public_key *pubkey, + const void *hash, size_t hash_len, + const void *signature, size_t sig_len) +{ + u8 point[ECDSA_POINT_BYTES]; + mbedtls_ecp_group_id gid; + mbedtls_ecp_group grp; + mbedtls_ecp_point q; + mbedtls_mpi r, s; + size_t key_len; + int ret; + + gid = ecdsa_mbedtls_group_id(pubkey->curve_name); + if (gid == MBEDTLS_ECP_DP_NONE) { + debug("%s: unsupported curve '%s'\n", __func__, + pubkey->curve_name); + return -EOPNOTSUPP; + } + + key_len = pubkey->size_bits / 8; + if (key_len > ECDSA_MAX_KEY_BYTES) + return -EINVAL; + + /* The signature is a raw (R, S) pair, each one key_len bytes wide. */ + if (sig_len != 2 * key_len) { + debug("%s: signature is %zu bytes, expected %zu\n", __func__, + sig_len, 2 * key_len); + return -EINVAL; + } + + mbedtls_ecp_group_init(&grp); + mbedtls_ecp_point_init(&q); + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + + ret = mbedtls_ecp_group_load(&grp, gid); + if (ret) { + debug("%s: cannot load curve '%s': -0x%04x\n", __func__, + pubkey->curve_name, -ret); + ret = -EINVAL; + goto out; + } + + /* + * Feed the key in as an uncompressed SEC1 point rather than poking at + * the MPIs behind mbedtls_ecp_point directly; this validates the + * encoding for us and keeps us out of MBEDTLS_PRIVATE() internals. + */ + point[0] = 0x04; + memcpy(point + 1, pubkey->x, key_len); + memcpy(point + 1 + key_len, pubkey->y, key_len); + + ret = mbedtls_ecp_point_read_binary(&grp, &q, point, 1 + 2 * key_len); + if (!ret) + ret = mbedtls_ecp_check_pubkey(&grp, &q); + if (ret) { + debug("%s: bad public key: -0x%04x\n", __func__, -ret); + ret = -EINVAL; + goto out; + } + + ret = mbedtls_mpi_read_binary(&r, signature, key_len); + if (!ret) + ret = mbedtls_mpi_read_binary(&s, signature + key_len, key_len); + if (ret) { + ret = -EINVAL; + goto out; + } + + ret = mbedtls_ecdsa_verify(&grp, hash, hash_len, &q, &r, &s); + if (ret) { + debug("%s: signature verification failed: -0x%04x\n", __func__, + -ret); + ret = -EPERM; + } + +out: + mbedtls_mpi_free(&s); + mbedtls_mpi_free(&r); + mbedtls_ecp_point_free(&q); + mbedtls_ecp_group_free(&grp); + + return ret; +} + +static const struct ecdsa_ops ecdsa_mbedtls_ops = { + .verify = ecdsa_mbedtls_verify, +}; + +U_BOOT_DRIVER(ecdsa_mbedtls) = { + .name = "ecdsa_mbedtls", + .id = UCLASS_ECDSA, + .ops = &ecdsa_mbedtls_ops, + .flags = DM_FLAG_PRE_RELOC, +}; + +U_BOOT_DRVINFO(ecdsa_mbedtls) = { + .name = "ecdsa_mbedtls", +}; -- 2.43.0
