From 858bc3b478581d7be664ec5673157da0ce50b08c Mon Sep 17 00:00:00 2001 From: Paul Adelsbach Date: Fri, 21 Aug 2026 12:20:22 -0700 Subject: [PATCH] Add crypto callback for Frodo-kem --- .github/workflows/cryptocb-only.yml | 3 + tests/api/test_frodokem.c | 15 +- wolfcrypt/src/cryptocb.c | 13 + .../src/port/arm/armv8-32-frodokem-asm.S | 12 +- .../src/port/arm/armv8-32-frodokem-asm_c.c | 8 +- wolfcrypt/src/port/arm/armv8-frodokem-asm.S | 16 +- wolfcrypt/src/port/arm/armv8-frodokem-asm.asm | 4 +- wolfcrypt/src/port/arm/armv8-frodokem-asm_c.c | 4 +- wolfcrypt/src/port/arm/thumb2-frodokem-asm.S | 4 +- .../src/port/arm/thumb2-frodokem-asm_c.c | 4 +- wolfcrypt/src/wc_frodokem.c | 74 +++- wolfcrypt/src/wc_frodokem_asm.S | 8 +- wolfcrypt/src/wc_frodokem_asm.asm | 4 + wolfcrypt/src/wc_frodokem_mat.c | 28 +- wolfcrypt/test/test.c | 319 +++++++++++++++++- wolfssl/wolfcrypt/settings.h | 6 + wolfssl/wolfcrypt/wc_frodokem.h | 16 + 17 files changed, 492 insertions(+), 46 deletions(-) diff --git a/.github/workflows/cryptocb-only.yml b/.github/workflows/cryptocb-only.yml index d8883f81acd..e546cf1ca41 100644 --- a/.github/workflows/cryptocb-only.yml +++ b/.github/workflows/cryptocb-only.yml @@ -117,6 +117,9 @@ jobs: {"name": "curve25519", "comment": "WOLF_CRYPTO_CB_ONLY_CURVE25519: strips software X25519 (keygen/shared-secret); swdev provides the software path via cryptocb. Nonblock and async X25519 have no callback path and are left disabled.", "configure": ["CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_CURVE25519"]}, + {"name": "frodokem", + "comment": "WOLF_CRYPTO_CB_ONLY_FRODOKEM: strips the FrodoKEM lattice math (key generation, encapsulation, decapsulation, matrix-A generation, noise sampling, the matrix multiplies and pack/unpack, plus the x86 and ARM assembly). The matrix store/load and one-shot hash helpers stay, because the key encode/decode API a callback needs still uses them. FrodoKEM is not wired into TLS, so nothing else has to be turned off; it does need --enable-experimental, which the shared base does not set.", + "configure": ["--enable-experimental", "--enable-frodokem", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_FRODOKEM"]}, {"name": "all", "comment": "All seven ONLY_* macros at once: every supported software primitive is stripped and dispatched through cryptocb. Catches any cross-algorithm call that a single-strip entry would still resolve via the remaining software paths.", "configure": ["CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_ECC -DWOLF_CRYPTO_CB_ONLY_RSA -DWOLF_CRYPTO_CB_ONLY_SHA256 -DWOLF_CRYPTO_CB_ONLY_SHA512 -DWOLF_CRYPTO_CB_ONLY_AES -DWOLF_CRYPTO_CB_ONLY_ED25519 -DWOLF_CRYPTO_CB_ONLY_CURVE25519"]} diff --git a/tests/api/test_frodokem.c b/tests/api/test_frodokem.c index 26c549b310c..e0b153570ae 100644 --- a/tests/api/test_frodokem.c +++ b/tests/api/test_frodokem.c @@ -100,7 +100,8 @@ static const int frodokem_types[] = { /* The KAT data is only used by the make-key/encapsulate/decapsulate KAT tests, * all of which need key generation to reconstruct the key. */ -#if !defined(NO_SHA256) && !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) +#if !defined(NO_SHA256) && !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) /* Known-answer test data derived from the official FrodoKEM and eFrodoKEM * KAT vectors (PQCkemKAT_*.rsp, count 0), for both the SHAKE and AES matrix A @@ -755,6 +756,7 @@ int test_wc_frodokem_make_key_kats(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(NO_SHA256) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) && \ !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) int i; FrodoKemKey* key = NULL; @@ -801,6 +803,7 @@ int test_wc_frodokem_encapsulate_kats(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(NO_SHA256) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) && \ !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) && \ !defined(WOLFSSL_FRODOKEM_NO_ENCAPSULATE) int i; @@ -850,6 +853,7 @@ int test_wc_frodokem_decapsulate_kats(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(NO_SHA256) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) && \ !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) && \ !defined(WOLFSSL_FRODOKEM_NO_ENCAPSULATE) && \ !defined(WOLFSSL_FRODOKEM_NO_DECAPSULATE) @@ -915,6 +919,7 @@ int test_wc_frodokem_roundtrip(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WC_NO_RNG) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) && \ !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) && \ !defined(WOLFSSL_FRODOKEM_NO_ENCAPSULATE) && \ !defined(WOLFSSL_FRODOKEM_NO_DECAPSULATE) @@ -969,6 +974,7 @@ int test_wc_frodokem_encode_decode(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WC_NO_RNG) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) && \ !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) && \ !defined(WOLFSSL_FRODOKEM_NO_ENCAPSULATE) && \ !defined(WOLFSSL_FRODOKEM_NO_DECAPSULATE) @@ -1061,6 +1067,7 @@ int test_wc_frodokem_decap_implicit_reject(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WC_NO_RNG) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) && \ !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) && \ !defined(WOLFSSL_FRODOKEM_NO_ENCAPSULATE) && \ !defined(WOLFSSL_FRODOKEM_NO_DECAPSULATE) @@ -1148,6 +1155,7 @@ int test_wc_frodokem_decapsulate_pubonly_fails(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WC_NO_RNG) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) && \ !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) && \ !defined(WOLFSSL_FRODOKEM_NO_ENCAPSULATE) && \ !defined(WOLFSSL_FRODOKEM_NO_DECAPSULATE) @@ -1216,6 +1224,7 @@ int test_wc_frodokem_decode_privkey_bad_pkh(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WC_NO_RNG) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) && \ !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) int i; FrodoKemKey* key = NULL; @@ -1439,6 +1448,7 @@ int test_wc_frodokem_op_len_checks(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WC_NO_RNG) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) && \ !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) && \ !defined(WOLFSSL_FRODOKEM_NO_ENCAPSULATE) && \ !defined(WOLFSSL_FRODOKEM_NO_DECAPSULATE) @@ -1579,6 +1589,7 @@ int test_wc_frodokem_asn1(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLFSSL_FRODOKEM_NO_ASN1) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) && \ defined(WC_ENABLE_ASYM_KEY_EXPORT) && \ defined(WC_ENABLE_ASYM_KEY_IMPORT) && !defined(WC_NO_RNG) && \ !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) && \ @@ -1768,6 +1779,7 @@ int test_wc_frodokem_key_pem(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLFSSL_FRODOKEM_NO_ASN1) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) && \ defined(WC_ENABLE_ASYM_KEY_EXPORT) && \ defined(WC_ENABLE_ASYM_KEY_IMPORT) && \ defined(WOLFSSL_DER_TO_PEM) && defined(WOLFSSL_PEM_TO_DER) && \ @@ -1867,6 +1879,7 @@ int test_wc_frodokem_x509(void) { EXPECT_DECLS; #if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLFSSL_FRODOKEM_NO_ASN1) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) && \ defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_ASN_TEMPLATE) && \ defined(HAVE_ECC) && \ defined(WOLFSSL_WC_FRODOKEM_976) && defined(WOLFSSL_FRODOKEM_SHAKE) && \ diff --git a/wolfcrypt/src/cryptocb.c b/wolfcrypt/src/cryptocb.c index 569a68e01a6..bbd7f008608 100644 --- a/wolfcrypt/src/cryptocb.c +++ b/wolfcrypt/src/cryptocb.c @@ -66,6 +66,7 @@ Crypto Callback Build Options: * WOLF_CRYPTO_CB_ONLY_AES: Use only callbacks for AES default: off * WOLF_CRYPTO_CB_ONLY_ED25519: Use only callbacks for Ed25519 default: off * WOLF_CRYPTO_CB_ONLY_CURVE25519: Use only callbacks for X25519 default: off + * WOLF_CRYPTO_CB_ONLY_FRODOKEM: Use only callbacks for FrodoKEM default: off */ #include @@ -1685,8 +1686,12 @@ int wc_CryptoCb_MakePqcKemKey(WC_RNG* rng, int type, int keySize, void* key) /* get devId */ devId = wc_CryptoCb_PqcKemGetDevId(type, key); + /* A find-callback build lets the device find callback resolve an unset + * device id, so leave the lookup below to decide. */ +#ifndef WOLF_CRYPTO_CB_FIND if (devId == INVALID_DEVID) return ret; +#endif /* locate registered callback */ dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK); @@ -1719,8 +1724,12 @@ int wc_CryptoCb_PqcEncapsulate(byte* ciphertext, word32 ciphertextLen, /* get devId */ devId = wc_CryptoCb_PqcKemGetDevId(type, key); + /* A find-callback build lets the device find callback resolve an unset + * device id, so leave the lookup below to decide. */ +#ifndef WOLF_CRYPTO_CB_FIND if (devId == INVALID_DEVID) return ret; +#endif /* locate registered callback */ dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK); @@ -1755,8 +1764,12 @@ int wc_CryptoCb_PqcDecapsulate(const byte* ciphertext, word32 ciphertextLen, /* get devId */ devId = wc_CryptoCb_PqcKemGetDevId(type, key); + /* A find-callback build lets the device find callback resolve an unset + * device id, so leave the lookup below to decide. */ +#ifndef WOLF_CRYPTO_CB_FIND if (devId == INVALID_DEVID) return ret; +#endif /* locate registered callback */ dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK); diff --git a/wolfcrypt/src/port/arm/armv8-32-frodokem-asm.S b/wolfcrypt/src/port/arm/armv8-32-frodokem-asm.S index 11e00e0b7c8..a50338e2161 100644 --- a/wolfcrypt/src/port/arm/armv8-32-frodokem-asm.S +++ b/wolfcrypt/src/port/arm/armv8-32-frodokem-asm.S @@ -32,7 +32,7 @@ #ifdef WOLFSSL_ARMASM #if !defined(__aarch64__) && !defined(WOLFSSL_ARMASM_THUMB2) #ifndef WOLFSSL_ARMASM_INLINE -#ifdef WOLFSSL_HAVE_FRODOKEM +#if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) #ifndef WOLFSSL_ARMASM_NO_NEON .text .align 4 @@ -402,8 +402,8 @@ L_frodokem_mul_add_sb_plus_e_neon_j: pop {r4, r5, r6, r7, r8, r9, pc} .size frodokem_mul_add_sb_plus_e_neon,.-frodokem_mul_add_sb_plus_e_neon #endif /* WOLFSSL_ARMASM_NO_NEON */ -#endif /* WOLFSSL_HAVE_FRODOKEM */ -#ifdef WOLFSSL_HAVE_FRODOKEM +#endif /* WOLFSSL_HAVE_FRODOKEM && !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ +#if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) #ifndef WOLFSSL_ARMASM_NO_NEON .fpu crypto-neon-fp-armv8 .text @@ -507,8 +507,8 @@ L_frodokem_gen_a_rows_aes_arm32_aes: pop {r4, r5, r6, r7, r8, r9, pc} .size frodokem_gen_a_rows_aes_arm32,.-frodokem_gen_a_rows_aes_arm32 #endif /* WOLFSSL_ARMASM_NO_NEON */ -#endif /* WOLFSSL_HAVE_FRODOKEM */ -#ifdef WOLFSSL_HAVE_FRODOKEM +#endif /* WOLFSSL_HAVE_FRODOKEM && !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ +#if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) #ifdef __ARM_FEATURE_SIMD32 #if (!defined(__ARM_NEON) && !defined(__ARM_NEON__)) || defined(WOLFSSL_ARMASM_NO_NEON) .text @@ -694,7 +694,7 @@ L_frodokem_mul_add_sb_plus_e_simd32_j: .size frodokem_mul_add_sb_plus_e_simd32,.-frodokem_mul_add_sb_plus_e_simd32 #endif /* (!defined(__ARM_NEON) && !defined(__ARM_NEON__)) || defined(WOLFSSL_ARMASM_NO_NEON) */ #endif /* __ARM_FEATURE_SIMD32 */ -#endif /* WOLFSSL_HAVE_FRODOKEM */ +#endif /* WOLFSSL_HAVE_FRODOKEM && !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ #if defined(__linux__) && defined(__ELF__) .section .note.GNU-stack,"",%progbits diff --git a/wolfcrypt/src/port/arm/armv8-32-frodokem-asm_c.c b/wolfcrypt/src/port/arm/armv8-32-frodokem-asm_c.c index 0fdd3931527..739b9556e87 100644 --- a/wolfcrypt/src/port/arm/armv8-32-frodokem-asm_c.c +++ b/wolfcrypt/src/port/arm/armv8-32-frodokem-asm_c.c @@ -51,7 +51,7 @@ #include -#ifdef WOLFSSL_HAVE_FRODOKEM +#if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) #ifndef WOLFSSL_ARMASM_NO_NEON void frodokem_add_neon(word16* a_p, const word16* b_p, int qmask_p); #ifndef WOLFSSL_NO_VAR_ASSIGN_REG @@ -560,8 +560,8 @@ WC_OMIT_FRAME_POINTER void frodokem_mul_add_sb_plus_e_neon(word16* out, } #endif /* WOLFSSL_ARMASM_NO_NEON */ -#endif /* WOLFSSL_HAVE_FRODOKEM */ -#ifdef WOLFSSL_HAVE_FRODOKEM +#endif /* WOLFSSL_HAVE_FRODOKEM && !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ +#if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) #ifdef __ARM_FEATURE_SIMD32 #if (!defined(__ARM_NEON) && !defined(__ARM_NEON__)) || \ defined(WOLFSSL_ARMASM_NO_NEON) @@ -872,7 +872,7 @@ WC_OMIT_FRAME_POINTER void frodokem_mul_add_sb_plus_e_simd32(word16* out, #endif /* (!defined(__ARM_NEON) && !defined(__ARM_NEON__)) || * defined(WOLFSSL_ARMASM_NO_NEON) */ #endif /* __ARM_FEATURE_SIMD32 */ -#endif /* WOLFSSL_HAVE_FRODOKEM */ +#endif /* WOLFSSL_HAVE_FRODOKEM && !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ #endif /* WOLFSSL_ARMASM_INLINE */ #endif /* !__aarch64__ && !WOLFSSL_ARMASM_THUMB2 */ diff --git a/wolfcrypt/src/port/arm/armv8-frodokem-asm.S b/wolfcrypt/src/port/arm/armv8-frodokem-asm.S index 1e5188193fe..d4af5937f0b 100644 --- a/wolfcrypt/src/port/arm/armv8-frodokem-asm.S +++ b/wolfcrypt/src/port/arm/armv8-frodokem-asm.S @@ -32,7 +32,7 @@ #ifdef WOLFSSL_ARMASM #ifdef __aarch64__ #ifndef WOLFSSL_ARMASM_INLINE -#ifdef WOLFSSL_HAVE_FRODOKEM +#if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) #ifndef __APPLE__ .text .section .rodata @@ -1500,8 +1500,8 @@ L_frodokem_mul_add_sb_plus_e_neon_j: #ifndef __APPLE__ .size frodokem_mul_add_sb_plus_e_neon,.-frodokem_mul_add_sb_plus_e_neon #endif /* __APPLE__ */ -#endif /* WOLFSSL_HAVE_FRODOKEM */ -#ifdef WOLFSSL_HAVE_FRODOKEM +#endif /* WOLFSSL_HAVE_FRODOKEM && !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ +#if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) .arch armv8-a+crypto #ifndef __APPLE__ .text @@ -1825,8 +1825,8 @@ L_frodokem_gen_a_rows_aes_arm64_done: #ifndef __APPLE__ .size frodokem_gen_a_rows_aes_arm64,.-frodokem_gen_a_rows_aes_arm64 #endif /* __APPLE__ */ -#endif /* WOLFSSL_HAVE_FRODOKEM */ -#ifdef WOLFSSL_HAVE_FRODOKEM +#endif /* WOLFSSL_HAVE_FRODOKEM && !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ +#if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) #ifdef WOLFSSL_FRODOKEM_SVE .arch armv9-a+sve2 #ifndef __APPLE__ @@ -2217,8 +2217,8 @@ L_frodokem_add_sve_k: .size frodokem_add_sve,.-frodokem_add_sve #endif /* __APPLE__ */ #endif /* WOLFSSL_FRODOKEM_SVE */ -#endif /* WOLFSSL_HAVE_FRODOKEM */ -#ifdef WOLFSSL_HAVE_FRODOKEM +#endif /* WOLFSSL_HAVE_FRODOKEM && !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ +#if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) #ifdef WOLFSSL_FRODOKEM_SME .arch armv9-a+sve2+sme+sme2 #ifndef __APPLE__ @@ -2378,7 +2378,7 @@ L_frodokem_as_accum_sme_k: .size frodokem_as_accum_sme,.-frodokem_as_accum_sme #endif /* __APPLE__ */ #endif /* WOLFSSL_FRODOKEM_SME */ -#endif /* WOLFSSL_HAVE_FRODOKEM */ +#endif /* WOLFSSL_HAVE_FRODOKEM && !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ #endif /* __aarch64__ */ #endif /* WOLFSSL_ARMASM */ diff --git a/wolfcrypt/src/port/arm/armv8-frodokem-asm.asm b/wolfcrypt/src/port/arm/armv8-frodokem-asm.asm index 943ed2b9304..6aedb4b9e1a 100644 --- a/wolfcrypt/src/port/arm/armv8-frodokem-asm.asm +++ b/wolfcrypt/src/port/arm/armv8-frodokem-asm.asm @@ -23,7 +23,7 @@ ; cd ../scripts ; ruby ./frodokem/frodokem.rb arm64 \ ; ../wolfssl/wolfcrypt/src/port/arm/armv8-frodokem-asm.asm - IF :DEF:WOLFSSL_HAVE_FRODOKEM + IF :DEF:WOLFSSL_HAVE_FRODOKEM :LAND: :LNOT::DEF:WOLF_CRYPTO_CB_ONLY_FRODOKEM AREA |.rodata|, DATA, READONLY, ALIGN=4 ALIGN 16 L_sha3_aarch64_r @@ -1348,7 +1348,7 @@ L_frodokem_mul_add_sb_plus_e_neon_j ret ENDP ENDIF - IF :DEF:WOLFSSL_HAVE_FRODOKEM + IF :DEF:WOLFSSL_HAVE_FRODOKEM :LAND: :LNOT::DEF:WOLF_CRYPTO_CB_ONLY_FRODOKEM ; .arch armv8-a+crypto AREA |.text|, CODE, READONLY ALIGN 4 diff --git a/wolfcrypt/src/port/arm/armv8-frodokem-asm_c.c b/wolfcrypt/src/port/arm/armv8-frodokem-asm_c.c index 40bd4de1271..a339ddd39d2 100644 --- a/wolfcrypt/src/port/arm/armv8-frodokem-asm_c.c +++ b/wolfcrypt/src/port/arm/armv8-frodokem-asm_c.c @@ -35,7 +35,7 @@ #ifdef WOLFSSL_ARMASM_INLINE #include -#ifdef WOLFSSL_HAVE_FRODOKEM +#if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) XALIGNED(16) static const word64 L_sha3_aarch64_r[] = { 0x0000000000000001UL, 0x0000000000008082UL, 0x800000000000808aUL, 0x8000000080008000UL, @@ -1314,7 +1314,7 @@ void frodokem_mul_add_sb_plus_e_neon(word16* out, const word16* b, ); } -#endif /* WOLFSSL_HAVE_FRODOKEM */ +#endif /* WOLFSSL_HAVE_FRODOKEM && !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ #endif /* __aarch64__ */ #endif /* WOLFSSL_ARMASM */ #endif /* WOLFSSL_ARMASM_INLINE */ diff --git a/wolfcrypt/src/port/arm/thumb2-frodokem-asm.S b/wolfcrypt/src/port/arm/thumb2-frodokem-asm.S index 9ebbfaecb0d..300f610228c 100644 --- a/wolfcrypt/src/port/arm/thumb2-frodokem-asm.S +++ b/wolfcrypt/src/port/arm/thumb2-frodokem-asm.S @@ -34,7 +34,7 @@ #ifndef WOLFSSL_ARMASM_INLINE .thumb .syntax unified -#ifdef WOLFSSL_HAVE_FRODOKEM +#if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) .text .align 4 .globl frodokem_add_thumb2 @@ -261,7 +261,7 @@ L_frodokem_mul_add_sb_plus_e_thumb2_j: POP {r4, r5, r6, r7, r8, r9, r10, r11, pc} /* Cycle Count = 97 */ .size frodokem_mul_add_sb_plus_e_thumb2,.-frodokem_mul_add_sb_plus_e_thumb2 -#endif /* WOLFSSL_HAVE_FRODOKEM */ +#endif /* WOLFSSL_HAVE_FRODOKEM && !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ #if defined(__linux__) && defined(__ELF__) .section .note.GNU-stack,"",%progbits diff --git a/wolfcrypt/src/port/arm/thumb2-frodokem-asm_c.c b/wolfcrypt/src/port/arm/thumb2-frodokem-asm_c.c index b85d3ab60ff..43381483b61 100644 --- a/wolfcrypt/src/port/arm/thumb2-frodokem-asm_c.c +++ b/wolfcrypt/src/port/arm/thumb2-frodokem-asm_c.c @@ -51,7 +51,7 @@ #include -#ifdef WOLFSSL_HAVE_FRODOKEM +#if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) #ifndef WOLFSSL_NO_VAR_ASSIGN_REG WC_OMIT_FRAME_POINTER void frodokem_add_thumb2(word16* a_p, const word16* b_p, int qmask_p) @@ -456,7 +456,7 @@ WC_OMIT_FRAME_POINTER void frodokem_mul_add_sb_plus_e_thumb2(word16* out, ); } -#endif /* WOLFSSL_HAVE_FRODOKEM */ +#endif /* WOLFSSL_HAVE_FRODOKEM && !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ #endif /* WOLFSSL_ARMASM_INLINE */ #endif /* WOLFSSL_ARMASM_THUMB2 */ diff --git a/wolfcrypt/src/wc_frodokem.c b/wolfcrypt/src/wc_frodokem.c index 5244673422e..b11c6a911f5 100644 --- a/wolfcrypt/src/wc_frodokem.c +++ b/wolfcrypt/src/wc_frodokem.c @@ -416,6 +416,20 @@ int wc_FrodoKemKey_Free(FrodoKemKey* key) ret = BAD_FUNC_ARG; } else { +#if defined(WOLF_CRYPTO_CB) && defined(WOLF_CRYPTO_CB_FREE) + /* Let the device release its side of the key (anything it hung off + * devCtx). Always continue to the software cleanup below. In a + * find-callback build the key may keep an unset device id and still be + * owned by a device, so the dispatcher does the lookup. */ + #ifndef WOLF_CRYPTO_CB_FIND + if (key->devId != INVALID_DEVID) + #endif + { + (void)wc_CryptoCb_Free(key->devId, WC_ALGO_TYPE_PK, + WC_PK_TYPE_PQC_KEM_KEYGEN, WC_PQC_KEM_TYPE_FRODOKEM, + (void*)key); + } +#endif /* Zeroize secret material. */ ForceZero(key->s, sizeof(key->s)); ForceZero(key->sMat, sizeof(key->sMat)); @@ -557,6 +571,7 @@ int wc_FrodoKemKey_PublicKeySize(const FrodoKemKey* key, word32* len) return ret; } +#ifndef WOLF_CRYPTO_CB_ONLY_FRODOKEM /* Wipe the secret-derived Keccak state retained in the reusable SHAKE object * after an operation. * @@ -579,6 +594,7 @@ static void frodokem_wipe_shake(FrodoKemKey* key) } } } +#endif /* !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ /******************************************************************************/ /* Key generation. */ @@ -605,6 +621,7 @@ int wc_FrodoKemKey_MakeKeyWithRandom(FrodoKemKey* key, { const FrodoKemParams* p = NULL; int ret = 0; +#ifndef WOLF_CRYPTO_CB_ONLY_FRODOKEM int n = 0; void* heap = NULL; const byte* seedSE; @@ -615,6 +632,7 @@ int wc_FrodoKemKey_MakeKeyWithRandom(FrodoKemKey* key, word16* bMat = NULL; word16* row = NULL; byte* seInput = NULL; +#endif /* !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ if ((key == NULL) || (rand == NULL)) { ret = BAD_FUNC_ARG; @@ -629,6 +647,14 @@ int wc_FrodoKemKey_MakeKeyWithRandom(FrodoKemKey* key, } } +#ifdef WOLF_CRYPTO_CB_ONLY_FRODOKEM + if (ret == 0) { + /* No software fallback: only a crypto callback can service the + * request, and no callback takes caller-chosen key generation + * randomness. */ + ret = NO_VALID_DEVID; + } +#else if (ret == 0) { n = p->n; heap = key->heap; @@ -695,6 +721,7 @@ int wc_FrodoKemKey_MakeKeyWithRandom(FrodoKemKey* key, /* Wipe secret-derived residue from the reusable SHAKE state. */ frodokem_wipe_shake(key); +#endif /* WOLF_CRYPTO_CB_ONLY_FRODOKEM */ return ret; } @@ -741,8 +768,14 @@ int wc_FrodoKemKey_MakeKey(FrodoKemKey* key, WC_RNG* rng) } #ifdef WOLF_CRYPTO_CB /* Offload to a registered crypto callback when a device is set; fall - * through to the software path when the callback is unavailable. */ + * through to the software path when the callback is unavailable. A + * find-callback build lets the dispatcher locate the device, so the key's + * device id is not consulted. */ +#ifndef WOLF_CRYPTO_CB_FIND if ((ret == 0) && (key->devId != INVALID_DEVID)) { +#else + if (ret == 0) { +#endif ret = wc_CryptoCb_MakePqcKemKey(rng, WC_PQC_KEM_TYPE_FRODOKEM, key->type, key); cbHandled = (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)); @@ -820,6 +853,7 @@ int wc_FrodoKemKey_EncapsulateWithRandom(FrodoKemKey* key, unsigned char* ct, { const FrodoKemParams* p = NULL; int ret = 0; +#ifndef WOLF_CRYPTO_CB_ONLY_FRODOKEM int n = 0; int nn = FRODOKEM_NBAR_SQ; void* heap = NULL; @@ -847,6 +881,7 @@ int wc_FrodoKemKey_EncapsulateWithRandom(FrodoKemKey* key, unsigned char* ct, #endif byte* kVal; size_t matSz = 0; +#endif /* !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ if ((key == NULL) || (ct == NULL) || (ss == NULL) || (rand == NULL)) { ret = BAD_FUNC_ARG; @@ -864,6 +899,14 @@ int wc_FrodoKemKey_EncapsulateWithRandom(FrodoKemKey* key, unsigned char* ct, } } +#ifdef WOLF_CRYPTO_CB_ONLY_FRODOKEM + if (ret == 0) { + /* No software fallback: only a crypto callback can service the + * request, and no callback takes caller-chosen encapsulation + * randomness. */ + ret = NO_VALID_DEVID; + } +#else if (ret == 0) { n = p->n; heap = key->heap; @@ -963,6 +1006,7 @@ int wc_FrodoKemKey_EncapsulateWithRandom(FrodoKemKey* key, unsigned char* ct, /* Wipe secret-derived residue from the reusable SHAKE state. */ frodokem_wipe_shake(key); +#endif /* WOLF_CRYPTO_CB_ONLY_FRODOKEM */ return ret; } @@ -1012,8 +1056,14 @@ int wc_FrodoKemKey_Encapsulate(FrodoKemKey* key, unsigned char* ct, } #ifdef WOLF_CRYPTO_CB /* Offload to a registered crypto callback when a device is set; fall - * through to the software path when the callback is unavailable. */ + * through to the software path when the callback is unavailable. A + * find-callback build lets the dispatcher locate the device, so the key's + * device id is not consulted. */ +#ifndef WOLF_CRYPTO_CB_FIND if ((ret == 0) && (key->devId != INVALID_DEVID)) { +#else + if (ret == 0) { +#endif ret = wc_CryptoCb_PqcEncapsulate(ct, (word32)p->ctSize, ss, (word32)p->lenSec, rng, WC_PQC_KEM_TYPE_FRODOKEM, key); cbHandled = (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)); @@ -1091,6 +1141,8 @@ int wc_FrodoKemKey_Decapsulate(FrodoKemKey* key, unsigned char* ss, { const FrodoKemParams* p = NULL; int ret = 0; + int cbHandled = 0; +#ifndef WOLF_CRYPTO_CB_ONLY_FRODOKEM int n = 0; int i; int nn = FRODOKEM_NBAR_SQ; @@ -1129,7 +1181,7 @@ int wc_FrodoKemKey_Decapsulate(FrodoKemKey* key, unsigned char* ss, word32 isEq; byte mask; size_t matSz = 0; - int cbHandled = 0; +#endif /* !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ if ((key == NULL) || (ss == NULL) || (ct == NULL)) { ret = BAD_FUNC_ARG; @@ -1149,8 +1201,14 @@ int wc_FrodoKemKey_Decapsulate(FrodoKemKey* key, unsigned char* ss, #ifdef WOLF_CRYPTO_CB /* Offload to a registered crypto callback when a device is set; fall - * through to the software path when the callback is unavailable. */ + * through to the software path when the callback is unavailable. A + * find-callback build lets the dispatcher locate the device, so the key's + * device id is not consulted. */ +#ifndef WOLF_CRYPTO_CB_FIND if ((ret == 0) && (key->devId != INVALID_DEVID)) { +#else + if (ret == 0) { +#endif ret = wc_CryptoCb_PqcDecapsulate(ct, len, ss, (word32)p->lenSec, WC_PQC_KEM_TYPE_FRODOKEM, key); cbHandled = (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)); @@ -1159,6 +1217,13 @@ int wc_FrodoKemKey_Decapsulate(FrodoKemKey* key, unsigned char* ss, } } #endif +#ifdef WOLF_CRYPTO_CB_ONLY_FRODOKEM + if ((ret == 0) && !cbHandled) { + /* No software fallback: only a crypto callback can service the + * request. */ + ret = NO_VALID_DEVID; + } +#else if ((ret == 0) && !cbHandled) { n = p->n; heap = key->heap; @@ -1293,6 +1358,7 @@ int wc_FrodoKemKey_Decapsulate(FrodoKemKey* key, unsigned char* ss, /* Wipe secret-derived residue from the reusable SHAKE state. */ frodokem_wipe_shake(key); +#endif /* WOLF_CRYPTO_CB_ONLY_FRODOKEM */ return ret; } diff --git a/wolfcrypt/src/wc_frodokem_asm.S b/wolfcrypt/src/wc_frodokem_asm.S index 80b956b3e3a..f99a1b62275 100644 --- a/wolfcrypt/src/wc_frodokem_asm.S +++ b/wolfcrypt/src/wc_frodokem_asm.S @@ -42,7 +42,7 @@ #endif /* HAVE_INTEL_AVX512 */ #endif /* NO_AVX512_SUPPORT */ -#ifdef WOLFSSL_HAVE_FRODOKEM +#if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) #ifdef HAVE_INTEL_AVX2 #ifndef __APPLE__ .text @@ -887,8 +887,8 @@ L_frodokem_gen_a_rows_aes_aesni_next: .size frodokem_gen_a_rows_aes_aesni,.-frodokem_gen_a_rows_aes_aesni #endif /* __APPLE__ */ #endif /* HAVE_INTEL_AVX2 */ -#endif /* WOLFSSL_HAVE_FRODOKEM */ -#ifdef WOLFSSL_HAVE_FRODOKEM +#endif /* WOLFSSL_HAVE_FRODOKEM && !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ +#if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) #ifdef HAVE_INTEL_AVX512 #ifndef __APPLE__ .text @@ -1600,7 +1600,7 @@ L_frodokem_gen_a_rows_aes_avx512_aes: .size frodokem_gen_a_rows_aes_avx512,.-frodokem_gen_a_rows_aes_avx512 #endif /* __APPLE__ */ #endif /* HAVE_INTEL_AVX512 */ -#endif /* WOLFSSL_HAVE_FRODOKEM */ +#endif /* WOLFSSL_HAVE_FRODOKEM && !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ #if defined(__linux__) && defined(__ELF__) .section .note.GNU-stack,"",%progbits diff --git a/wolfcrypt/src/wc_frodokem_asm.asm b/wolfcrypt/src/wc_frodokem_asm.asm index 47eb73f5f08..1ab087b0de3 100644 --- a/wolfcrypt/src/wc_frodokem_asm.asm +++ b/wolfcrypt/src/wc_frodokem_asm.asm @@ -42,6 +42,7 @@ _WIN64 = 1 ENDIF IFDEF WOLFSSL_HAVE_FRODOKEM +IFNDEF WOLF_CRYPTO_CB_ONLY_FRODOKEM IFDEF HAVE_INTEL_AVX2 _TEXT SEGMENT READONLY PARA frodokem_sa_accum_avx2 PROC @@ -878,7 +879,9 @@ frodokem_gen_a_rows_aes_aesni ENDP _TEXT ENDS ENDIF ENDIF +ENDIF IFDEF WOLFSSL_HAVE_FRODOKEM +IFNDEF WOLF_CRYPTO_CB_ONLY_FRODOKEM IFDEF HAVE_INTEL_AVX512 _TEXT SEGMENT READONLY PARA frodokem_sa_accum_avx512 PROC @@ -1610,4 +1613,5 @@ frodokem_gen_a_rows_aes_avx512 ENDP _TEXT ENDS ENDIF ENDIF +ENDIF END diff --git a/wolfcrypt/src/wc_frodokem_mat.c b/wolfcrypt/src/wc_frodokem_mat.c index f80966b0b06..fe560b944e5 100644 --- a/wolfcrypt/src/wc_frodokem_mat.c +++ b/wolfcrypt/src/wc_frodokem_mat.c @@ -197,15 +197,16 @@ #define FRODOKEM_RESTRICT #endif -#if defined(USE_INTEL_SPEEDUP) || defined(FRODOKEM_HAVE_SVE) || \ - defined(FRODOKEM_HAVE_NEON_RUNTIME) +#if (defined(USE_INTEL_SPEEDUP) || defined(FRODOKEM_HAVE_SVE) || \ + defined(FRODOKEM_HAVE_NEON_RUNTIME)) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) /* Cached CPU feature flags used to select SIMD routines: AVX2 / BMI2 Keccak in * matrix-A generation on Intel, and the SVE / NEON matrix ops on AArch64 (NEON * is gated on Advanced SIMD). Populated once by frodokem_init(). */ static cpuid_flags_t cpuid_flags = WC_CPUID_INITIALIZER; #endif -#ifdef FRODOKEM_HAVE_SME +#if defined(FRODOKEM_HAVE_SME) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) /* The SME kernels compute the whole nbar x nbar (8 x 8) product in one ZA.S * tile, which needs a streaming vector length (SVL) of at least 256 bits (32 * bytes). HWCAP2_SME alone does not guarantee that, so the SVL is measured and @@ -231,17 +232,23 @@ static WC_INLINE int frodokem_sme_svl_bytes(void) * call repeatedly (the flags are read from the CPU only once). */ void frodokem_init(void) { -#if defined(USE_INTEL_SPEEDUP) || defined(FRODOKEM_HAVE_SVE) || \ - defined(FRODOKEM_HAVE_NEON_RUNTIME) +#if (defined(USE_INTEL_SPEEDUP) || defined(FRODOKEM_HAVE_SVE) || \ + defined(FRODOKEM_HAVE_NEON_RUNTIME)) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) cpuid_get_flags_ex(&cpuid_flags); #endif -#ifdef FRODOKEM_HAVE_SME +#if defined(FRODOKEM_HAVE_SME) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) /* SME needs SVL >= 256 bits (>= 32 bytes) for the 8 x 8 ZA.S tile. */ frodokem_sme_svl_ok = IS_AARCH64_SME(cpuid_flags) && (frodokem_sme_svl_bytes() >= 32); #endif } +/* In a callback-only build every operation that uses the lattice math is + * serviced by a crypto callback, so only the matrix store/load and the one-shot + * hash stay: the key encode and decode API is all that still calls in here. */ +#ifndef WOLF_CRYPTO_CB_ONLY_FRODOKEM + #ifdef FRODOKEM_HAVE_SME /* Portable-C A * S accumulate (defined later): the SME fallback on allocation * failure. Compiled here since FRODOKEM_HAVE_SME implies AArch64 NEON @@ -468,6 +475,8 @@ void frodokem_unpack(word16* out, const byte* in, int nElem, int d) #endif } +#endif /* !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ + /* Serialize a matrix of word16 coefficients as little-endian 16-bit values. * * Used for the secret matrix S^T in the private key, whose two's-complement @@ -521,6 +530,8 @@ void frodokem_load_matrix(word16* mat, const byte* in, int cnt) /* Encoding and decoding of messages to/from matrices (Section 6.1). */ /******************************************************************************/ +#ifndef WOLF_CRYPTO_CB_ONLY_FRODOKEM + /* Encode a message bit string into an nbar x nbar matrix. * * Groups of B bits (least-significant bit of each byte first, Section 6.1) are @@ -699,6 +710,8 @@ int frodokem_shake(const FrodoKemParams* p, wc_Shake* shake, const byte* in0, return ret; } +#endif /* !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ + /* One-shot SHAKE over a single contiguous input buffer, selecting SHAKE128 for * FrodoKEM-640 and SHAKE256 for FrodoKEM-976 / -1344. * @@ -750,6 +763,8 @@ int frodokem_shake_oneshot(const FrodoKemParams* p, wc_Shake* shake, return ret; } +#ifndef WOLF_CRYPTO_CB_ONLY_FRODOKEM + /******************************************************************************/ /* Error sampling (Section 6.5). */ /******************************************************************************/ @@ -3077,5 +3092,6 @@ void frodokem_add(word16* a, const word16* b, int qmask) } #endif /* FRODOKEM_HAVE_ARM_ASM */ } +#endif /* !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ #endif /* WOLFSSL_HAVE_FRODOKEM */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 0556fd53513..22dc4901e56 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -56775,6 +56775,44 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t mlkem_test(void) #endif /* WOLFSSL_HAVE_MLKEM */ #ifdef WOLFSSL_HAVE_FRODOKEM + +/* Any compiled-in parameter set shows the dispatch behaviour; which one is + * irrelevant, so pick the first that is actually built. */ +#ifdef WOLFSSL_FRODOKEM_SHAKE + #if defined(WOLFSSL_WC_FRODOKEM_640) + #define FRODOKEM_TEST_TYPE WC_FRODOKEM_640_SHAKE + #define FRODOKEM_TEST_RAND_SZ WC_FRODOKEM_640_MAKEKEY_RAND_SZ + #define FRODOKEM_TEST_ENC_SZ WC_FRODOKEM_640_ENC_RAND_SZ + #elif defined(WOLFSSL_WC_FRODOKEM_976) + #define FRODOKEM_TEST_TYPE WC_FRODOKEM_976_SHAKE + #define FRODOKEM_TEST_RAND_SZ WC_FRODOKEM_976_MAKEKEY_RAND_SZ + #define FRODOKEM_TEST_ENC_SZ WC_FRODOKEM_976_ENC_RAND_SZ + #define FRODOKEM_TEST_SHAKE256 + #elif defined(WOLFSSL_WC_FRODOKEM_1344) + #define FRODOKEM_TEST_TYPE WC_FRODOKEM_1344_SHAKE + #define FRODOKEM_TEST_RAND_SZ WC_FRODOKEM_1344_MAKEKEY_RAND_SZ + #define FRODOKEM_TEST_ENC_SZ WC_FRODOKEM_1344_ENC_RAND_SZ + #define FRODOKEM_TEST_SHAKE256 + #endif +#endif +#if !defined(FRODOKEM_TEST_TYPE) && defined(WOLFSSL_FRODOKEM_AES) + #if defined(WOLFSSL_WC_FRODOKEM_640) + #define FRODOKEM_TEST_TYPE WC_FRODOKEM_640_AES + #define FRODOKEM_TEST_RAND_SZ WC_FRODOKEM_640_MAKEKEY_RAND_SZ + #define FRODOKEM_TEST_ENC_SZ WC_FRODOKEM_640_ENC_RAND_SZ + #elif defined(WOLFSSL_WC_FRODOKEM_976) + #define FRODOKEM_TEST_TYPE WC_FRODOKEM_976_AES + #define FRODOKEM_TEST_RAND_SZ WC_FRODOKEM_976_MAKEKEY_RAND_SZ + #define FRODOKEM_TEST_ENC_SZ WC_FRODOKEM_976_ENC_RAND_SZ + #define FRODOKEM_TEST_SHAKE256 + #elif defined(WOLFSSL_WC_FRODOKEM_1344) + #define FRODOKEM_TEST_TYPE WC_FRODOKEM_1344_AES + #define FRODOKEM_TEST_RAND_SZ WC_FRODOKEM_1344_MAKEKEY_RAND_SZ + #define FRODOKEM_TEST_ENC_SZ WC_FRODOKEM_1344_ENC_RAND_SZ + #define FRODOKEM_TEST_SHAKE256 + #endif +#endif + /* Basic FrodoKEM test: for each compiled variant generate a key, encapsulate * and decapsulate (shared secrets must match), then confirm an encode/decode * round trip of the private key still decapsulates correctly. */ @@ -56783,7 +56821,8 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t frodokem_test(void) wc_test_ret_t ret = 0; #if !defined(WC_NO_RNG) && !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) && \ !defined(WOLFSSL_FRODOKEM_NO_ENCAPSULATE) && \ - !defined(WOLFSSL_FRODOKEM_NO_DECAPSULATE) + !defined(WOLFSSL_FRODOKEM_NO_DECAPSULATE) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) /* ASN.1 key encode/decode is exercised when it has not been disabled. */ #if !defined(WOLFSSL_FRODOKEM_NO_ASN1) && \ defined(WC_ENABLE_ASYM_KEY_EXPORT) && defined(WC_ENABLE_ASYM_KEY_IMPORT) @@ -57015,6 +57054,200 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t frodokem_test(void) #undef FRODOKEM_TEST_ASN1 #endif #endif /* !WC_NO_RNG */ + +#if defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) && \ + defined(FRODOKEM_TEST_TYPE) && \ + !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) && \ + !defined(WOLFSSL_FRODOKEM_NO_ENCAPSULATE) && \ + !defined(WOLFSSL_FRODOKEM_NO_DECAPSULATE) + /* Software FrodoKEM is compiled out. Confirm the public API still runs its + * argument checks and then reports that nothing can service the request, + * rather than silently doing nothing. */ + { + /* FrodoKemKey holds maximum-sized matrices, so keep it and the + * ciphertext off the stack as the test above does. */ + FrodoKemKey* key; + byte* pk = NULL; + byte* ct; + int key_inited = 0; + word32 pkLen = 0; + byte rand[FRODOKEM_TEST_RAND_SZ]; + byte ss[FRODOKEM_MAX_LENSEC]; + int r; + + XMEMSET(rand, 0, sizeof(rand)); + + key = (FrodoKemKey*)XMALLOC(sizeof(*key), HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER); + ct = (byte*)XMALLOC(FRODOKEM_MAX_CIPHER_TEXT_SIZE, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER); + if ((key == NULL) || (ct == NULL)) + ret = WC_TEST_RET_ENC_NC; + + if (ret == 0) { + r = wc_FrodoKemKey_Init(key, FRODOKEM_TEST_TYPE, HEAP_HINT, + INVALID_DEVID); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else + key_inited = 1; + } + + /* Argument checks still run ahead of the dispatch report. */ + if (ret == 0) { + r = wc_FrodoKemKey_MakeKeyWithRandom(key, NULL, (int)sizeof(rand)); + if (r != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + r = wc_FrodoKemKey_MakeKeyWithRandom(key, rand, + (int)sizeof(rand) - 1); + if (r != WC_NO_ERR_TRACE(BUFFER_E)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + r = wc_FrodoKemKey_MakeKeyWithRandom(key, rand, (int)sizeof(rand)); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } + + /* Encapsulation needs a public key set. A decoded one is enough: the + * public key has no values to validate. */ + if (ret == 0) { + r = wc_FrodoKemKey_PublicKeySize(key, &pkLen); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } + if (ret == 0) { + pk = (byte*)XMALLOC(pkLen, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + if (pk == NULL) + ret = WC_TEST_RET_ENC_NC; + else + XMEMSET(pk, 0, pkLen); + } + if (ret == 0) { + r = wc_FrodoKemKey_DecodePublicKey(key, pk, pkLen); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } + if (ret == 0) { + r = wc_FrodoKemKey_EncapsulateWithRandom(key, ct, ss, rand, + FRODOKEM_TEST_ENC_SZ); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } + + /* Importing key material is how a callback-backed build gets a usable + * key, and the private key encode/decode pair is all that still calls + * the matrix load/store and one-shot hash helpers. Build a private key + * whose only constrained field, the public key hash, is correct: the + * rest may be zero. Layout is s || seedA || b || S^T || pkh, so the + * hash covers seedA || b, which is the encoded public key. */ + if (ret == 0) { + word32 skLen = 0; + word32 ssLen = 0; + + r = wc_FrodoKemKey_PrivateKeySize(key, &skLen); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + if (ret == 0) { + r = wc_FrodoKemKey_SharedSecretSize(key, &ssLen); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } + if (ret == 0) { + byte* sk = (byte*)XMALLOC(skLen, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER); + wc_Shake shake; + int shakeInit = 0; + + if (sk == NULL) + ret = WC_TEST_RET_ENC_NC; + else + XMEMSET(sk, 0, skLen); + + /* pkh = SHAKE(seedA || b, lenSec), written into the trailing + * lenSec bytes. SHAKE-128 for FrodoKEM-640, SHAKE-256 above. */ + if (ret == 0) { + #ifdef FRODOKEM_TEST_SHAKE256 + r = wc_InitShake256(&shake, HEAP_HINT, INVALID_DEVID); + if (r == 0) { + shakeInit = 1; + r = wc_Shake256_Update(&shake, sk + ssLen, pkLen); + } + if (r == 0) + r = wc_Shake256_Final(&shake, sk + skLen - ssLen, + ssLen); + #else + r = wc_InitShake128(&shake, HEAP_HINT, INVALID_DEVID); + if (r == 0) { + shakeInit = 1; + r = wc_Shake128_Update(&shake, sk + ssLen, pkLen); + } + if (r == 0) + r = wc_Shake128_Final(&shake, sk + skLen - ssLen, + ssLen); + #endif + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } + if (shakeInit) { + #ifdef FRODOKEM_TEST_SHAKE256 + wc_Shake256_Free(&shake); + #else + wc_Shake128_Free(&shake); + #endif + } + + if (ret == 0) { + r = wc_FrodoKemKey_DecodePrivateKey(key, sk, skLen); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + } + /* Re-encoding must reproduce the blob it was decoded from. */ + if (ret == 0) { + byte* sk2 = (byte*)XMALLOC(skLen, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER); + + if (sk2 == NULL) + ret = WC_TEST_RET_ENC_NC; + else { + r = wc_FrodoKemKey_EncodePrivateKey(key, sk2, skLen); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else if (XMEMCMP(sk, sk2, skLen) != 0) + ret = WC_TEST_RET_ENC_NC; + XFREE(sk2, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + } + } + XFREE(sk, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + } + } + + /* With a private key set, decapsulation reaches the dispatch report + * rather than stopping at the key-state check. */ + if (ret == 0) { + word32 ctLen = 0; + + r = wc_FrodoKemKey_CipherTextSize(key, &ctLen); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else { + XMEMSET(ct, 0, ctLen); + r = wc_FrodoKemKey_Decapsulate(key, ss, ct, ctLen); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } + } + + if (key_inited) + wc_FrodoKemKey_Free(key); + XFREE(pk, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(ct, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(key, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + } +#endif /* WOLF_CRYPTO_CB_ONLY_FRODOKEM && FRODOKEM_TEST_TYPE */ + return ret; } #endif /* WOLFSSL_HAVE_FRODOKEM */ @@ -81096,7 +81329,10 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) } } #endif /* WOLFSSL_HAVE_MLKEM */ - #ifdef WOLFSSL_HAVE_FRODOKEM + #if defined(WOLFSSL_HAVE_FRODOKEM) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) + /* exampleVar counts the branches taken so cryptocb_test can confirm + * FrodoKEM really went through the callback. */ if (info->pk.type == WC_PK_TYPE_PQC_KEM_KEYGEN) { if ((info->pk.pqc_kem_kg.type == WC_PQC_KEM_TYPE_FRODOKEM) && (info->pk.pqc_kem_kg.key != NULL)) { @@ -81105,6 +81341,7 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) key->devId = INVALID_DEVID; ret = wc_FrodoKemKey_MakeKey(key, info->pk.pqc_kem_kg.rng); key->devId = devIdArg; + myCtx->exampleVar++; } } else if (info->pk.type == WC_PK_TYPE_PQC_KEM_ENCAPS) { @@ -81117,6 +81354,7 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) info->pk.pqc_encaps.sharedSecret, info->pk.pqc_encaps.rng); key->devId = devIdArg; + myCtx->exampleVar++; } } else if (info->pk.type == WC_PK_TYPE_PQC_KEM_DECAPS) { @@ -81129,9 +81367,10 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) info->pk.pqc_decaps.ciphertext, info->pk.pqc_decaps.ciphertextLen); key->devId = devIdArg; + myCtx->exampleVar++; } } - #endif /* WOLFSSL_HAVE_FRODOKEM */ + #endif /* WOLFSSL_HAVE_FRODOKEM && !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ } else if (info->algo_type == WC_ALGO_TYPE_CIPHER) { #if !defined(NO_AES) || !defined(NO_DES3) @@ -82712,6 +82951,11 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) #ifdef WOLF_CRYPTO_CB_FIND +/* Number of times the find callback should still resolve an unset device id to + * the test device. Each resolution consumes one, so a callback that re-enters + * the same operation to run it in software is not routed back to the device. */ +static int myCryptoCbFindInvalidLeft = 0; + static int myCryptoCbFind(int currentId, int algoType) { /* can have algo specific overrides here @@ -82731,6 +82975,10 @@ static int myCryptoCbFind(int currentId, int algoType) if (currentId == INVALID_DEVID) { /* can override invalid devid found with 1 */ + if (myCryptoCbFindInvalidLeft > 0) { + myCryptoCbFindInvalidLeft--; + return devId; + } } return currentId; } @@ -83035,9 +83283,70 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) if (ret == 0) ret = mlkem_test(); #endif -#ifdef WOLFSSL_HAVE_FRODOKEM - if (ret == 0) +#if defined(WOLFSSL_HAVE_FRODOKEM) && !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) + if (ret == 0) { + /* Route FrodoKEM through the crypto callback (global devId is set) and + * confirm the cb path was actually exercised via the hit counter, so a + * silent software fallback can't mask a dispatch regression. + * frodokem_test builds every key with the test devId, so keygen, + * encapsulate and decapsulate all reach the callback. */ + int baseline = myCtx.exampleVar; ret = frodokem_test(); + if ((ret == 0) && (myCtx.exampleVar == baseline)) + ret = WC_TEST_RET_ENC_NC; + myCtx.exampleVar = baseline; + } +#if defined(WOLF_CRYPTO_CB_FIND) && !defined(WOLFSSL_SWDEV) && \ + !defined(WC_NO_RNG) && !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) && \ + defined(FRODOKEM_TEST_TYPE) + /* A find-callback build must reach the device for a key that carries no + * device id of its own: that is the whole point of the find callback, and + * the dispatch guard ignores the key's device id there. */ + if (ret == 0) { + FrodoKemKey* key = (FrodoKemKey*)XMALLOC(sizeof(*key), HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER); + int baseline = myCtx.exampleVar; + int key_inited = 0; + WC_RNG rng; + int rngInit = 0; + int r; + + if (key == NULL) + ret = WC_TEST_RET_ENC_NC; + if (ret == 0) { + r = wc_InitRng_ex(&rng, HEAP_HINT, INVALID_DEVID); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else + rngInit = 1; + } + if (ret == 0) { + r = wc_FrodoKemKey_Init(key, FRODOKEM_TEST_TYPE, HEAP_HINT, + INVALID_DEVID); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else + key_inited = 1; + } + if (ret == 0) { + /* One resolution: the keygen dispatch. The callback then re-enters + * key generation to run it in software, which must not resolve. */ + myCryptoCbFindInvalidLeft = 1; + r = wc_FrodoKemKey_MakeKey(key, &rng); + myCryptoCbFindInvalidLeft = 0; + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else if (myCtx.exampleVar == baseline) + ret = WC_TEST_RET_ENC_NC; /* never reached the device */ + } + myCtx.exampleVar = baseline; + if (key_inited) + wc_FrodoKemKey_Free(key); + if (rngInit) + wc_FreeRng(&rng); + XFREE(key, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + } +#endif #endif #ifdef WOLFSSL_HAVE_MLDSA if (ret == 0) diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 2bfbb4ddef4..b82ee40bcd7 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -5901,6 +5901,12 @@ blinding by defining WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS." #error "WOLF_CRYPTO_CB_ONLY_CURVE25519 is incompatible with " \ "WOLFSSL_ASYNC_CRYPT" #endif +#if defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) && !defined(WOLF_CRYPTO_CB) + #error "WOLF_CRYPTO_CB_ONLY_FRODOKEM requires WOLF_CRYPTO_CB" +#endif +#if defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) && !defined(WOLFSSL_HAVE_FRODOKEM) + #error "WOLF_CRYPTO_CB_ONLY_FRODOKEM requires WOLFSSL_HAVE_FRODOKEM" +#endif /* Early Data / Session Rules */ #if !defined(WOLFCRYPT_ONLY) && defined(WOLFSSL_EARLY_DATA) && \ diff --git a/wolfssl/wolfcrypt/wc_frodokem.h b/wolfssl/wolfcrypt/wc_frodokem.h index f1ec2198777..f9a0faf987a 100644 --- a/wolfssl/wolfcrypt/wc_frodokem.h +++ b/wolfssl/wolfcrypt/wc_frodokem.h @@ -255,6 +255,10 @@ typedef struct FrodoKemKey { void* heap; /* Device Id. */ int devId; +#ifdef WOLF_CRYPTO_CB + /* Device context for a hardware key handle. */ + void* devCtx; +#endif /* Flags indicating what is stored in the key. */ int flags; @@ -347,6 +351,18 @@ WOLFSSL_API int wc_FrodoKemKey_PrivateKeyDecode(FrodoKemKey* key, } /* extern "C" */ #endif +/* Native implementation core (internal). The public wc_FrodoKemKey_* functions + * in wc_frodokem.c wrap it with cryptocb dispatch and argument checking. With + * WOLF_CRYPTO_CB_ONLY_FRODOKEM the lattice math is not compiled: key + * generation, encapsulation and decapsulation all go through the crypto + * callback. The key encode and decode helpers stay, since a callback that + * returns key material needs them. */ +#ifndef WOLF_CRYPTO_CB_ONLY_FRODOKEM +/* Signals that native key generation, encapsulation and decapsulation are + * available. */ +#define WC_FRODOKEM_HAVE_NATIVE +#endif + #endif /* WOLFSSL_HAVE_FRODOKEM */ #endif /* WOLF_CRYPT_WC_FRODOKEM_H */