[PATCH] crypto: sunxi: add CE ECDSA verifier

James Hilliard james.hilliard1 at gmail.com
Fri Jun 26 02:34:00 CEST 2026


The Allwinner sunxi Crypto Engine found on the H616 includes an ECC
engine which can verify ECDSA signatures. Add a UCLASS_ECDSA provider
so FIT signature verification can use the hardware block from U-Boot
proper and SPL.

The CE takes explicit curve parameters, so provide parameter tables for
secp224r1, prime256v1, secp384r1 and secp521r1 using the curves
accepted by U-Boot's FIT ECDSA parser. Extend the generic ECDSA signing
and verification plumbing for the additional algorithm names and for
non-32-bit coordinate sizes.

The CE input buffer uses fixed-width curve fields. Reuse the parameter
packing logic for the message digest as well, so wider digests are
truncated to the leftmost curve-width bytes according to ECDSA rules.

Signed-off-by: James Hilliard <james.hilliard1 at gmail.com>
---
 MAINTAINERS                    |   1 +
 doc/mkimage.1                  |   3 +
 doc/usage/fit/signature.rst    |   3 +-
 drivers/crypto/Kconfig         |  26 ++
 drivers/crypto/Makefile        |   1 +
 drivers/crypto/sunxi_ecdsa.c   | 612 +++++++++++++++++++++++++++++++++
 include/u-boot/ecdsa.h         |  16 +
 include/u-boot/fdt-libcrypto.h |   4 +-
 lib/ecdsa/Kconfig              |   2 +-
 lib/ecdsa/ecdsa-libcrypto.c    | 109 +++---
 lib/ecdsa/ecdsa-verify.c       |  33 +-
 lib/fdt-libcrypto.c            |  57 +--
 tools/image-sig-host.c         |   7 +
 13 files changed, 764 insertions(+), 110 deletions(-)
 create mode 100644 drivers/crypto/sunxi_ecdsa.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 370bcff56c1..d62252e1d1b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -770,6 +770,7 @@ F:	arch/arm/include/asm/arch-sunxi/
 F:	arch/arm/mach-sunxi/
 F:	board/sunxi/
 F:	drivers/clk/sunxi/
+F:	drivers/crypto/sunxi_ecdsa.c
 F:	drivers/phy/allwinner/
 F:	drivers/pinctrl/sunxi/
 F:	drivers/video/sunxi/
diff --git a/doc/mkimage.1 b/doc/mkimage.1
index 9a2d07cee75..b6ba88dd314 100644
--- a/doc/mkimage.1
+++ b/doc/mkimage.1
@@ -459,7 +459,10 @@ lb.
 rsa2048
 rsa3072
 rsa4096
+ecdsa224
 ecdsa256
+ecdsa384
+secp521r1
 .TE
 .RE
 .
diff --git a/doc/usage/fit/signature.rst b/doc/usage/fit/signature.rst
index da08cc75c3a..4f35f44bc8a 100644
--- a/doc/usage/fit/signature.rst
+++ b/doc/usage/fit/signature.rst
@@ -132,7 +132,8 @@ rsa,n0-inverse
 For ECDSA the following are mandatory:
 
 ecdsa,curve
-    Name of ECDSA curve (e.g. "prime256v1")
+    Name of ECDSA curve, such as "secp224r1", "prime256v1",
+    "secp384r1", or "secp521r1"
 
 ecdsa,x-point
     Public key X coordinate as a big-endian multi-word integer
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 0d58e3910fe..9b0a4f18e24 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -6,6 +6,32 @@ source "drivers/crypto/aes/Kconfig"
 
 source "drivers/crypto/fsl/Kconfig"
 
+config SUNXI_ECDSA
+	bool "Allwinner sunxi CE ECDSA verifier"
+	depends on ARCH_SUNXI
+	depends on ECDSA_VERIFY
+	depends on CLK && DM_RESET
+	help
+	  Select this option to enable ECDSA signature verification using
+	  the Crypto Engine found in Allwinner sunxi SoCs. FIT image
+	  signatures can then be checked by the hardware accelerator in
+	  U-Boot proper. Digests wider than the selected curve are
+	  truncated according to ECDSA rules.
+
+config SPL_SUNXI_ECDSA
+	bool "Allwinner sunxi CE ECDSA verifier in SPL"
+	depends on ARCH_SUNXI
+	depends on SPL_DM
+	depends on SPL_OF_CONTROL
+	depends on SPL_ECDSA_VERIFY
+	select SPL_CRYPTO
+	help
+	  Select this option to enable ECDSA signature verification in SPL
+	  using the Crypto Engine found in Allwinner sunxi SoCs. This allows
+	  SPL FIT image signatures to be checked by the hardware accelerator
+	  before U-Boot proper is loaded. Digests wider than the selected
+	  curve are truncated according to ECDSA rules.
+
 source "drivers/crypto/aspeed/Kconfig"
 
 source "drivers/crypto/nuvoton/Kconfig"
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index e4a4482b7f3..88291ae235f 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -4,6 +4,7 @@
 #	http://www.samsung.com
 
 obj-$(CONFIG_EXYNOS_ACE_SHA)	+= ace_sha.o
+obj-$(CONFIG_$(PHASE_)SUNXI_ECDSA) += sunxi_ecdsa.o
 obj-y += aes/
 obj-y += rsa_mod_exp/
 obj-y += fsl/
