From 0cd56caceef65474f1cba389cb5c8b2e74e626dd Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 23 Oct 2026 16:48:20 -0700 Subject: [PATCH] Add ECDSA/SHA-384 image-verify timing harness and SECP384R1 to the C2000 example --- .../ti-c2000-f28p55x/Header/ecc_p384_kat.h | 42 ++ .../ti-c2000-f28p55x/Header/user_settings.h | 14 +- embedded/ti-c2000-f28p55x/Makefile | 24 ++ embedded/ti-c2000-f28p55x/README.md | 20 + embedded/ti-c2000-f28p55x/Source/wolf_main.c | 379 ++++++++++++++++++ 5 files changed, 478 insertions(+), 1 deletion(-) create mode 100644 embedded/ti-c2000-f28p55x/Header/ecc_p384_kat.h diff --git a/embedded/ti-c2000-f28p55x/Header/ecc_p384_kat.h b/embedded/ti-c2000-f28p55x/Header/ecc_p384_kat.h new file mode 100644 index 000000000..b1b59424b --- /dev/null +++ b/embedded/ti-c2000-f28p55x/Header/ecc_p384_kat.h @@ -0,0 +1,42 @@ +/* ecc_p384_kat.h - deterministic ECDSA P-384 (SECP384R1) known-answer vector. + * + * Source: RFC 6979 Appendix A.2.6 (P-384), message "sample", hash SHA-384. + * The same vector wolfCrypt's own ecc384_test_deterministic_k() uses. The + * digest is not stored: the device hashes "sample" with SHA-384 itself, so the + * KAT covers the hash-to-verify chain rather than just the curve math. + * + * Brace-init byte arrays (NOT "\x.." string literals): on cl2000 a char[]/byte[] + * initialized from a "\x.." string sign-extends bytes >= 0x80 into the 16-bit + * cell; a brace-init list stores clean octets. + */ +#ifndef ECC_P384_KAT_H +#define ECC_P384_KAT_H + +/* Public key Q = (Ux, Uy), 48 bytes each, big-endian. */ +static const unsigned char kat_p384_qx[48] = { + 0xEC,0x3A,0x4E,0x41,0x5B,0x4E,0x19,0xA4,0x56,0x86,0x18,0x02,0x9F,0x42,0x7F,0xA5, + 0xDA,0x9A,0x8B,0xC4,0xAE,0x92,0xE0,0x2E,0x06,0xAA,0xE5,0x28,0x6B,0x30,0x0C,0x64, + 0xDE,0xF8,0xF0,0xEA,0x90,0x55,0x86,0x60,0x64,0xA2,0x54,0x51,0x54,0x80,0xBC,0x13 +}; +static const unsigned char kat_p384_qy[48] = { + 0x80,0x15,0xD9,0xB7,0x2D,0x7D,0x57,0x24,0x4E,0xA8,0xEF,0x9A,0xC0,0xC6,0x21,0x89, + 0x67,0x08,0xA5,0x93,0x67,0xF9,0xDF,0xB9,0xF5,0x4C,0xA8,0x4B,0x3F,0x1C,0x9D,0xB1, + 0x28,0x8B,0x23,0x1C,0x3A,0xE0,0xD4,0xFE,0x73,0x44,0xFD,0x25,0x33,0x26,0x47,0x20 +}; + +/* Signature (r, s), 48 bytes each, big-endian. */ +static const unsigned char kat_p384_r[48] = { + 0x94,0xED,0xBB,0x92,0xA5,0xEC,0xB8,0xAA,0xD4,0x73,0x6E,0x56,0xC6,0x91,0x91,0x6B, + 0x3F,0x88,0x14,0x06,0x66,0xCE,0x9F,0xA7,0x3D,0x64,0xC4,0xEA,0x95,0xAD,0x13,0x3C, + 0x81,0xA6,0x48,0x15,0x2E,0x44,0xAC,0xF9,0x6E,0x36,0xDD,0x1E,0x80,0xFA,0xBE,0x46 +}; +static const unsigned char kat_p384_s[48] = { + 0x99,0xEF,0x4A,0xEB,0x15,0xF1,0x78,0xCE,0xA1,0xFE,0x40,0xDB,0x26,0x03,0x13,0x8F, + 0x13,0x0E,0x74,0x0A,0x19,0x62,0x45,0x26,0x20,0x3B,0x63,0x51,0xD0,0xA3,0xA9,0x4F, + 0xA3,0x29,0xC1,0x45,0x78,0x6E,0x67,0x9E,0x7B,0x82,0xC7,0x1A,0x38,0x62,0x8A,0xC8 +}; + +/* The message that was signed; the device computes SHA-384 over it. */ +static const char kat_p384_msg[] = "sample"; + +#endif /* ECC_P384_KAT_H */ diff --git a/embedded/ti-c2000-f28p55x/Header/user_settings.h b/embedded/ti-c2000-f28p55x/Header/user_settings.h index d762ab622..c1d7ed2ba 100644 --- a/embedded/ti-c2000-f28p55x/Header/user_settings.h +++ b/embedded/ti-c2000-f28p55x/Header/user_settings.h @@ -235,10 +235,18 @@ extern "C" { /* ------------------------------------------------------------------------- */ #undef HAVE_ECC #define HAVE_ECC -#undef ECC_USER_CURVES /* build only the curves we name (just P-256) */ +#undef ECC_USER_CURVES /* build only the curves named below: P-256, */ + /* plus P-384 when WOLF_ECC384 is set */ #define ECC_USER_CURVES #undef HAVE_ECC256 #define HAVE_ECC256 +#ifdef WOLF_ECC384 +/* SECP384R1 alongside P-256 (ECC384=1 build); pairs with SHA-384. */ +#undef HAVE_ECC384 +#define HAVE_ECC384 +#undef WOLFSSL_SP_384 +#define WOLFSSL_SP_384 +#endif #undef HAVE_ECC_VERIFY #define HAVE_ECC_VERIFY #undef HAVE_ECC_SIGN @@ -254,8 +262,12 @@ extern "C" { #define WOLFSSL_HAVE_SP_ECC #undef WOLFSSL_SP_NO_MALLOC #define WOLFSSL_SP_NO_MALLOC +#ifndef WOLF_ECC_FAST +/* Small (looped) SP code by default; ECCFAST=1 picks the windowed SP path, + * which trades flash for roughly ten times the ECDSA throughput. */ #undef WOLFSSL_SP_SMALL #define WOLFSSL_SP_SMALL +#endif #undef SP_WORD_SIZE #define SP_WORD_SIZE 32 /* C28x int is 16-bit (WC_16BIT_CPU) but we use 32-bit SP digits + octet-wise diff --git a/embedded/ti-c2000-f28p55x/Makefile b/embedded/ti-c2000-f28p55x/Makefile index 824aab7a5..d530fc918 100644 --- a/embedded/ti-c2000-f28p55x/Makefile +++ b/embedded/ti-c2000-f28p55x/Makefile @@ -91,6 +91,17 @@ WC_SRCS := \ $(WOLFROOT)/wolfcrypt/src/wc_mldsa.c \ $(WOLFROOT)/wolfcrypt/src/random.c +# ECC384=1 adds SECP384R1, ECCFAST=1 drops WOLFSSL_SP_SMALL, and ECCBENCH=1 +# adds the ECDSA / SHA-384 image-verify timing harness. All three imply ECC. +ECC384 ?= 0 +ECCFAST ?= 0 +ECCBENCH ?= 0 +ifneq ($(ECC384)$(ECCFAST)$(ECCBENCH),000) + # override: a command-line ECC=0 alongside one of these would otherwise leave + # WOLF_ECC384 defined with the ECC sources and HAVE_ECC block compiled out. + override ECC := 1 +endif + # ECC=1 adds ECDSA/ECDH over SECP256R1 via SP single-precision math. ECC ?= 0 ifeq ($(ECC),1) @@ -102,6 +113,19 @@ ifeq ($(ECC),1) $(WOLFROOT)/wolfcrypt/src/sp_c32.c endif +ifeq ($(ECC384),1) + CFLAGS += --define=WOLF_ECC384 +endif +ifeq ($(ECCFAST),1) + CFLAGS += --define=WOLF_ECC_FAST +endif +# The timing harness runs on its own: the wolfcrypt test and benchmark are left +# out so the run is short and the heap uncontended. +ifeq ($(ECCBENCH),1) + CFLAGS += --define=WOLF_ECC_BENCH --define=NO_CRYPT_TEST \ + --define=NO_CRYPT_BENCHMARK +endif + # MLKEM=1 adds ML-KEM-768 (FIPS 203) via the wolfCrypt C implementation. MLKEM ?= 0 ifeq ($(MLKEM),1) diff --git a/embedded/ti-c2000-f28p55x/README.md b/embedded/ti-c2000-f28p55x/README.md index 7c0c552bd..de984e4eb 100644 --- a/embedded/ti-c2000-f28p55x/README.md +++ b/embedded/ti-c2000-f28p55x/README.md @@ -90,6 +90,9 @@ Each is `make =1` (default 0 unless noted), additive on top of the default | Option | Adds | |---|---| | `ECC=1` | ECDSA/ECDH over SECP256R1 (SP math) | +| `ECC384=1` | Adds SECP384R1 (`HAVE_ECC384` + `WOLFSSL_SP_384`) | +| `ECCFAST=1` | Drops `WOLFSSL_SP_SMALL` for the windowed SP path (~10x faster ECDSA, +30 KB flash for P-256). Needs a wolfSSL with the SP scalar-recode fix for 16-bit bytes; older trees fail to build with `identifier "int8_t" is undefined`. | +| `ECCBENCH=1` | ECDSA sign/verify, ECDH and SHA-256/384 image-hash timings instead of the test/benchmark harness | | `MLKEM=1` | ML-KEM-768 (FIPS 203) | | `AES=1` | AES-CBC/CTR/CFB/GCM (table-driven, `GCM_SMALL`) | | `AESEXTRA=1` | AES-CMAC, AES-CCM, AES-GMAC (implies the AES core) | @@ -136,6 +139,23 @@ Measured at 150 MHz (SHA-256 Hash-DRBG via `WOLFSSL_GENSEED_FORTEST`; build with | SHAKE128 / SHAKE256 | ~319 / 264 KiB/s | | RNG (Hash-DRBG) | ~122 KiB/s | +ECDSA and ECDH (@150 MHz, `ECC_TIMING_RESISTANT` on). The default `WOLFSSL_SP_SMALL` build keeps SP code small at a large cost in speed; `ECCFAST=1` selects the windowed SP path: + +| Operation | Default SP | Windowed SP (`ECCFAST=1`) | +|---|---|---| +| ECDSA P-256 sign | ~350 ms/op | 46 ms/op | +| ECDSA P-256 verify | ~635 ms/op | 64 ms/op | +| ECDH P-256 agree | ~308 ms/op | 57 ms/op | +| ECDSA P-384 sign | ~1280 ms/op | 98 ms/op | +| ECDSA P-384 verify | ~2260 ms/op | 183 ms/op | +| ECDH P-384 agree | ~1108 ms/op | 163 ms/op | + +The default-SP column varies by a few percent run to run, since each run makes a fresh key and ECDSA verify is a public operation with no timing-resistance constraint. + +The image hash walks a 4 KiB flash-resident block repeatedly to reach 128 KiB. That is equivalent to one sweep over a 128 KiB array on this part, which has no data cache in front of flash: measured on hardware, a true single pass took 0.740 s against 0.736 s for the repeated block. The small block is the default because a genuine 128 KiB octet array costs 256 KB of flash, one octet per 16-bit cell, and fills a whole flash bank. Build with `-DWOLF_IMG_BLK=131072` in `EXTRA_CFLAGS` for the single sweep. + +For a secure-boot style check of a 128 KB flash image, the hash dominates once the windowed path is in use: SHA-384 over the image takes 0.75 s, giving 0.81 s for SHA-384 plus a P-256 verify and 0.93 s with P-384 (1.39 s and 2.96 s respectively on the default SP path). SHA-256 over the same image takes 0.46 s. Building with `EXTRA_CFLAGS="--opt_level=3 --opt_for_speed=5"` takes SHA-384 down to 0.64 s for about 7 KB more flash, but does not move the ECC math. The windowed path itself costs about 30 KB of flash for P-256 alone, 82 KB for both curves. + ML-DSA-87 (asymmetric, @150 MHz): verify 244 ms/op default, **189 ms/op** with `FASTVERIFY=1` and `--opt_level=3 --opt_for_speed=5` (23% faster, RAM 10.7 -> 23.9 KB, zero heap either way); keygen and signing also run (`SIGN=1`). ML-DSA-87 verify memory: ~10.7 KB total (key/workspace struct + ~2 KB stack, zero heap) with `WOLFSSL_MLDSA_VERIFY_SMALLEST_MEM` + `WOLFSSL_MLDSA_ASSIGN_KEY`. ## Memory notes diff --git a/embedded/ti-c2000-f28p55x/Source/wolf_main.c b/embedded/ti-c2000-f28p55x/Source/wolf_main.c index e0f716725..deed2ee30 100644 --- a/embedded/ti-c2000-f28p55x/Source/wolf_main.c +++ b/embedded/ti-c2000-f28p55x/Source/wolf_main.c @@ -118,6 +118,9 @@ #endif #ifdef WOLF_ECC #include "ecc_p256_kat.h" +#ifdef WOLF_ECC384 +#include "ecc_p384_kat.h" +#endif #endif #ifdef WOLF_RSA #include "rsa2048_kat.h" @@ -664,6 +667,372 @@ static void wolf_ecc_test(void) wc_ecc_free(&keyA); wc_FreeRng(&rng); } + +#ifdef WOLF_ECC_BENCH +/* Timings for a secure-boot style image check: hash a flash-resident image, + * then one ECDSA verify. CPU Timer 0 is sampled every iteration so no single + * interval can outrun the counter's ~28 s wrap. A benchmark that reports a + * rate for work that failed is worse than one that reports nothing, so every + * loop below stops at the first error and divides by the number of operations + * that actually completed. */ +#ifndef WOLF_IMG_TOTAL +#define WOLF_IMG_TOTAL 131072UL /* octets hashed in total */ +#endif +#ifndef WOLF_IMG_BLK +#define WOLF_IMG_BLK 4096U /* size of the flash-resident block */ +#endif +#ifndef WOLF_IMG_CHUNK +#define WOLF_IMG_CHUNK 4096U /* octets handed to each Update */ +#endif + +/* const lands in .const, i.e. flash, so the hash reads at flash wait states + * the way a real image check does rather than out of RAM. One octet per + * 16-bit cell, matching how wolfCrypt addresses bytes on this target. + * + * By default the block is smaller than WOLF_IMG_TOTAL and is walked repeatedly + * to reach the total, so this measures hashing 128 KiB of flash-resident data + * rather than a single sweep over a 128 KiB address range. The two are + * equivalent here: the C28x has no data cache in front of flash, and measuring + * both on hardware gave 0.740 s for a true single pass over a 128 KiB array + * against 0.736 s for a repeated 4 KiB block - within run-to-run spread. The + * small block is the default because a genuine 128 KiB octet array costs 256 KB + * of flash (one octet per 16-bit cell) and fills an entire flash bank. Build + * with -DWOLF_IMG_BLK=131072 to do the single sweep instead. */ +static const unsigned char wolf_img[WOLF_IMG_BLK] = { 0 }; + +/* Hash WOLF_IMG_TOTAL octets of flash-resident data. */ +static void wolf_sha_image_bench(int use384) +{ + static wc_Sha256 sha256; + static wc_Sha384 sha384; + unsigned char dig[WC_SHA384_DIGEST_SIZE]; + const char* nm = use384 ? "SHA-384" : "SHA-256"; + unsigned long off; + double t; + int ret; + + ret = use384 ? wc_InitSha384(&sha384) : wc_InitSha256(&sha256); + if (ret != 0) { + printf("%s image bench: init FAIL ret=%d\r\n", nm, ret); + return; + } + + (void)current_time(1); + for (off = 0; off < WOLF_IMG_TOTAL; off += WOLF_IMG_CHUNK) { + unsigned long left = WOLF_IMG_TOTAL - off; + word32 n = (word32)((left < WOLF_IMG_CHUNK) ? left : WOLF_IMG_CHUNK); + /* walk within the block, wrapping when it is smaller than the total */ + unsigned long pos = off % (unsigned long)WOLF_IMG_BLK; + + if (pos + n > (unsigned long)WOLF_IMG_BLK) { + n = (word32)((unsigned long)WOLF_IMG_BLK - pos); + } + ret = use384 ? wc_Sha384Update(&sha384, wolf_img + pos, n) + : wc_Sha256Update(&sha256, wolf_img + pos, n); + (void)current_time(0); + if (ret != 0) { + break; + } + } + /* Only finalize a context whose updates all succeeded. */ + if (ret == 0) { + ret = use384 ? wc_Sha384Final(&sha384, dig) + : wc_Sha256Final(&sha256, dig); + } + t = current_time(0); + + if (use384) { + wc_Sha384Free(&sha384); + } + else { + wc_Sha256Free(&sha256); + } + + if (ret != 0) { + printf("%s over %lu KiB image: FAIL ret=%d (no timing)\r\n", + nm, WOLF_IMG_TOTAL / 1024UL, ret); + return; + } + printf("%s over %lu KiB image: %.3f s\r\n", nm, WOLF_IMG_TOTAL / 1024UL, t); +} + +static void wolf_ecdsa_bench(int curveId, int keySz, int reps, const char* nm) +{ + static ecc_key bk; + static WC_RNG brng; + static mp_int br; + static mp_int bs; + unsigned char dig[WC_SHA384_DIGEST_SIZE]; + double t; + int i, ret, res = 0, done; + int rngOk = 0, keyOk = 0, rOk = 0, sOk = 0; + + /* A SHA-384 digest; ECDSA truncates it to the curve order for P-256. */ + XMEMSET(dig, 0xA5, sizeof(dig)); + + ret = wc_InitRng(&brng); + if (ret == 0) { + rngOk = 1; + ret = wc_ecc_init(&bk); + } + if (ret == 0) { + keyOk = 1; + ret = wc_ecc_make_key_ex(&brng, keySz, &bk, curveId); + } + if (ret == 0) { + ret = wc_ecc_set_rng(&bk, &brng); + } + if (ret == 0) { + ret = mp_init(&br); + } + if (ret == 0) { + rOk = 1; + ret = mp_init(&bs); + } + if (ret == 0) { + sOk = 1; + } + if (ret != 0) { + printf("ECDSA %s bench: setup FAIL ret=%d\r\n", nm, ret); + goto bench_done; + } + + done = 0; + (void)current_time(1); + for (i = 0; i < reps; i++) { + ret = wc_ecc_sign_hash_ex(dig, (word32)keySz, &brng, &bk, &br, &bs); + (void)current_time(0); + if (ret != 0) { + break; + } + done++; + } + t = current_time(0); + if (done == 0) { + printf("ECDSA %s sign: FAIL ret=%d (no timing)\r\n", nm, ret); + goto bench_done; + } + printf("ECDSA %s sign: %d ops in %.3f s = %.1f ms/op%s\r\n", + nm, done, t, (t * 1000.0) / (double)done, + (done < reps) ? " [STOPPED EARLY]" : ""); + + done = 0; + (void)current_time(1); + for (i = 0; i < reps; i++) { + ret = wc_ecc_verify_hash_ex(&br, &bs, dig, (word32)keySz, &res, &bk); + (void)current_time(0); + if ((ret != 0) || (res != 1)) { + break; + } + done++; + } + t = current_time(0); + if (done == 0) { + printf("ECDSA %s verify: FAIL ret=%d res=%d (no timing)\r\n", + nm, ret, res); + goto bench_done; + } + printf("ECDSA %s verify: %d ops in %.3f s = %.1f ms/op%s\r\n", + nm, done, t, (t * 1000.0) / (double)done, + (done < reps) ? " [STOPPED EARLY]" : ""); + +bench_done: + if (sOk) { + mp_clear(&bs); + } + if (rOk) { + mp_clear(&br); + } + if (keyOk) { + wc_ecc_free(&bk); + } + if (rngOk) { + wc_FreeRng(&brng); + } +} + +static void wolf_ecdh_bench(int curveId, int keySz, int reps, const char* nm) +{ + static ecc_key ka; + static ecc_key kb; + static WC_RNG brng; + static byte secret[48]; + word32 len = 0; + double t; + int i, ret, done; + int rngOk = 0, kaOk = 0, kbOk = 0; + + ret = wc_InitRng(&brng); + if (ret == 0) { + rngOk = 1; + ret = wc_ecc_init(&ka); + } + if (ret == 0) { + kaOk = 1; + ret = wc_ecc_init(&kb); + } + if (ret == 0) { + kbOk = 1; + ret = wc_ecc_make_key_ex(&brng, keySz, &ka, curveId); + } + if (ret == 0) { + ret = wc_ecc_make_key_ex(&brng, keySz, &kb, curveId); + } + if (ret == 0) { + ret = wc_ecc_set_rng(&ka, &brng); + } + if (ret != 0) { + printf("ECDH %s bench: setup FAIL ret=%d\r\n", nm, ret); + goto ecdh_done; + } + + done = 0; + (void)current_time(1); + for (i = 0; i < reps; i++) { + len = (word32)sizeof(secret); + ret = wc_ecc_shared_secret(&ka, &kb, secret, &len); + (void)current_time(0); + if (ret != 0) { + break; + } + done++; + } + t = current_time(0); + if (done == 0) { + printf("ECDH %s agree: FAIL ret=%d (no timing)\r\n", nm, ret); + goto ecdh_done; + } + printf("ECDH %s agree: %d ops in %.3f s = %.1f ms/op (len=%lu)%s\r\n", + nm, done, t, (t * 1000.0) / (double)done, (unsigned long)len, + (done < reps) ? " [STOPPED EARLY]" : ""); + +ecdh_done: + if (kbOk) { + wc_ecc_free(&kb); + } + if (kaOk) { + wc_ecc_free(&ka); + } + if (rngOk) { + wc_FreeRng(&brng); + } +} + +static void wolf_ecc_bench(void) +{ + wolf_sha_image_bench(0); + wolf_sha_image_bench(1); + wolf_ecdsa_bench(ECC_SECP256R1, 32, 10, "P-256"); + wolf_ecdh_bench(ECC_SECP256R1, 32, 10, "P-256"); +#ifdef WOLF_ECC384 + wolf_ecdsa_bench(ECC_SECP384R1, 48, 5, "P-384"); + wolf_ecdh_bench(ECC_SECP384R1, 48, 5, "P-384"); +#endif +} +#endif /* WOLF_ECC_BENCH */ + + +#ifdef WOLF_ECC384 +/* P-384 correctness, independent of the timing harness: an RFC 6979 verify KAT + * over a device-computed SHA-384 digest, then a two-sided ECDH agreement. The + * KAT uses externally supplied public-key and signature octets, so it catches + * the serialization defects a self-generated sign/verify pair cannot. */ +static void wolf_ecc384_test(void) +{ + /* Large under SP_NO_MALLOC - keep off the 16 KW stack. */ + static ecc_key k384; + static ecc_key k384a; + static ecc_key k384b; + static WC_RNG rng384; + static mp_int r384; + static mp_int s384; + static wc_Sha384 sha384; + static byte secA[48]; + static byte secB[48]; + unsigned char dig[WC_SHA384_DIGEST_SIZE]; + word32 lenA, lenB; + int ret, res = 0; + + ret = wc_InitSha384(&sha384); + if (ret == 0) { + ret = wc_Sha384Update(&sha384, (const byte*)kat_p384_msg, + (word32)(sizeof(kat_p384_msg) - 1)); + } + if (ret == 0) { + ret = wc_Sha384Final(&sha384, dig); + } + wc_Sha384Free(&sha384); + + if (ret == 0) { + ret = wc_ecc_init(&k384); + } + if (ret == 0) { + ret = wc_ecc_import_unsigned(&k384, (byte*)kat_p384_qx, + (byte*)kat_p384_qy, NULL, ECC_SECP384R1); + } + if (ret == 0) { + ret = mp_init(&r384); + } + if (ret == 0) { + ret = mp_init(&s384); + } + if (ret == 0) { + ret = mp_read_unsigned_bin(&r384, kat_p384_r, + (word32)sizeof(kat_p384_r)); + } + if (ret == 0) { + ret = mp_read_unsigned_bin(&s384, kat_p384_s, + (word32)sizeof(kat_p384_s)); + } + if (ret == 0) { + ret = wc_ecc_verify_hash_ex(&r384, &s384, dig, + (word32)sizeof(dig), &res, &k384); + } + printf("ECDSA P-384 verify KAT: %s (ret=%d res=%d)\r\n", + (ret == 0 && res == 1) ? "PASS" : "FAIL", ret, res); + mp_clear(&s384); + mp_clear(&r384); + wc_ecc_free(&k384); + + /* Two-sided ECDH: both parties must derive the same secret. */ + ret = wc_InitRng(&rng384); + if (ret == 0) { + ret = wc_ecc_init(&k384a); + } + if (ret == 0) { + ret = wc_ecc_init(&k384b); + } + if (ret == 0) { + ret = wc_ecc_make_key_ex(&rng384, 48, &k384a, ECC_SECP384R1); + } + if (ret == 0) { + ret = wc_ecc_make_key_ex(&rng384, 48, &k384b, ECC_SECP384R1); + } + if (ret == 0) { + ret = wc_ecc_set_rng(&k384a, &rng384); + } + if (ret == 0) { + ret = wc_ecc_set_rng(&k384b, &rng384); + } + lenA = (word32)sizeof(secA); + lenB = (word32)sizeof(secB); + if (ret == 0) { + ret = wc_ecc_shared_secret(&k384a, &k384b, secA, &lenA); + } + if (ret == 0) { + ret = wc_ecc_shared_secret(&k384b, &k384a, secB, &lenB); + } + printf("ECDH P-384 shared secret: %s (ret=%d len=%lu)\r\n", + (ret == 0 && lenA == lenB && lenA == 48U && + XMEMCMP(secA, secB, lenA) == 0) ? "PASS" : "FAIL", + ret, (unsigned long)lenA); + + wc_ecc_free(&k384b); + wc_ecc_free(&k384a); + wc_FreeRng(&rng384); +} +#endif /* WOLF_ECC384 */ + #endif /* WOLF_ECC */ /* Focused SHA3-256 known-answer test (SHA3-256("abc")), to confirm the @@ -2510,6 +2879,16 @@ int main(void) wolf_ecc_test(); #endif +#ifdef WOLF_ECC384 + printf("\r\n--- ECDSA/ECDH P-384 (SP) ---\r\n"); + wolf_ecc384_test(); +#endif + +#ifdef WOLF_ECC_BENCH + printf("\r\n--- ECDSA / SHA-384 image-verify timings ---\r\n"); + wolf_ecc_bench(); +#endif + #ifdef WOLF_MLKEM printf("\r\n--- ML-KEM-768 ---\r\n"); wolf_mlkem768_test();