diff --git a/.github/workflows/cryptocb-only.yml b/.github/workflows/cryptocb-only.yml index c31f875d76f..cc0ec00d174 100644 --- a/.github/workflows/cryptocb-only.yml +++ b/.github/workflows/cryptocb-only.yml @@ -151,12 +151,15 @@ jobs: {"name": "falcon-onlycb-no-swdev", "minutes": 1.0, "comment": "WOLF_CRYPTO_CB_ONLY_FALCON without swdev, which has no Falcon handlers: builds the Falcon key API that a callback-only build keeps, including its TLS and ASN callers, and runs the tests that need no device.", "configure": ["--disable-swdev", "--enable-falcon", "--enable-experimental", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_FALCON"]}, + {"name": "frodokem", "minutes": 4.0, + "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": "shake-xof", "minutes": 4.0, "comment": "WOLF_CRYPTO_CB_SHAKE_XOF: swdev handles SHAKE absorb and squeeze. No ONLY_* strip exists for SHAKE, so software SHAKE stays in. ML-KEM and ML-DSA reach swdev_shake through WOLF_CRYPTO_CB_FIND, and cryptocb_test runs shake_cb_xof_test.", "configure": ["CPPFLAGS=-DWOLF_CRYPTO_CB_SHAKE_XOF"]}, - {"name": "all", "minutes": 19, - "comment": "All nine 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": ["--enable-slhdsa=yes,sha2", "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 -DWOLF_CRYPTO_CB_ONLY_CURVE448 -DWOLF_CRYPTO_CB_ONLY_SLHDSA"]}, + {"name": "all", "minutes": 20, + "comment": "All ten 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": ["--enable-slhdsa=yes,sha2", "--enable-experimental", "--enable-frodokem", "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 -DWOLF_CRYPTO_CB_ONLY_CURVE448 -DWOLF_CRYPTO_CB_ONLY_SLHDSA -DWOLF_CRYPTO_CB_ONLY_FRODOKEM"]}, {"name": "only", "minutes": 4.0, "comment": "Same coverage as the \"all\" entry above, but driven by ./configure --enable-cryptocb=only instead of a hand-written CPPFLAGS list. This is the regression test for the configure option: it must emit exactly the WOLF_CRYPTO_CB_ONLY_* set that \"all\" passes by hand, for the algorithms this base enables. The \"all\" entry deliberately stays on explicit CPPFLAGS so a bug in the configure logic cannot silently weaken both. Note the base already passes --enable-cryptocb; this entry's flags are appended after the base, so --enable-cryptocb=only wins.", "configure": ["--enable-cryptocb=only"]}, diff --git a/configure.ac b/configure.ac index ffb92d5e842..4468d7e4dd3 100644 --- a/configure.ac +++ b/configure.ac @@ -12118,6 +12118,9 @@ then if test "$ENABLED_SLHDSA" != "no"; then AM_CFLAGS="$AM_CFLAGS -DWOLF_CRYPTO_CB_ONLY_SLHDSA" fi + if test "$ENABLED_FRODOKEM" != "no"; then + AM_CFLAGS="$AM_CFLAGS -DWOLF_CRYPTO_CB_ONLY_FRODOKEM" + fi fi if test "$ENABLED_CRYPTOCB_SW_TEST" = "no" diff --git a/tests/api/test_frodokem.c b/tests/api/test_frodokem.c index c67eeefee82..c089c329f82 100644 --- a/tests/api/test_frodokem.c +++ b/tests/api/test_frodokem.c @@ -101,7 +101,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 @@ -756,6 +757,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; @@ -802,6 +804,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; @@ -851,6 +854,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) @@ -916,6 +920,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) @@ -970,6 +975,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) @@ -1062,6 +1068,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) @@ -1149,6 +1156,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) @@ -1217,6 +1225,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; @@ -1440,6 +1449,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) @@ -1580,6 +1590,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) && \ @@ -1769,6 +1780,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) && \ @@ -1868,6 +1880,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) && \ @@ -2054,6 +2067,7 @@ int test_wc_frodokem_cert_verify(void) } #if defined(WOLFSSL_HAVE_FRODOKEM) && defined(WOLF_CRYPTO_CB) && \ + !defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) && \ !defined(WC_NO_RNG) && !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) && \ !defined(WOLFSSL_FRODOKEM_NO_ENCAPSULATE) && \ !defined(WOLFSSL_FRODOKEM_NO_DECAPSULATE) diff --git a/wolfcrypt/src/cryptocb.c b/wolfcrypt/src/cryptocb.c index d706d8a608f..db9b1b278fa 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 * WOLF_CRYPTO_CB_SHAKE_XOF: Dispatch SHAKE absorb and squeeze default: off * as well as update and final. Off by * default because a callback that predates @@ -2073,8 +2074,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); @@ -2107,8 +2112,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); @@ -2143,8 +2152,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 aba36e6d8bd..1448716a90f 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 @@ -1498,8 +1498,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 @@ -1823,8 +1823,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__ @@ -2215,8 +2215,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__ @@ -2376,7 +2376,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 000f471aecd..15197a7e3a8 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, @@ -1312,7 +1312,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 57bc8e1aaad..2c99b08dbec 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) @@ -592,7 +592,7 @@ WC_OMIT_FRAME_POINTER void frodokem_mul_add_sb_plus_e_thumb2(word16* out, #endif /* WOLFSSL_NO_VAR_ASSIGN_REG */ } -#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 6f1a8f4b712..e4405536a26 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)); @@ -428,6 +442,11 @@ int wc_FrodoKemKey_Free(FrodoKemKey* key) wc_AesFree(&key->aes); #endif key->flags = 0; +#ifdef WOLF_CRYPTO_CB + /* Clear device ownership so a subsequent free has nothing to do. */ + key->devCtx = NULL; + key->devId = INVALID_DEVID; +#endif } return ret; @@ -557,6 +576,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 +599,7 @@ static void frodokem_wipe_shake(FrodoKemKey* key) } } } +#endif /* !WOLF_CRYPTO_CB_ONLY_FRODOKEM */ /******************************************************************************/ /* Key generation. */ @@ -605,6 +626,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 +637,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 +652,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 +726,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 +773,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); if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) @@ -822,6 +860,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; @@ -849,6 +888,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; @@ -866,6 +906,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; @@ -965,6 +1013,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; } @@ -1014,8 +1063,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); if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) @@ -1095,6 +1150,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; @@ -1133,7 +1190,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; @@ -1143,9 +1200,6 @@ int wc_FrodoKemKey_Decapsulate(FrodoKemKey* key, unsigned char* ss, if (p == NULL) { ret = NOT_COMPILED_IN; } - else if ((key->flags & FRODOKEM_FLAG_PRIV_SET) == 0) { - ret = BAD_STATE_E; - } else if (len != (word32)p->ctSize) { ret = BUFFER_E; } @@ -1153,8 +1207,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); if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) @@ -1165,6 +1225,21 @@ int wc_FrodoKemKey_Decapsulate(FrodoKemKey* key, unsigned char* ss, } } #endif + + /* Check for a private key to decapsulate with. Done after dispatch for + * cases where the private key lives in a device. */ + if ((ret == 0) && !cbHandled && + ((key->flags & FRODOKEM_FLAG_PRIV_SET) == 0)) { + ret = BAD_STATE_E; + } + +#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; @@ -1299,6 +1374,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 260b6e94d22..107adfd55d1 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -65079,6 +65079,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. */ @@ -65087,7 +65125,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) @@ -65319,6 +65358,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 */ @@ -88676,6 +88909,11 @@ typedef struct { int mldsaSignHashCount; /* ML-DSA pre-hash sign invocations */ int mldsaVerifyHashCount; /* ML-DSA pre-hash verify invocations */ #endif +#ifdef WOLFSSL_HAVE_FRODOKEM + int frodoKgCount; /* FrodoKEM keygen callback invocations */ + int frodoEncapsCount; /* FrodoKEM encapsulate callback invocations */ + int frodoDecapsCount; /* FrodoKEM decapsulate callback invocations */ +#endif } myCryptoDevCtx; #ifdef WOLF_CRYPTO_CB_ONLY_RSA @@ -89561,6 +89799,140 @@ static wc_test_ret_t curve25519_onlycb_test(myCryptoDevCtx *ctx) } #endif /* WOLF_CRYPTO_CB_ONLY_CURVE25519 */ +#if defined(WOLFSSL_HAVE_FRODOKEM) && defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) \ + && defined(FRODOKEM_TEST_TYPE) && !defined(WC_NO_RNG) \ + && !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) \ + && !defined(WOLFSSL_FRODOKEM_NO_ENCAPSULATE) \ + && !defined(WOLFSSL_FRODOKEM_NO_DECAPSULATE) +static int frodokem_buf_is(const byte* p, byte v, word32 len) +{ + word32 i; + + for (i = 0; i < len; i++) { + if (p[i] != v) + return 0; + } + return 1; +} + +/* With software FrodoKEM stripped, every operation must reach the device. + * Drive all three through the stub and check the outputs it produced, then + * confirm a declining device still reports NO_VALID_DEVID. */ +static wc_test_ret_t frodokem_onlycb_test(myCryptoDevCtx* ctx) +{ + wc_test_ret_t ret = 0; + FrodoKemKey* key; + byte* ct; + byte ss[FRODOKEM_MAX_LENSEC]; + byte ss2[FRODOKEM_MAX_LENSEC]; + word32 ctLen = 0; + word32 ssLen = 0; + int kgBase = ctx->frodoKgCount; + int encBase = ctx->frodoEncapsCount; + int decBase = ctx->frodoDecapsCount; + int key_inited = 0; + int rngInit = 0; + WC_RNG rng; + int r; + + /* FrodoKemKey holds maximum-sized matrices; keep it off the stack. */ + 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_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, devId); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else + key_inited = 1; + } + if (ret == 0) { + r = wc_FrodoKemKey_CipherTextSize(key, &ctLen); + 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); + } + + /* cb handles the op, expects 0(success) and the stub's key state */ + if (ret == 0) { + ctx->exampleVar = 99; + r = wc_FrodoKemKey_MakeKey(key, &rng); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else if (ctx->frodoKgCount == kgBase) + ret = WC_TEST_RET_ENC_NC; + else if ((key->flags & FRODOKEM_FLAG_BOTH_SET) != + FRODOKEM_FLAG_BOTH_SET) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + XMEMSET(ct, 0, ctLen); + XMEMSET(ss, 0, ssLen); + r = wc_FrodoKemKey_Encapsulate(key, ct, ss, &rng); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else if (ctx->frodoEncapsCount == encBase) + ret = WC_TEST_RET_ENC_NC; + else if (!frodokem_buf_is(ct, 0xC7, ctLen) || + !frodokem_buf_is(ss, 0x5E, ssLen)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + XMEMSET(ss2, 0, ssLen); + r = wc_FrodoKemKey_Decapsulate(key, ss2, ct, ctLen); + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else if (ctx->frodoDecapsCount == decBase) + ret = WC_TEST_RET_ENC_NC; + else if (XMEMCMP(ss, ss2, ssLen) != 0) + ret = WC_TEST_RET_ENC_NC; + } + + /* cb delegates, expects NO_VALID_DEVID(failure) from every operation */ + if (ret == 0) { + ctx->exampleVar = 1; + r = wc_FrodoKemKey_MakeKey(key, &rng); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + r = wc_FrodoKemKey_Encapsulate(key, ct, ss, &rng); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + r = wc_FrodoKemKey_Decapsulate(key, ss2, ct, ctLen); + if (r != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_NC; + } + ctx->exampleVar = 1; + + if (key_inited) + wc_FrodoKemKey_Free(key); + if (rngInit) + wc_FreeRng(&rng); + XFREE(ct, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(key, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + + return ret; +} +#endif /* FRODOKEM && CB_ONLY_FRODOKEM && TEST_TYPE && !WC_NO_RNG */ + #if defined(WOLF_CRYPTO_CB_ONLY_CURVE448) && !defined(WOLFSSL_SWDEV) /* Is every byte of buf the marker value v? */ static int curve448_buf_is(const byte* buf, byte v, word32 len) @@ -91103,15 +91475,33 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) } #endif #endif /* WOLFSSL_HAVE_MLKEM */ - #ifdef WOLFSSL_HAVE_FRODOKEM + #if defined(WOLFSSL_HAVE_FRODOKEM) + /* exampleVar counts the branches taken so cryptocb_test can confirm + * FrodoKEM really went through the callback. Under CB_ONLY the stub + * cannot delegate to the public API, so exampleVar 99 makes it answer + * with deterministic material and anything else lets the call fall + * through to the API, which has no software left and reports + * NO_VALID_DEVID. */ 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)) { FrodoKemKey* key = (FrodoKemKey*)info->pk.pqc_kem_kg.key; /* set devId to invalid, so software is used */ key->devId = INVALID_DEVID; + #ifdef WOLF_CRYPTO_CB_ONLY_FRODOKEM + if (myCtx->exampleVar == 99) { + key->devId = devIdArg; + if (info->pk.pqc_kem_kg.rng == NULL) + return BAD_FUNC_ARG; + key->flags |= FRODOKEM_FLAG_BOTH_SET; + myCtx->frodoKgCount++; + return 0; + } + #endif ret = wc_FrodoKemKey_MakeKey(key, info->pk.pqc_kem_kg.rng); key->devId = devIdArg; + myCtx->exampleVar++; + myCtx->frodoKgCount++; } } else if (info->pk.type == WC_PK_TYPE_PQC_KEM_ENCAPS) { @@ -91119,11 +91509,26 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) (info->pk.pqc_encaps.key != NULL)) { FrodoKemKey* key = (FrodoKemKey*)info->pk.pqc_encaps.key; key->devId = INVALID_DEVID; + #ifdef WOLF_CRYPTO_CB_ONLY_FRODOKEM + if (myCtx->exampleVar == 99) { + key->devId = devIdArg; + /* deterministic output so the caller can prove the + * callback's result actually reached it */ + XMEMSET(info->pk.pqc_encaps.ciphertext, 0xC7, + info->pk.pqc_encaps.ciphertextLen); + XMEMSET(info->pk.pqc_encaps.sharedSecret, 0x5E, + info->pk.pqc_encaps.sharedSecretLen); + myCtx->frodoEncapsCount++; + return 0; + } + #endif ret = wc_FrodoKemKey_Encapsulate(key, info->pk.pqc_encaps.ciphertext, info->pk.pqc_encaps.sharedSecret, info->pk.pqc_encaps.rng); key->devId = devIdArg; + myCtx->exampleVar++; + myCtx->frodoEncapsCount++; } } else if (info->pk.type == WC_PK_TYPE_PQC_KEM_DECAPS) { @@ -91131,11 +91536,24 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) (info->pk.pqc_decaps.key != NULL)) { FrodoKemKey* key = (FrodoKemKey*)info->pk.pqc_decaps.key; key->devId = INVALID_DEVID; + #ifdef WOLF_CRYPTO_CB_ONLY_FRODOKEM + if (myCtx->exampleVar == 99) { + key->devId = devIdArg; + /* same secret the encapsulate stub produced, so the + * caller's round-trip comparison is meaningful */ + XMEMSET(info->pk.pqc_decaps.sharedSecret, 0x5E, + info->pk.pqc_decaps.sharedSecretLen); + myCtx->frodoDecapsCount++; + return 0; + } + #endif ret = wc_FrodoKemKey_Decapsulate(key, info->pk.pqc_decaps.sharedSecret, info->pk.pqc_decaps.ciphertext, info->pk.pqc_decaps.ciphertextLen); key->devId = devIdArg; + myCtx->exampleVar++; + myCtx->frodoDecapsCount++; } } #endif /* WOLFSSL_HAVE_FRODOKEM */ @@ -93129,6 +93547,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 @@ -93148,6 +93571,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; } @@ -93957,6 +94384,11 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) /* example data for callback */ myCtx.exampleVar = 1; +#ifdef WOLFSSL_HAVE_FRODOKEM + myCtx.frodoKgCount = 0; + myCtx.frodoEncapsCount = 0; + myCtx.frodoDecapsCount = 0; +#endif #ifdef HAVE_ECC myCtx.eccMakePubCount = 0; myCtx.eccCheckPubCount = 0; @@ -94622,9 +95054,139 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) if (ret == 0) ret = mlkem_test(); #endif -#ifdef WOLFSSL_HAVE_FRODOKEM +#if defined(WOLFSSL_HAVE_FRODOKEM) && defined(WOLF_CRYPTO_CB_ONLY_FRODOKEM) \ + && defined(FRODOKEM_TEST_TYPE) && !defined(WC_NO_RNG) \ + && !defined(WOLFSSL_FRODOKEM_NO_MAKE_KEY) \ + && !defined(WOLFSSL_FRODOKEM_NO_ENCAPSULATE) \ + && !defined(WOLFSSL_FRODOKEM_NO_DECAPSULATE) if (ret == 0) + ret = frodokem_onlycb_test(&myCtx); +#endif +#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, so a silent software + * fallback can't mask a dispatch regression. Each operation is counted + * on its own: a shared counter would let keygen alone stand in for a + * broken encapsulate or decapsulate. frodokem_test builds every key + * with the test devId, so all three reach the callback. */ + int baseline = myCtx.exampleVar; ret = frodokem_test(); +#ifndef WOLFSSL_FRODOKEM_NO_MAKE_KEY + if ((ret == 0) && (myCtx.frodoKgCount == 0)) + ret = WC_TEST_RET_ENC_NC; +#endif +#ifndef WOLFSSL_FRODOKEM_NO_ENCAPSULATE + if ((ret == 0) && (myCtx.frodoEncapsCount == 0)) + ret = WC_TEST_RET_ENC_NC; +#endif +#ifndef WOLFSSL_FRODOKEM_NO_DECAPSULATE + if ((ret == 0) && (myCtx.frodoDecapsCount == 0)) + ret = WC_TEST_RET_ENC_NC; +#endif + 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 kgBase = myCtx.frodoKgCount; + 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.frodoKgCount == kgBase) + ret = WC_TEST_RET_ENC_NC; /* never reached the device */ + } +#if !defined(WOLFSSL_FRODOKEM_NO_ENCAPSULATE) && \ + !defined(WOLFSSL_FRODOKEM_NO_DECAPSULATE) + /* Encapsulate and decapsulate resolve through the finder the same way, + * so a lookup regression in either cannot hide behind keygen. */ + if (ret == 0) { + byte* ct = (byte*)XMALLOC(FRODOKEM_MAX_CIPHER_TEXT_SIZE, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER); + byte ss[FRODOKEM_MAX_LENSEC]; + byte ss2[FRODOKEM_MAX_LENSEC]; + word32 ctLen = 0; + word32 ssLen = 0; + int encBase = myCtx.frodoEncapsCount; + int decBase = myCtx.frodoDecapsCount; + + if (ct == NULL) + ret = WC_TEST_RET_ENC_NC; + if (ret == 0) { + r = wc_FrodoKemKey_CipherTextSize(key, &ctLen); + 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) { + myCryptoCbFindInvalidLeft = 1; + r = wc_FrodoKemKey_Encapsulate(key, ct, ss, &rng); + myCryptoCbFindInvalidLeft = 0; + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else if (myCtx.frodoEncapsCount == encBase) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + myCryptoCbFindInvalidLeft = 1; + r = wc_FrodoKemKey_Decapsulate(key, ss2, ct, ctLen); + myCryptoCbFindInvalidLeft = 0; + if (r != 0) + ret = WC_TEST_RET_ENC_EC(r); + else if (myCtx.frodoDecapsCount == decBase) + ret = WC_TEST_RET_ENC_NC; + else if (XMEMCMP(ss, ss2, ssLen) != 0) + ret = WC_TEST_RET_ENC_NC; + } + XFREE(ct, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + } +#endif + 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 85f98891574..f98bdea9aaa 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -6084,6 +6084,12 @@ blinding by defining WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS." #if defined(WOLF_CRYPTO_CB_ONLY_SLHDSA) && !defined(WOLFSSL_HAVE_SLHDSA) #error "WOLF_CRYPTO_CB_ONLY_SLHDSA requires WOLFSSL_HAVE_SLHDSA" #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 */