diff --git a/drivers/crypto/sunxi_ecdsa.c b/drivers/crypto/sunxi_ecdsa.c
new file mode 100644
index 00000000000..e74239d640c
--- /dev/null
+++ b/drivers/crypto/sunxi_ecdsa.c
@@ -0,0 +1,612 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2026 James Hilliard
+ */
+
+#include <cpu_func.h>
+#include <crypto/ecdsa-uclass.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <reset.h>
+#include <log.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <string.h>
+#include <asm/cache.h>
+#include <asm/arch/cpu.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+
+#define SUNXI_CE_TDA			0x00
+#define SUNXI_CE_ICR			0x08
+#define SUNXI_CE_ISR			0x0c
+#define SUNXI_CE_TLR			0x10
+#define SUNXI_CE_ERR			0x18
+
+#define SUNXI_CE_CHAN			0
+#define SUNXI_CE_CHAN_INT		BIT(SUNXI_CE_CHAN)
+#define SUNXI_CE_CHAN_ERR_MASK		(0xff << (SUNXI_CE_CHAN * 8))
+#define SUNXI_CE_TASK_INT		BIT(31)
+#define SUNXI_CE_METHOD_ECC		33
+#define SUNXI_CE_TLR_METHOD_SHIFT	8
+#define SUNXI_CE_ECC_OP_VERIFY		7
+#define SUNXI_CE_ECC_OP_SHIFT		16
+#define SUNXI_CE_WORD_SHIFT		2
+#define SUNXI_CE_TIMEOUT_US		1000000
+
+#define SUN50I_H616_CCU_CE_CLK		0x680
+#define SUN50I_H616_CCU_CE_BGR		0x68c
+#define SUN50I_H616_CCU_MBUS_GATE	0x804
+#define SUN50I_H616_CCU_CE_CLK_SRC_PLL_PERIPH0_2X BIT(24)
+#define SUN50I_H616_CCU_CE_CLK_N_MASK	GENMASK(9, 8)
+#define SUN50I_H616_CCU_CE_CLK_M_MASK	GENMASK(3, 0)
+#define SUN50I_H616_CCU_CE_CLK_M_4	3
+#define SUN50I_H616_CCU_CE_CLK_GATE	BIT(31)
+#define SUN50I_H616_CCU_CE_BUS_GATE	BIT(0)
+#define SUN50I_H616_CCU_CE_BUS_RST	BIT(16)
+#define SUN50I_H616_CCU_MBUS_GATE_CE	BIT(2)
+
+#define SUNXI_ECDSA_PARAMS		12
+#define SUNXI_ECDSA_MAX_WORDS		17
+#define SUNXI_ECDSA_MAX_BYTES		(SUNXI_ECDSA_MAX_WORDS * sizeof(u32))
+#define SUNXI_ECDSA_MAX_SRC_BYTES	(SUNXI_ECDSA_PARAMS * SUNXI_ECDSA_MAX_BYTES)
+
+struct sunxi_ce_sg {
+	u32 addr;
+	u32 len;
+};
+
+struct sunxi_ce_task {
+	u32 chan_id;
+	u32 comm_ctl;
+	u32 sym_ctl;
+	u32 asym_ctl;
+	u32 key_addr;
+	u32 iv_addr;
+	u32 ctr_addr;
+	u32 data_len;
+	struct sunxi_ce_sg src[8];
+	struct sunxi_ce_sg dst[8];
+	u32 next;
+	u32 reserved[3];
+};
+
+struct sunxi_ecdsa_job {
+	struct sunxi_ce_task task __aligned(ARCH_DMA_MINALIGN);
+	u8 src[SUNXI_ECDSA_MAX_SRC_BYTES] __aligned(ARCH_DMA_MINALIGN);
+	u8 dst[SUNXI_ECDSA_MAX_BYTES] __aligned(ARCH_DMA_MINALIGN);
+};
+
+struct sunxi_ecdsa_curve {
+	const char *name;
+	const u8 *p;
+	const u8 *a;
+	const u8 *gx;
+	const u8 *gy;
+	const u8 *n;
+	u16 bits;
+	u8 bytes;
+	u8 words;
+};
+
+struct sunxi_ecdsa_variant {
+	void (*spl_enable_clocks)(void);
+};
+
+struct sunxi_ecdsa_priv {
+	void __iomem *base;
+	const struct sunxi_ecdsa_variant *variant;
+	struct clk_bulk clks;
+	struct reset_ctl_bulk resets;
+};
+
+static const u8 ecdsa_p224_p[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x01,
+};
+
+static const u8 ecdsa_p224_a[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xfe,
+};
+
+static const u8 ecdsa_p224_gx[] = {
+	0xb7, 0x0e, 0x0c, 0xbd, 0x6b, 0xb4, 0xbf, 0x7f,
+	0x32, 0x13, 0x90, 0xb9, 0x4a, 0x03, 0xc1, 0xd3,
+	0x56, 0xc2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xd6,
+	0x11, 0x5c, 0x1d, 0x21,
+};
+
+static const u8 ecdsa_p224_gy[] = {
+	0xbd, 0x37, 0x63, 0x88, 0xb5, 0xf7, 0x23, 0xfb,
+	0x4c, 0x22, 0xdf, 0xe6, 0xcd, 0x43, 0x75, 0xa0,
+	0x5a, 0x07, 0x47, 0x64, 0x44, 0xd5, 0x81, 0x99,
+	0x85, 0x00, 0x7e, 0x34,
+};
+
+static const u8 ecdsa_p224_n[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
+	0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
+	0x5c, 0x5c, 0x2a, 0x3d,
+};
+
+static const u8 ecdsa_p256_p[] = {
+	0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+static const u8 ecdsa_p256_a[] = {
+	0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
+};
+
+static const u8 ecdsa_p256_gx[] = {
+	0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47,
+	0xf8, 0xbc, 0xe6, 0xe5, 0x63, 0xa4, 0x40, 0xf2,
+	0x77, 0x03, 0x7d, 0x81, 0x2d, 0xeb, 0x33, 0xa0,
+	0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98, 0xc2, 0x96,
+};
+
+static const u8 ecdsa_p256_gy[] = {
+	0x4f, 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b,
+	0x8e, 0xe7, 0xeb, 0x4a, 0x7c, 0x0f, 0x9e, 0x16,
+	0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, 0xce,
+	0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5,
+};
+
+static const u8 ecdsa_p256_n[] = {
+	0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84,
+	0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51,
+};
+
+static const u8 ecdsa_p384_p[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+	0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+};
+
+static const u8 ecdsa_p384_a[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+	0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfc,
+};
+
+static const u8 ecdsa_p384_gx[] = {
+	0xaa, 0x87, 0xca, 0x22, 0xbe, 0x8b, 0x05, 0x37,
+	0x8e, 0xb1, 0xc7, 0x1e, 0xf3, 0x20, 0xad, 0x74,
+	0x6e, 0x1d, 0x3b, 0x62, 0x8b, 0xa7, 0x9b, 0x98,
+	0x59, 0xf7, 0x41, 0xe0, 0x82, 0x54, 0x2a, 0x38,
+	0x55, 0x02, 0xf2, 0x5d, 0xbf, 0x55, 0x29, 0x6c,
+	0x3a, 0x54, 0x5e, 0x38, 0x72, 0x76, 0x0a, 0xb7,
+};
+
+static const u8 ecdsa_p384_gy[] = {
+	0x36, 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c, 0x6f,
+	0x5d, 0x9e, 0x98, 0xbf, 0x92, 0x92, 0xdc, 0x29,
+	0xf8, 0xf4, 0x1d, 0xbd, 0x28, 0x9a, 0x14, 0x7c,
+	0xe9, 0xda, 0x31, 0x13, 0xb5, 0xf0, 0xb8, 0xc0,
+	0x0a, 0x60, 0xb1, 0xce, 0x1d, 0x7e, 0x81, 0x9d,
+	0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5f,
+};
+
+static const u8 ecdsa_p384_n[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xc7, 0x63, 0x4d, 0x81, 0xf4, 0x37, 0x2d, 0xdf,
+	0x58, 0x1a, 0x0d, 0xb2, 0x48, 0xb0, 0xa7, 0x7a,
+	0xec, 0xec, 0x19, 0x6a, 0xcc, 0xc5, 0x29, 0x73,
+};
+
+static const u8 ecdsa_p521_p[] = {
+	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,
+};
+
+static const u8 ecdsa_p521_a[] = {
+	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, 0xfc,
+};
+
+static const u8 ecdsa_p521_gx[] = {
+	0x00, 0xc6, 0x85, 0x8e, 0x06, 0xb7, 0x04, 0x04,
+	0xe9, 0xcd, 0x9e, 0x3e, 0xcb, 0x66, 0x23, 0x95,
+	0xb4, 0x42, 0x9c, 0x64, 0x81, 0x39, 0x05, 0x3f,
+	0xb5, 0x21, 0xf8, 0x28, 0xaf, 0x60, 0x6b, 0x4d,
+	0x3d, 0xba, 0xa1, 0x4b, 0x5e, 0x77, 0xef, 0xe7,
+	0x59, 0x28, 0xfe, 0x1d, 0xc1, 0x27, 0xa2, 0xff,
+	0xa8, 0xde, 0x33, 0x48, 0xb3, 0xc1, 0x85, 0x6a,
+	0x42, 0x9b, 0xf9, 0x7e, 0x7e, 0x31, 0xc2, 0xe5,
+	0xbd, 0x66,
+};
+
+static const u8 ecdsa_p521_gy[] = {
+	0x01, 0x18, 0x39, 0x29, 0x6a, 0x78, 0x9a, 0x3b,
+	0xc0, 0x04, 0x5c, 0x8a, 0x5f, 0xb4, 0x2c, 0x7d,
+	0x1b, 0xd9, 0x98, 0xf5, 0x44, 0x49, 0x57, 0x9b,
+	0x44, 0x68, 0x17, 0xaf, 0xbd, 0x17, 0x27, 0x3e,
+	0x66, 0x2c, 0x97, 0xee, 0x72, 0x99, 0x5e, 0xf4,
+	0x26, 0x40, 0xc5, 0x50, 0xb9, 0x01, 0x3f, 0xad,
+	0x07, 0x61, 0x35, 0x3c, 0x70, 0x86, 0xa2, 0x72,
+	0xc2, 0x40, 0x88, 0xbe, 0x94, 0x76, 0x9f, 0xd1,
+	0x66, 0x50,
+};
+
+static const u8 ecdsa_p521_n[] = {
+	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, 0xfa, 0x51, 0x86, 0x87, 0x83, 0xbf, 0x2f,
+	0x96, 0x6b, 0x7f, 0xcc, 0x01, 0x48, 0xf7, 0x09,
+	0xa5, 0xd0, 0x3b, 0xb5, 0xc9, 0xb8, 0x89, 0x9c,
+	0x47, 0xae, 0xbb, 0x6f, 0xb7, 0x1e, 0x91, 0x38,
+	0x64, 0x09,
+};
+
+static const struct sunxi_ecdsa_curve sunxi_ecdsa_curves[] = {
+	{
+		.name = "secp224r1",
+		.bits = 224,
+		.bytes = 28,
+		.words = 7,
+		.p = ecdsa_p224_p,
+		.a = ecdsa_p224_a,
+		.gx = ecdsa_p224_gx,
+		.gy = ecdsa_p224_gy,
+		.n = ecdsa_p224_n,
+	}, {
+		.name = "prime256v1",
+		.bits = 256,
+		.bytes = 32,
+		.words = 8,
+		.p = ecdsa_p256_p,
+		.a = ecdsa_p256_a,
+		.gx = ecdsa_p256_gx,
+		.gy = ecdsa_p256_gy,
+		.n = ecdsa_p256_n,
+	}, {
+		.name = "secp384r1",
+		.bits = 384,
+		.bytes = 48,
+		.words = 12,
+		.p = ecdsa_p384_p,
+		.a = ecdsa_p384_a,
+		.gx = ecdsa_p384_gx,
+		.gy = ecdsa_p384_gy,
+		.n = ecdsa_p384_n,
+	}, {
+		.name = "secp521r1",
+		.bits = 521,
+		.bytes = 66,
+		.words = 17,
+		.p = ecdsa_p521_p,
+		.a = ecdsa_p521_a,
+		.gx = ecdsa_p521_gx,
+		.gy = ecdsa_p521_gy,
+		.n = ecdsa_p521_n,
+	},
+};
+
+static const struct sunxi_ecdsa_curve *
+sunxi_ecdsa_find_curve(const struct ecdsa_public_key *pubkey)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(sunxi_ecdsa_curves); i++) {
+		if (!strcmp(pubkey->curve_name, sunxi_ecdsa_curves[i].name))
+			return &sunxi_ecdsa_curves[i];
+	}
+
+	return NULL;
+}
+
+static void sunxi_ecdsa_flush(void *buf, size_t len)
+{
+	ulong start = ALIGN_DOWN((ulong)buf, ARCH_DMA_MINALIGN);
+	ulong end = ALIGN((ulong)buf + len, ARCH_DMA_MINALIGN);
+
+	flush_dcache_range(start, end);
+}
+
+static void sunxi_ecdsa_invalidate(void *buf, size_t len)
+{
+	ulong start = ALIGN_DOWN((ulong)buf, ARCH_DMA_MINALIGN);
+	ulong end = ALIGN((ulong)buf + len, ARCH_DMA_MINALIGN);
+
+	invalidate_dcache_range(start, end);
+}
+
+static u32 sunxi_ecdsa_word_addr(void *buf)
+{
+	return (u32)(virt_to_phys(buf) >> SUNXI_CE_WORD_SHIFT);
+}
+
+static void sun50i_h616_ecdsa_spl_enable_clocks(void)
+{
+	void __iomem *ccu = (void __iomem *)SUNXI_CCM_BASE;
+
+	clrsetbits_le32(ccu + SUN50I_H616_CCU_CE_CLK,
+			SUN50I_H616_CCU_CE_CLK_SRC_PLL_PERIPH0_2X |
+			SUN50I_H616_CCU_CE_CLK_N_MASK |
+			SUN50I_H616_CCU_CE_CLK_M_MASK,
+			SUN50I_H616_CCU_CE_CLK_GATE |
+			SUN50I_H616_CCU_CE_CLK_SRC_PLL_PERIPH0_2X |
+			SUN50I_H616_CCU_CE_CLK_M_4);
+	setbits_le32(ccu + SUN50I_H616_CCU_CE_BGR,
+		     SUN50I_H616_CCU_CE_BUS_GATE |
+		     SUN50I_H616_CCU_CE_BUS_RST);
+	setbits_le32(ccu + SUN50I_H616_CCU_MBUS_GATE,
+		     SUN50I_H616_CCU_MBUS_GATE_CE);
+}
+
+static void sunxi_ecdsa_spl_enable_clocks(struct sunxi_ecdsa_priv *priv)
+{
+	if (priv->variant && priv->variant->spl_enable_clocks)
+		priv->variant->spl_enable_clocks();
+}
+
+static void sunxi_ecdsa_copy_le_param(u8 **dst,
+				      const struct sunxi_ecdsa_curve *curve,
+				      const void *src, size_t len)
+{
+	const u8 *p = src;
+	size_t copy_len;
+	int i;
+
+	copy_len = min_t(size_t, len, curve->bytes);
+	for (i = 0; i < copy_len; i++)
+		(*dst)[i] = p[copy_len - 1 - i];
+	*dst += curve->words * sizeof(u32);
+}
+
+static void sunxi_ecdsa_fill_src(struct sunxi_ecdsa_job *job,
+				 const struct sunxi_ecdsa_curve *curve,
+				 const struct ecdsa_public_key *pubkey,
+				 const void *hash, size_t hash_len,
+				 const void *signature)
+{
+	const u8 *r = signature;
+	const u8 *s = r + curve->bytes;
+	u8 *dst = job->src;
+
+	sunxi_ecdsa_copy_le_param(&dst, curve, curve->n, curve->bytes);
+	sunxi_ecdsa_copy_le_param(&dst, curve, s, curve->bytes);
+	/* ECDSA uses the leftmost bits when the digest is wider than the key. */
+	sunxi_ecdsa_copy_le_param(&dst, curve, hash, hash_len);
+	sunxi_ecdsa_copy_le_param(&dst, curve, r, curve->bytes);
+	sunxi_ecdsa_copy_le_param(&dst, curve, curve->p, curve->bytes);
+	sunxi_ecdsa_copy_le_param(&dst, curve, curve->a, curve->bytes);
+	sunxi_ecdsa_copy_le_param(&dst, curve, curve->gx, curve->bytes);
+	sunxi_ecdsa_copy_le_param(&dst, curve, curve->gy, curve->bytes);
+	sunxi_ecdsa_copy_le_param(&dst, curve, pubkey->x, curve->bytes);
+	sunxi_ecdsa_copy_le_param(&dst, curve, pubkey->y, curve->bytes);
+	sunxi_ecdsa_copy_le_param(&dst, curve, curve->n, curve->bytes);
+	sunxi_ecdsa_copy_le_param(&dst, curve, r, curve->bytes);
+}
+
+static void sunxi_ecdsa_fill_task(struct sunxi_ecdsa_job *job,
+				  const struct sunxi_ecdsa_curve *curve)
+{
+	struct sunxi_ce_task *task = &job->task;
+	u32 bytes = curve->words * sizeof(u32);
+
+	task->chan_id = SUNXI_CE_CHAN;
+	task->comm_ctl = SUNXI_CE_TASK_INT | SUNXI_CE_METHOD_ECC;
+	task->asym_ctl = curve->words |
+			 (SUNXI_CE_ECC_OP_VERIFY << SUNXI_CE_ECC_OP_SHIFT);
+	task->data_len = SUNXI_ECDSA_PARAMS * bytes;
+	task->src[0].addr = sunxi_ecdsa_word_addr(job->src);
+	task->src[0].len = task->data_len / sizeof(u32);
+	task->dst[0].addr = sunxi_ecdsa_word_addr(job->dst);
+	task->dst[0].len = curve->words;
+}
+
+static int sunxi_ecdsa_run(struct sunxi_ecdsa_priv *priv,
+			   struct sunxi_ecdsa_job *job)
+{
+	u32 val, err, result;
+	int ret;
+
+	sunxi_ecdsa_flush(&job->task, sizeof(job->task));
+	sunxi_ecdsa_flush(job->src, job->task.data_len);
+	sunxi_ecdsa_flush(job->dst, job->task.dst[0].len * sizeof(u32));
+
+	writel(SUNXI_CE_CHAN_INT, priv->base + SUNXI_CE_ISR);
+	writel(SUNXI_CE_CHAN_ERR_MASK, priv->base + SUNXI_CE_ERR);
+	writel(SUNXI_CE_CHAN_INT, priv->base + SUNXI_CE_ICR);
+
+	writel(sunxi_ecdsa_word_addr(&job->task), priv->base + SUNXI_CE_TDA);
+	writel(BIT(0) | (SUNXI_CE_METHOD_ECC << SUNXI_CE_TLR_METHOD_SHIFT),
+	       priv->base + SUNXI_CE_TLR);
+
+	ret = readl_poll_timeout(priv->base + SUNXI_CE_ISR, val,
+				 val & SUNXI_CE_CHAN_INT,
+				 SUNXI_CE_TIMEOUT_US);
+	if (ret)
+		return ret;
+
+	err = readl(priv->base + SUNXI_CE_ERR) & SUNXI_CE_CHAN_ERR_MASK;
+	writel(SUNXI_CE_CHAN_INT, priv->base + SUNXI_CE_ISR);
+	writel(SUNXI_CE_CHAN_ERR_MASK, priv->base + SUNXI_CE_ERR);
+
+	if (err) {
+		debug("%s: CE error %#x\n", __func__, err);
+		return -EIO;
+	}
+
+	sunxi_ecdsa_invalidate(job->dst, job->task.dst[0].len * sizeof(u32));
+	result = *(u32 *)job->dst;
+
+	return result == 1 ? 0 : -EPERM;
+}
+
+static int sunxi_ecdsa_verify_common(struct sunxi_ecdsa_priv *priv,
+				     const struct ecdsa_public_key *pubkey,
+				     const void *hash, size_t hash_len,
+				     const void *signature, size_t sig_len)
+{
+	const struct sunxi_ecdsa_curve *curve;
+	struct sunxi_ecdsa_job *job;
+	int ret;
+
+	curve = sunxi_ecdsa_find_curve(pubkey);
+	if (!curve || pubkey->size_bits != curve->bits || !hash_len ||
+	    sig_len != curve->bytes * 2)
+		return -EINVAL;
+
+	job = malloc_cache_aligned(sizeof(*job));
+	if (!job)
+		return -ENOMEM;
+
+	memset(job, 0, sizeof(*job));
+	sunxi_ecdsa_fill_src(job, curve, pubkey, hash, hash_len, signature);
+	sunxi_ecdsa_fill_task(job, curve);
+
+	if (IS_ENABLED(CONFIG_XPL_BUILD))
+		sunxi_ecdsa_spl_enable_clocks(priv);
+
+	ret = sunxi_ecdsa_run(priv, job);
+	free(job);
+
+	return ret;
+}
+
+static int sunxi_ecdsa_verify_dm(struct udevice *dev,
+				 const struct ecdsa_public_key *pubkey,
+				 const void *hash, size_t hash_len,
+				 const void *signature, size_t sig_len)
+{
+	struct sunxi_ecdsa_priv *priv = dev_get_priv(dev);
+
+	return sunxi_ecdsa_verify_common(priv, pubkey, hash, hash_len,
+					 signature, sig_len);
+}
+
+static int sunxi_ecdsa_probe(struct udevice *dev)
+{
+	struct sunxi_ecdsa_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	priv->variant = (const struct sunxi_ecdsa_variant *)
+			dev_get_driver_data(dev);
+	priv->base = dev_read_addr_ptr(dev);
+	if (!priv->base)
+		return -EINVAL;
+
+	if (IS_ENABLED(CONFIG_XPL_BUILD)) {
+		sunxi_ecdsa_spl_enable_clocks(priv);
+		return 0;
+	}
+
+	ret = reset_get_bulk(dev, &priv->resets);
+	if (ret) {
+		dev_err(dev, "failed to get resets: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_get_bulk(dev, &priv->clks);
+	if (ret) {
+		dev_err(dev, "failed to get clocks: %d\n", ret);
+		goto err_release_resets;
+	}
+
+	ret = reset_deassert_bulk(&priv->resets);
+	if (ret) {
+		dev_err(dev, "failed to deassert resets: %d\n", ret);
+		goto err_release_clks;
+	}
+
+	ret = clk_enable_bulk(&priv->clks);
+	if (ret) {
+		dev_err(dev, "failed to enable clocks: %d\n", ret);
+		goto err_assert_resets;
+	}
+
+	return 0;
+
+err_assert_resets:
+	reset_assert_bulk(&priv->resets);
+err_release_clks:
+	clk_release_bulk(&priv->clks);
+err_release_resets:
+	reset_release_bulk(&priv->resets);
+
+	return ret;
+}
+
+static int sunxi_ecdsa_remove(struct udevice *dev)
+{
+	struct sunxi_ecdsa_priv *priv = dev_get_priv(dev);
+
+	if (IS_ENABLED(CONFIG_XPL_BUILD))
+		return 0;
+
+	clk_disable_bulk(&priv->clks);
+	clk_release_bulk(&priv->clks);
+	reset_assert_bulk(&priv->resets);
+	reset_release_bulk(&priv->resets);
+
+	return 0;
+}
+
+static const struct ecdsa_ops sunxi_ecdsa_ops = {
+	.verify = sunxi_ecdsa_verify_dm,
+};
+
+static const struct sunxi_ecdsa_variant sun50i_h616_variant = {
+	.spl_enable_clocks = sun50i_h616_ecdsa_spl_enable_clocks,
+};
+
+static const struct udevice_id sunxi_ecdsa_ids[] = {
+	{
+		.compatible = "allwinner,sun50i-h616-crypto",
+		.data = (ulong)&sun50i_h616_variant,
+	},
+	{ }
+};
+
+U_BOOT_DRIVER(sunxi_ecdsa) = {
+	.name = "sunxi_ecdsa",
+	.id = UCLASS_ECDSA,
+	.of_match = sunxi_ecdsa_ids,
+	.ops = &sunxi_ecdsa_ops,
+	.probe = sunxi_ecdsa_probe,
+	.remove = sunxi_ecdsa_remove,
+	.priv_auto = sizeof(struct sunxi_ecdsa_priv),
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/include/u-boot/ecdsa.h b/include/u-boot/ecdsa.h
index f0ac0f327e9..e8b01cd0258 100644
--- a/include/u-boot/ecdsa.h
+++ b/include/u-boot/ecdsa.h
@@ -8,6 +8,7 @@
 
 #include <errno.h>
 #include <image.h>
+#include <string.h>
 
 /**
  * crypto_algo API impementation for ECDSA;
@@ -64,8 +65,23 @@ int ecdsa_verify(struct image_sign_info *info,
 		 uint8_t *sig, uint sig_len);
 /** @} */
 
+#define ECDSA224_BYTES	(224 / 8)
 #define ECDSA256_BYTES	(256 / 8)
 #define ECDSA384_BYTES	(384 / 8)
 #define ECDSA521_BYTES	((521 + 7) / 8)
 
+static inline unsigned int ecdsa_curve_size(const char *curve_name)
+{
+	if (!strcmp(curve_name, "secp224r1"))
+		return 224;
+	else if (!strcmp(curve_name, "prime256v1"))
+		return 256;
+	else if (!strcmp(curve_name, "secp384r1"))
+		return 384;
+	else if (!strcmp(curve_name, "secp521r1"))
+		return 521;
+
+	return 0;
+}
+
 #endif
diff --git a/include/u-boot/fdt-libcrypto.h b/include/u-boot/fdt-libcrypto.h
index b15d8a1eaf4..a7631cd0e04 100644
--- a/include/u-boot/fdt-libcrypto.h
+++ b/include/u-boot/fdt-libcrypto.h
@@ -12,7 +12,7 @@
 /**
  * fdt_add_bignum() - Write a libcrypto BIGNUM as an FDT property
  *
- * Convert a libcrypto BIGNUM * into a big endian array of integers.
+ * Convert a libcrypto BIGNUM * into a fixed-width big endian byte array.
  *
  * @blob:	FDT blob to modify
  * @noffset:	Offset of the FDT node
@@ -22,6 +22,6 @@
  * Return: 0 if all good all working, -ve on horror
  */
 int fdt_add_bignum(void *blob, int noffset, const char *prop_name,
-		   BIGNUM *num, int num_bits);
+		   const BIGNUM *num, int num_bits);
 
 #endif /* _FDT_LIBCRYPTO_H */
diff --git a/lib/ecdsa/Kconfig b/lib/ecdsa/Kconfig
index ca13b6bfa1f..f0c56278fb6 100644
--- a/lib/ecdsa/Kconfig
+++ b/lib/ecdsa/Kconfig
@@ -17,7 +17,7 @@ config ECDSA_VERIFY
 
 config SPL_ECDSA_VERIFY
 	bool "Enable ECDSA verification support in SPL"
-	depends on SPL
+	depends on SPL && SPL_DM
 	help
 	  Allow ECDSA signatures to be recognized and verified in SPL.
 
diff --git a/lib/ecdsa/ecdsa-libcrypto.c b/lib/ecdsa/ecdsa-libcrypto.c
index c4bfb2cec61..4877940c631 100644
--- a/lib/ecdsa/ecdsa-libcrypto.c
+++ b/lib/ecdsa/ecdsa-libcrypto.c
@@ -31,7 +31,7 @@ struct signer {
 	EVP_PKEY *evp_key;	/* Pointer to EVP_PKEY object */
 	EC_KEY *ecdsa_key;	/* Pointer to EC_KEY object */
 	void *hash;		/* Pointer to hash used for verification */
-	void *signature;	/* Pointer to output signature. Do not free()!*/
+	void *signature;	/* Pointer to raw signature buffer */
 };
 
 struct ecdsa_public_key {
@@ -50,11 +50,8 @@ static int fdt_get_key(struct ecdsa_public_key *key, const void *fdt, int node)
 	if (!key->curve_name)
 		return -ENOMSG;
 
-	if (!strcmp(key->curve_name, "prime256v1"))
-		key->size_bits = 256;
-	else if (!strcmp(key->curve_name, "secp384r1"))
-		key->size_bits = 384;
-	else
+	key->size_bits = ecdsa_curve_size(key->curve_name);
+	if (!key->size_bits)
 		return -EINVAL;
 
 	key->x = fdt_getprop(fdt, node, "ecdsa,x-point", &x_len);
@@ -63,7 +60,8 @@ 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)
+	if (x_len != (key->size_bits + 7) / 8 ||
+	    y_len != (key->size_bits + 7) / 8)
 		return -EINVAL;
 
 	return 0;
@@ -85,11 +83,8 @@ static int read_key_from_fdt(struct signer *ctx, const void *fdt, int node)
 		return ret;
 	}
 
-	if (!strcmp(pubkey.curve_name, "prime256v1")) {
-		nid = NID_X9_62_prime256v1;
-	} else if (!strcmp(pubkey.curve_name, "secp384r1")) {
-		nid = NID_secp384r1;
-	} else {
+	nid = OBJ_sn2nid(pubkey.curve_name);
+	if (nid == NID_undef) {
 		fprintf(stderr, "Unsupported curve name: '%s'\n", pubkey.curve_name);
 		return -EINVAL;
 	}
@@ -111,7 +106,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];
 
@@ -140,7 +135,7 @@ static int read_key_from_fdt(struct signer *ctx, const void *fdt, int node)
 	return 0;
 }
 
