[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