-static int alloc_ctx(struct signer *ctx, const struct image_sign_info *info)
+static int init_ctx(struct signer *ctx)
 {
 	memset(ctx, 0, sizeof(*ctx));
 
@@ -149,11 +144,21 @@ static int alloc_ctx(struct signer *ctx, const struct image_sign_info *info)
 		return -1;
 	}
 
+	return 0;
+}
+
+static int alloc_sig_ctx(struct signer *ctx, const struct image_sign_info *info)
+{
 	ctx->hash = malloc(info->checksum->checksum_len);
 	ctx->signature = malloc(info->crypto->key_len * 2);
 
-	if (!ctx->hash || !ctx->signature)
+	if (!ctx->hash || !ctx->signature) {
+		free(ctx->hash);
+		free(ctx->signature);
+		ctx->hash = NULL;
+		ctx->signature = NULL;
 		return -ENOMEM;
+	}
 
 	return 0;
 }
@@ -166,8 +171,8 @@ static void free_ctx(struct signer *ctx)
 	if (ctx->evp_key)
 		EVP_PKEY_free(ctx->evp_key);
 
-	if (ctx->hash)
-		free(ctx->hash);
+	free(ctx->hash);
+	free(ctx->signature);
 }
 
 /*
@@ -271,10 +276,6 @@ static int load_key_from_fdt(struct signer *ctx, const struct image_sign_info *i
 	if (!fdt)
 		return -EINVAL;
 
-	ret = alloc_ctx(ctx, info);
-	if (ret)
-		return ret;
-
 	sig_node = fdt_subnode_offset(fdt, 0, FIT_SIG_NODENAME);
 	if (sig_node < 0) {
 		fprintf(stderr, "No /signature node found\n");
@@ -331,7 +332,9 @@ static int prepare_ctx(struct signer *ctx, const struct image_sign_info *info)
 	int key_len_bytes, ret;
 	char kname[1024];
 
-	memset(ctx, 0, sizeof(*ctx));
+	ret = init_ctx(ctx);
+	if (ret)
+		return ret;
 
 	if (info->fdt_blob) {
 		return load_key_from_fdt(ctx, info);
@@ -345,10 +348,6 @@ static int prepare_ctx(struct signer *ctx, const struct image_sign_info *info)
 		return -EINVAL;
 	}
 
-	ret = alloc_ctx(ctx, info);
-	if (ret)
-		return ret;
-
 	ret = read_key(ctx, kname);
 	if (ret)
 		return ret;
@@ -371,8 +370,11 @@ static int do_sign(struct signer *ctx, struct image_sign_info *info,
 
 	algo->calculate(algo->name, region, region_count, ctx->hash);
 	sig = ECDSA_do_sign(ctx->hash, algo->checksum_len, ctx->ecdsa_key);
+	if (!sig)
+		return -EIO;
 
 	ecdsa_sig_encode_raw(ctx->signature, sig, info->crypto->key_len);
+	ECDSA_SIG_free(sig);
 
 	return 0;
 }
@@ -420,11 +422,16 @@ int ecdsa_sign(struct image_sign_info *info, const struct image_region region[],
 
 	ret = prepare_ctx(&ctx, info);
 	if (ret >= 0) {
-		do_sign(&ctx, info, region, region_count);
-		*sigp = ctx.signature;
-		*sig_len = info->crypto->key_len * 2;
-
-		ret = ecdsa_check_signature(&ctx, info);
+		ret = alloc_sig_ctx(&ctx, info);
+		if (!ret)
+			ret = do_sign(&ctx, info, region, region_count);
+		if (!ret)
+			ret = ecdsa_check_signature(&ctx, info);
+		if (!ret) {
+			*sigp = ctx.signature;
+			*sig_len = info->crypto->key_len * 2;
+			ctx.signature = NULL;
+		}
 	}
 
 	free_ctx(&ctx);
@@ -439,8 +446,12 @@ int ecdsa_verify(struct image_sign_info *info,
 	int ret;
 
 	ret = prepare_ctx(&ctx, info);
-	if (ret >= 0)
-		ret = do_verify(&ctx, info, region, region_count, sig, sig_len);
+	if (ret >= 0) {
+		ret = alloc_sig_ctx(&ctx, info);
+		if (!ret)
+			ret = do_verify(&ctx, info, region, region_count,
+					sig, sig_len);
+	}
 
 	free_ctx(&ctx);
 	return ret;
@@ -453,7 +464,7 @@ static int do_add(struct signer *ctx, void *fdt, const char *key_node_name,
 	const char *curve_name;
 	const EC_GROUP *group;
 	const EC_POINT *point;
-	BIGNUM *x, *y;
+	BIGNUM *x = NULL, *y = NULL;
 
 	signature_node = fdt_subnode_offset(fdt, 0, FIT_SIG_NODENAME);
 	if (signature_node == -FDT_ERR_NOTFOUND) {
@@ -491,42 +502,54 @@ static int do_add(struct signer *ctx, void *fdt, const char *key_node_name,
 	group = EC_KEY_get0_group(ctx->ecdsa_key);
 	key_bits = EC_GROUP_order_bits(group);
 	curve_name = OBJ_nid2sn(EC_GROUP_get_curve_name(group));
-	/* Let 'x' and 'y' memory leak by not BN_free()'ing them. */
 	x = BN_new();
 	y = BN_new();
+	if (!x || !y) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
 	point = EC_KEY_get0_public_key(ctx->ecdsa_key);
-	EC_POINT_get_affine_coordinates(group, point, x, y, NULL);
+	if (!EC_POINT_get_affine_coordinates(group, point, x, y, NULL)) {
+		ret = -EINVAL;
+		goto out;
+	}
 
 	ret = fdt_setprop_string(fdt, key_node, FIT_KEY_HINT,
 				 info->keyname);
 	if (ret < 0)
-		return ret;
+		goto out;
 
 	ret = fdt_setprop_string(fdt, key_node, "ecdsa,curve", curve_name);
 	if (ret < 0)
-		return ret;
+		goto out;
 
 	ret = fdt_add_bignum(fdt, key_node, "ecdsa,x-point", x, key_bits);
 	if (ret < 0)
-		return ret;
+		goto out;
 
 	ret = fdt_add_bignum(fdt, key_node, "ecdsa,y-point", y, key_bits);
 	if (ret < 0)
-		return ret;
+		goto out;
 
 	ret = fdt_setprop_string(fdt, key_node, FIT_ALGO_PROP,
 				 info->name);
 	if (ret < 0)
-		return ret;
+		goto out;
 
 	if (info->require_keys) {
 		ret = fdt_setprop_string(fdt, key_node, FIT_KEY_REQUIRED,
 					 info->require_keys);
 		if (ret < 0)
-			return ret;
+			goto out;
 	}
 
-	return key_node;
+	ret = key_node;
+
+out:
+	BN_free(x);
+	BN_free(y);
+	return ret;
 }
 
 int ecdsa_add_verify_data(struct image_sign_info *info, void *fdt)
diff --git a/lib/ecdsa/ecdsa-verify.c b/lib/ecdsa/ecdsa-verify.c
index 629b662cf6c..201111bc128 100644
--- a/lib/ecdsa/ecdsa-verify.c
+++ b/lib/ecdsa/ecdsa-verify.c
@@ -12,22 +12,6 @@
 #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 if (!strcmp(curve_name, "secp384r1"))
-		return 384;
-
-	return 0;
-}
-
 static int fdt_get_key(struct ecdsa_public_key *key, const void *fdt, int node)
 {
 	int x_len, y_len;
@@ -38,7 +22,7 @@ static int fdt_get_key(struct ecdsa_public_key *key, const void *fdt, int node)
 		return -ENOMSG;
 	}
 
-	key->size_bits = ecdsa_key_size(key->curve_name);
+	key->size_bits = ecdsa_curve_size(key->curve_name);
 	if (key->size_bits == 0) {
 		debug("Unknown ECDSA curve '%s'", key->curve_name);
 		return -EINVAL;
@@ -50,7 +34,8 @@ 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)) {
+	if (x_len != (key->size_bits + 7) / 8 ||
+	    y_len != (key->size_bits + 7) / 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;
@@ -123,6 +108,12 @@ int ecdsa_verify(struct image_sign_info *info,
 	return ecdsa_verify_hash(dev, info, hash, sig, sig_len);
 }
 
+U_BOOT_CRYPTO_ALGO(ecdsa224) = {
+	.name = "ecdsa224",
+	.key_len = ECDSA224_BYTES,
+	.verify = ecdsa_verify,
+};
+
 U_BOOT_CRYPTO_ALGO(ecdsa256) = {
 	.name = "ecdsa256",
 	.key_len = ECDSA256_BYTES,
@@ -135,6 +126,12 @@ U_BOOT_CRYPTO_ALGO(ecdsa384) = {
 	.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..8afe31cbe9d 100644
--- a/lib/fdt-libcrypto.c
+++ b/lib/fdt-libcrypto.c
@@ -5,68 +5,35 @@
  */
 
 #include <libfdt.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <u-boot/fdt-libcrypto.h>
 
 int fdt_add_bignum(void *blob, int noffset, const char *prop_name,
-		   BIGNUM *num, int num_bits)
+		   const BIGNUM *num, int num_bits)
 {
-	int nwords = num_bits / 32;
-	int size;
-	uint32_t *buf, *ptr;
-	BIGNUM *tmp, *big2, *big32, *big2_32;
-	BN_CTX *ctx;
+	int size = (num_bits + 7) / 8;
+	unsigned char *buf;
 	int ret;
 
-	tmp = BN_new();
-	big2 = BN_new();
-	big32 = BN_new();
-	big2_32 = BN_new();
+	if (size <= 0)
+		return -EINVAL;
 
-	/*
-	 * Note: This code assumes that all of the above succeed, or all fail.
-	 * In practice memory allocations generally do not fail (unless the
-	 * process is killed), so it does not seem worth handling each of these
-	 * as a separate case. Technicaly this could leak memory on failure,
-	 * but a) it won't happen in practice, and b) it doesn't matter as we
-	 * will immediately exit with a failure code.
-	 */
-	if (!tmp || !big2 || !big32 || !big2_32) {
-		fprintf(stderr, "Out of memory (bignum)\n");
-		return -ENOMEM;
-	}
-	ctx = BN_CTX_new();
-	if (!ctx) {
-		fprintf(stderr, "Out of memory (bignum context)\n");
-		return -ENOMEM;
-	}
-	BN_set_word(big2, 2L);
-	BN_set_word(big32, 32L);
-	BN_exp(big2_32, big2, big32, ctx); /* B = 2^32 */
-
-	size = nwords * sizeof(uint32_t);
 	buf = malloc(size);
 	if (!buf) {
 		fprintf(stderr, "Out of memory (%d bytes)\n", size);
 		return -ENOMEM;
 	}
 
-	/* Write out modulus as big endian array of integers */
-	for (ptr = buf + nwords - 1; ptr >= buf; ptr--) {
-		BN_mod(tmp, num, big2_32, ctx); /* n = N mod B */
-		*ptr = cpu_to_fdt32(BN_get_word(tmp));
-		BN_rshift(num, num, 32); /*  N = N/B */
+	if (BN_bn2binpad(num, buf, size) != size) {
+		free(buf);
+		return -EINVAL;
 	}
 
-	/*
-	 * We try signing with successively increasing size values, so this
-	 * might fail several times
-	 */
+	/* Callers may retry with a larger FDT if the property does not fit. */
 	ret = fdt_setprop(blob, noffset, prop_name, buf, size);
 	free(buf);
-	BN_free(tmp);
-	BN_free(big2);
-	BN_free(big32);
-	BN_free(big2_32);
 
 	return ret ? -FDT_ERR_NOSPACE : 0;
 }
diff --git a/tools/image-sig-host.c b/tools/image-sig-host.c
index 5285263c616..aeca83bd440 100644
--- a/tools/image-sig-host.c
+++ b/tools/image-sig-host.c
@@ -69,6 +69,13 @@ struct crypto_algo crypto_algos[] = {
 		.add_verify_data = rsa_add_verify_data,
 		.verify = rsa_verify,
 	},
+	{
+		.name = "ecdsa224",
+		.key_len = ECDSA224_BYTES,
+		.sign = ecdsa_sign,
+		.add_verify_data = ecdsa_add_verify_data,
+		.verify = ecdsa_verify,
+	},
 	{
 		.name = "ecdsa256",
 		.key_len = ECDSA256_BYTES,
-- 
2.53.0



More information about the U-Boot mailing list