From 820885da43809d62318b6bad47e87865d6afec6c Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Thu, 10 Sep 2026 15:20:21 -0600 Subject: [PATCH 01/20] sha3: bracket the block permute and gate non-approved services --- wolfcrypt/src/sha3.c | 61 ++++++++++++++++++++++++++++++++++--------- wolfcrypt/test/test.c | 7 +++-- 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index e7db61ad48c..8ea3b17dadf 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -53,6 +53,9 @@ #undef USE_INTEL_SPEEDUP #undef WOLFSSL_ARMASM #undef WOLFSSL_RISCV_ASM + #undef WOLFSSL_PPC64_ASM + #undef WOLFSSL_PPC64_ASM_POWER8 + #undef WOLFSSL_PPC32_ASM #endif #ifdef WOLFSSL_X86_BUILD #undef USE_INTEL_SPEEDUP @@ -142,10 +145,18 @@ * WOLFSSL_SHA3_NO_AVX2 never uses AVX2. */ /* SHA3_USE_AVX2() is defined in sha3.h - shared with ML-DSA. */ + /* True only when AVX2 was explicitly asked for; it then wins over BMI2, + * which is otherwise tried first (see the selection order below). */ +#if !defined(WOLFSSL_SHA3_NO_AVX2) && defined(WOLFSSL_SHA3_AVX2) + #define SHA3_FORCE_AVX2(f) IS_INTEL_AVX2(f) +#else + #define SHA3_FORCE_AVX2(f) 0 +#endif + /* True when the selected block function uses vector registers and so * needs the caller to save/restore them. BMI2 and the C block use only * general registers. */ -#ifdef WOLFSSL_SHA3_NO_AVX2 +#if defined(WOLFSSL_SHA3_NO_AVX2) #define SHA3_BLOCK_VREGS(f) 0 #else #define SHA3_BLOCK_VREGS(f) ((f) == sha3_block_avx2) @@ -160,6 +171,14 @@ #define SHA3_NEEDS_VREG_CLAIM #endif +#if defined(WOLFSSL_ARMASM) && !defined(__aarch64__) && \ + !defined(WOLFSSL_ARMASM_THUMB2) && !defined(WC_SHA3_NO_ASM) + /* The one arm32 block is NEON (port/arm/armv8-32-sha3-asm.S, d0-d15), so + * it always needs the registers; Thumb2's block is integer-only. */ + #define SHA3_BLOCK_VREGS(f) 1 + #define SHA3_NEEDS_VREG_CLAIM +#endif + #if !defined(WOLFSSL_ARMASM) && !defined(WOLFSSL_RISCV_ASM) && \ !defined(WOLFSSL_PPC64_ASM) && !defined(WOLFSSL_PPC32_ASM) @@ -910,7 +929,7 @@ static int InitSha3(wc_Sha3* sha3) sha3->hashType = WC_HASH_TYPE_NONE; #endif -#ifdef USE_INTEL_SPEEDUP +#if defined(USE_INTEL_SPEEDUP) { int cpuid_flags_were_updated = cpuid_get_flags_ex(&cpuid_flags); #ifdef WC_C_DYNAMIC_FALLBACK @@ -925,8 +944,9 @@ static int InitSha3(wc_Sha3* sha3) } else #endif - /* See the selection comment above: AVX2 on Intel, otherwise BMI2. */ - if (SHA3_USE_AVX2(cpuid_flags)) { + /* BMI2 first: measured 1.25x AVX2 here, and it uses only general + * registers, so in-kernel it needs no vector-register save. */ + if (SHA3_FORCE_AVX2(cpuid_flags)) { SHA3_BLOCK = sha3_block_avx2; SHA3_BLOCK_N = sha3_block_n_avx2; } @@ -934,6 +954,10 @@ static int InitSha3(wc_Sha3* sha3) SHA3_BLOCK = sha3_block_bmi2; SHA3_BLOCK_N = sha3_block_n_bmi2; } + else if (SHA3_USE_AVX2(cpuid_flags)) { + SHA3_BLOCK = sha3_block_avx2; + SHA3_BLOCK_N = sha3_block_n_avx2; + } else { SHA3_BLOCK = BlockSha3; SHA3_BLOCK_N = NULL; @@ -1004,7 +1028,7 @@ static int Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, word32 p) if (SHA3_BLOCK_VREGS(sha3_block)) { ret = SAVE_VECTOR_REGISTERS2(); if (ret != 0) { -#ifdef WC_C_DYNAMIC_FALLBACK +#if defined(USE_INTEL_SPEEDUP) && defined(WC_C_DYNAMIC_FALLBACK) sha3_block = BlockSha3; sha3_block_n = NULL; ret = 0; @@ -1180,7 +1204,9 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l #if !defined(BIG_ENDIAN_ORDER) && !defined(WC_SHA3_FAULT_HARDEN) && \ !defined(WOLFSSL_WIDE_BYTE) xorbuf(sha3->s, sha3->t, sha3->i); -#ifdef WOLFSSL_HASH_FLAGS + /* SHA3-256 emits the FIPS 202 0x06 pad; the non-approved legacy + * Keccak-256 0x01 pad is excluded from the FIPS module (FIPS 202 6.1). */ +#if defined(WOLFSSL_HASH_FLAGS) && !FIPS_VERSION3_GE(7,0,0) if ((p == WC_SHA3_256_COUNT) && (sha3->flags & WC_HASH_SHA3_KECCAK256)) { padChar = 0x01; } @@ -1189,7 +1215,9 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l ((byte*)sha3->s)[rate - 1] ^= 0x80; #else sha3->t[rate - 1] = 0x00; -#ifdef WOLFSSL_HASH_FLAGS + /* SHA3-256 emits the FIPS 202 0x06 pad; the non-approved legacy + * Keccak-256 0x01 pad is excluded from the FIPS module (FIPS 202 6.1). */ +#if defined(WOLFSSL_HASH_FLAGS) && !FIPS_VERSION3_GE(7,0,0) if ((p == WC_SHA3_256_COUNT) && (sha3->flags & WC_HASH_SHA3_KECCAK256)) { padChar = 0x01; } @@ -1216,7 +1244,7 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l if (SHA3_BLOCK_VREGS(sha3_block)) { int ret = SAVE_VECTOR_REGISTERS2(); if (ret != 0) { -#ifdef WC_C_DYNAMIC_FALLBACK +#if defined(USE_INTEL_SPEEDUP) && defined(WC_C_DYNAMIC_FALLBACK) sha3_block = BlockSha3; #else return ret; @@ -2339,14 +2367,14 @@ int wc_Shake128_SqueezeBlocks(wc_Shake* shake, byte* out, word32 blockCnt) } #if defined(USE_INTEL_SPEEDUP) || defined(SHA3_NEEDS_VREG_CLAIM) -#ifdef WC_C_DYNAMIC_FALLBACK +#if defined(USE_INTEL_SPEEDUP) && defined(WC_C_DYNAMIC_FALLBACK) sha3_block = SHA3_BLOCK; #endif if (SHA3_BLOCK_VREGS(sha3_block)) { int ret = SAVE_VECTOR_REGISTERS2(); if (ret != 0) { -#ifdef WC_C_DYNAMIC_FALLBACK +#if defined(USE_INTEL_SPEEDUP) && defined(WC_C_DYNAMIC_FALLBACK) sha3_block = BlockSha3; #else return ret; @@ -2656,14 +2684,14 @@ int wc_Shake256_SqueezeBlocks(wc_Shake* shake, byte* out, word32 blockCnt) } #if defined(USE_INTEL_SPEEDUP) || defined(SHA3_NEEDS_VREG_CLAIM) -#ifdef WC_C_DYNAMIC_FALLBACK +#if defined(USE_INTEL_SPEEDUP) && defined(WC_C_DYNAMIC_FALLBACK) sha3_block = SHA3_BLOCK; #endif if (SHA3_BLOCK_VREGS(sha3_block)) { int ret = SAVE_VECTOR_REGISTERS2(); if (ret != 0) { -#ifdef WC_C_DYNAMIC_FALLBACK +#if defined(USE_INTEL_SPEEDUP) && defined(WC_C_DYNAMIC_FALLBACK) sha3_block = BlockSha3; #else return ret; @@ -2733,6 +2761,15 @@ int wc_Shake256_Copy(wc_Shake* src, wc_Shake* dst) #if (defined(WOLFSSL_KMAC) || defined(WOLFSSL_CSHAKE)) && \ defined(WC_SHA3_SW_KECCAK) + +#if FIPS_VERSION3_GE(7,0,0) && \ + !defined(WOLFSSL_FIPS_DEV) && !defined(WOLFSSL_FIPS_READY) + /* KMAC and cSHAKE (SP 800-185) have no CAST and no service-layer gate, so + * they are not approved services and stay out of the validated module. + * The dev and ready prep builds still exercise them. */ + #error "KMAC/cSHAKE (SP 800-185) are not part of the FIPS module boundary" +#endif + /* cSHAKE and KMAC - NIST SP 800-185. * * cSHAKE is a customizable SHAKE; KMAC is cSHAKE keyed with the function name diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index c248b5cbf18..09897f08505 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -8009,7 +8009,10 @@ static wc_test_ret_t sha3_256_kat_test(wc_Sha3* sha, wc_Sha3* shaCopy) /* this is a software only variant of SHA3 not supported by external * hardware devices */ -#if defined(WOLFSSL_HASH_FLAGS) && !defined(WOLFSSL_ASYNC_CRYPT) +/* The legacy Keccak-256 pad is not FIPS 202, so a v7 module ignores the flag + * and this vector cannot hold there; sha3.c carries the same condition. */ +#if defined(WOLFSSL_HASH_FLAGS) && !defined(WOLFSSL_ASYNC_CRYPT) && \ + !FIPS_VERSION3_GE(7,0,0) { /* test vector with hash of empty string */ static const char* Keccak256EmptyOut = @@ -8040,7 +8043,7 @@ static wc_test_ret_t sha3_256_kat_test(wc_Sha3* sha, wc_Sha3* shaCopy) keccak_exit: wc_Sha3_256_Free(&ksha); } -#endif /* WOLFSSL_HASH_FLAGS && !WOLFSSL_ASYNC_CRYPT */ +#endif /* WOLFSSL_HASH_FLAGS && !WOLFSSL_ASYNC_CRYPT && !FIPS_VERSION3_GE(7,0,0) */ return ret; } From 9caeb2695a70b30ee1bedbb33ecd739f2c950b8b Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Thu, 10 Sep 2026 15:20:21 -0600 Subject: [PATCH 02/20] fips v7: build the ppc64 and riscv64 AES assembly --- src/include.am | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/include.am b/src/include.am index 9b0e56fce32..da815c8fe31 100644 --- a/src/include.am +++ b/src/include.am @@ -1028,6 +1028,22 @@ endif endif endif +# The FIPS v5 and v6 blocks and the non-FIPS block all list these; v7 did not, +# so aes.c called AES_*_RISCV64 and ppc64_AES_* with nothing to link against +# and --enable-fips=v7 --enable-riscv-asm failed with 55 undefined references. +if BUILD_PPC64_ASM +if BUILD_PPC64_ASM_INLINE +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/ppc64/ppc64-aes-asm_c.c +else +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/ppc64/ppc64-aes-asm.S +endif !BUILD_PPC64_ASM_INLINE +endif BUILD_PPC64_ASM + +if BUILD_RISCV_ASM +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/riscv64/riscv-64-aes-asm.S +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/riscv64/riscv-64-aes-asm_c.c +endif BUILD_RISCV_ASM + if BUILD_SHA src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/sha.c endif From 83233eed779d5ec0b2bd32a64fc3f95bb19192b8 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Thu, 10 Sep 2026 18:18:01 -0600 Subject: [PATCH 03/20] sha3: claim the vector registers before the final absorb --- wolfcrypt/src/sha3.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index 8ea3b17dadf..b590b95804c 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -1201,6 +1201,19 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l if (sha3->i >= rate) return BAD_STATE_E; +#if defined(USE_INTEL_SPEEDUP) || defined(SHA3_NEEDS_VREG_CLAIM) + if (SHA3_BLOCK_VREGS(sha3_block)) { + int ret = SAVE_VECTOR_REGISTERS2(); + if (ret != 0) { +#if defined(USE_INTEL_SPEEDUP) && defined(WC_C_DYNAMIC_FALLBACK) + sha3_block = BlockSha3; +#else + return ret; +#endif + } + } +#endif + #if !defined(BIG_ENDIAN_ORDER) && !defined(WC_SHA3_FAULT_HARDEN) && \ !defined(WOLFSSL_WIDE_BYTE) xorbuf(sha3->s, sha3->t, sha3->i); @@ -1235,22 +1248,14 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l } #ifdef WC_SHA3_FAULT_HARDEN if (check != p) { - return BAD_COND_E; - } -#endif -#endif - #if defined(USE_INTEL_SPEEDUP) || defined(SHA3_NEEDS_VREG_CLAIM) - if (SHA3_BLOCK_VREGS(sha3_block)) { - int ret = SAVE_VECTOR_REGISTERS2(); - if (ret != 0) { -#if defined(USE_INTEL_SPEEDUP) && defined(WC_C_DYNAMIC_FALLBACK) - sha3_block = BlockSha3; -#else - return ret; -#endif + if (SHA3_BLOCK_VREGS(sha3_block)) { + RESTORE_VECTOR_REGISTERS(); } +#endif + return BAD_COND_E; } +#endif #endif for (j = 0; l - j >= rate; j += rate) { From 560ffb1d9459ae0f670822ae0ce85fc7d316f2bd Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Fri, 11 Sep 2026 15:00:26 -0600 Subject: [PATCH 04/20] sha3: refuse the Keccak-256 flag in a FIPS v7 build --- wolfcrypt/src/sha3.c | 8 ++++++++ wolfcrypt/test/test.c | 25 ++++++++++++++++++++----- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index b590b95804c..f9f4bee9550 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -2111,6 +2111,14 @@ int wc_Sha3_512_Copy(wc_Sha3* src, wc_Sha3* dst) int wc_Sha3_SetFlags(wc_Sha3* sha3, word32 flags) { if (sha3) { + #if FIPS_VERSION3_GE(7,0,0) + /* Keccak-256 is a different hash from SHA3-256, so refuse the request + * instead of accepting it and hashing with the other one + * (FIPS 202 6.1). */ + if ((flags & WC_HASH_SHA3_KECCAK256) != 0) { + return FIPS_NOT_ALLOWED_E; + } + #endif sha3->flags = flags; } return 0; diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 09897f08505..a354c170787 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -8009,10 +8009,24 @@ static wc_test_ret_t sha3_256_kat_test(wc_Sha3* sha, wc_Sha3* shaCopy) /* this is a software only variant of SHA3 not supported by external * hardware devices */ -/* The legacy Keccak-256 pad is not FIPS 202, so a v7 module ignores the flag - * and this vector cannot hold there; sha3.c carries the same condition. */ -#if defined(WOLFSSL_HASH_FLAGS) && !defined(WOLFSSL_ASYNC_CRYPT) && \ - !FIPS_VERSION3_GE(7,0,0) +#if defined(WOLFSSL_HASH_FLAGS) && !defined(WOLFSSL_ASYNC_CRYPT) +#if FIPS_VERSION3_GE(7,0,0) + { + /* Keccak-256 is a different hash from SHA3-256, so the module refuses + * the flag rather than accepting it and hashing with the other one + * (FIPS 202 6.1). */ + wc_Sha3 ksha; + + ret = wc_InitSha3_256(&ksha, HEAP_HINT, devId); + if (ret != 0) + return WC_TEST_RET_ENC_EC(ret); + ret = wc_Sha3_SetFlags(&ksha, WC_HASH_SHA3_KECCAK256); + wc_Sha3_256_Free(&ksha); + if (ret != WC_NO_ERR_TRACE(FIPS_NOT_ALLOWED_E)) + return WC_TEST_RET_ENC_EC(ret); + ret = 0; + } +#else { /* test vector with hash of empty string */ static const char* Keccak256EmptyOut = @@ -8043,7 +8057,8 @@ static wc_test_ret_t sha3_256_kat_test(wc_Sha3* sha, wc_Sha3* shaCopy) keccak_exit: wc_Sha3_256_Free(&ksha); } -#endif /* WOLFSSL_HASH_FLAGS && !WOLFSSL_ASYNC_CRYPT && !FIPS_VERSION3_GE(7,0,0) */ +#endif /* !FIPS_VERSION3_GE(7,0,0) */ +#endif /* WOLFSSL_HASH_FLAGS && !WOLFSSL_ASYNC_CRYPT */ return ret; } From c99ae8fc87f5add3574802ad0bf32f48159dffbd Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Fri, 11 Sep 2026 15:00:32 -0600 Subject: [PATCH 05/20] test: cover the retry after a refused vector-register claim --- wolfcrypt/test/test.c | 59 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index a354c170787..87efbed891a 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -8206,6 +8206,61 @@ static wc_test_ret_t sha3_256_reset_test(wc_Sha3* sha) } #endif +#if defined(DEBUG_VECTOR_REGISTER_ACCESS) && !defined(WC_C_DYNAMIC_FALLBACK) +/* A refused vector-register claim must leave the context usable: the retry has + * to return the same digest, not one built from a half-absorbed state. */ +static wc_test_ret_t sha3_256_claim_retry_test(void) +{ + wc_Sha3 sha; + byte ref[WC_SHA3_256_DIGEST_SIZE]; + byte got[WC_SHA3_256_DIGEST_SIZE]; + wc_test_ret_t ret; + int inited = 0; + + ret = wc_InitSha3_256(&sha, HEAP_HINT, devId); + if (ret != 0) + return WC_TEST_RET_ENC_EC(ret); + inited = 1; + ret = wc_Sha3_256_Update(&sha, (const byte*)"abc", 3); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + ret = wc_Sha3_256_Final(&sha, ref); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + wc_Sha3_256_Free(&sha); + inited = 0; + + ret = wc_InitSha3_256(&sha, HEAP_HINT, devId); + if (ret != 0) + return WC_TEST_RET_ENC_EC(ret); + inited = 1; + ret = wc_Sha3_256_Update(&sha, (const byte*)"abc", 3); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + + wc_debug_vector_registers_retval = WC_ACCEL_INHIBIT_E; + ret = wc_Sha3_256_Final(&sha, got); + wc_debug_vector_registers_retval = 0; + /* Nothing to test when this build selected a block that claims nothing; + * the refusal never happened, so do not report it as covered. */ + if (ret == 0) + goto out; + + ret = wc_Sha3_256_Final(&sha, got); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); + if (XMEMCMP(got, ref, WC_SHA3_256_DIGEST_SIZE) != 0) + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + ret = 0; + +out: + wc_debug_vector_registers_retval = 0; + if (inited) + wc_Sha3_256_Free(&sha); + return ret; +} +#endif /* DEBUG_VECTOR_REGISTER_ACCESS && !WC_C_DYNAMIC_FALLBACK */ + static wc_test_ret_t sha3_256_test(void) { wc_Sha3 sha; @@ -8232,6 +8287,10 @@ static wc_test_ret_t sha3_256_test(void) !defined(WOLFSSL_XILINX_CRYPT) && !defined(WOLFSSL_AFALG_XILINX_SHA3) if ((ret = sha3_256_reset_test(&sha)) != 0) goto out; +#endif +#if defined(DEBUG_VECTOR_REGISTER_ACCESS) && !defined(WC_C_DYNAMIC_FALLBACK) + if ((ret = sha3_256_claim_retry_test()) != 0) + goto out; #endif ret = 0; out: From b369889029bbe0731ad7d2f68c1b232de29a14f7 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Sat, 12 Sep 2026 15:23:37 -0600 Subject: [PATCH 06/20] test: require the claim refusal before the SHA-3 retry --- wolfcrypt/test/test.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 87efbed891a..6249fe57684 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -8206,7 +8206,18 @@ static wc_test_ret_t sha3_256_reset_test(wc_Sha3* sha) } #endif -#if defined(DEBUG_VECTOR_REGISTER_ACCESS) && !defined(WC_C_DYNAMIC_FALLBACK) +/* Only where a refusal is certain to be seen: the AVX2 lane pinned, no C + * fallback, and no fuzzer failing claims at random. */ +#if defined(DEBUG_VECTOR_REGISTER_ACCESS) && \ + !defined(DEBUG_VECTOR_REGISTER_ACCESS_FUZZING) && \ + !defined(WC_C_DYNAMIC_FALLBACK) && defined(USE_INTEL_SPEEDUP) && \ + !defined(WOLFSSL_X86_BUILD) && !defined(WC_SHA3_NO_ASM) && \ + defined(WOLFSSL_SHA3_AVX2) && !defined(WOLFSSL_SHA3_NO_AVX2) && \ + defined(__GNUC__) + #define SHA3_256_CLAIM_RETRY_TEST +#endif + +#ifdef SHA3_256_CLAIM_RETRY_TEST /* A refused vector-register claim must leave the context usable: the retry has * to return the same digest, not one built from a half-absorbed state. */ static wc_test_ret_t sha3_256_claim_retry_test(void) @@ -8217,7 +8228,11 @@ static wc_test_ret_t sha3_256_claim_retry_test(void) wc_test_ret_t ret; int inited = 0; - ret = wc_InitSha3_256(&sha, HEAP_HINT, devId); + /* Without AVX2 the pinned lane never runs, so no claim is made to refuse. */ + if (!__builtin_cpu_supports("avx2")) + return 0; + + ret = wc_InitSha3_256(&sha, HEAP_HINT, INVALID_DEVID); if (ret != 0) return WC_TEST_RET_ENC_EC(ret); inited = 1; @@ -8230,7 +8245,7 @@ static wc_test_ret_t sha3_256_claim_retry_test(void) wc_Sha3_256_Free(&sha); inited = 0; - ret = wc_InitSha3_256(&sha, HEAP_HINT, devId); + ret = wc_InitSha3_256(&sha, HEAP_HINT, INVALID_DEVID); if (ret != 0) return WC_TEST_RET_ENC_EC(ret); inited = 1; @@ -8241,10 +8256,10 @@ static wc_test_ret_t sha3_256_claim_retry_test(void) wc_debug_vector_registers_retval = WC_ACCEL_INHIBIT_E; ret = wc_Sha3_256_Final(&sha, got); wc_debug_vector_registers_retval = 0; - /* Nothing to test when this build selected a block that claims nothing; - * the refusal never happened, so do not report it as covered. */ if (ret == 0) - goto out; + ERROR_OUT(WC_TEST_RET_ENC_NC, out); + if (ret != WC_NO_ERR_TRACE(WC_ACCEL_INHIBIT_E)) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); ret = wc_Sha3_256_Final(&sha, got); if (ret != 0) @@ -8259,7 +8274,7 @@ static wc_test_ret_t sha3_256_claim_retry_test(void) wc_Sha3_256_Free(&sha); return ret; } -#endif /* DEBUG_VECTOR_REGISTER_ACCESS && !WC_C_DYNAMIC_FALLBACK */ +#endif /* SHA3_256_CLAIM_RETRY_TEST */ static wc_test_ret_t sha3_256_test(void) { @@ -8288,7 +8303,7 @@ static wc_test_ret_t sha3_256_test(void) if ((ret = sha3_256_reset_test(&sha)) != 0) goto out; #endif -#if defined(DEBUG_VECTOR_REGISTER_ACCESS) && !defined(WC_C_DYNAMIC_FALLBACK) +#ifdef SHA3_256_CLAIM_RETRY_TEST if ((ret = sha3_256_claim_retry_test()) != 0) goto out; #endif From a18674cd129a3ac530dadd53a064ba9142f157d0 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Fri, 25 Sep 2026 09:48:44 -0600 Subject: [PATCH 07/20] sha3: only claim vector registers for the NEON arm32 block --- wolfcrypt/src/sha3.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index f9f4bee9550..8c782afc590 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -172,9 +172,10 @@ #endif #if defined(WOLFSSL_ARMASM) && !defined(__aarch64__) && \ - !defined(WOLFSSL_ARMASM_THUMB2) && !defined(WC_SHA3_NO_ASM) - /* The one arm32 block is NEON (port/arm/armv8-32-sha3-asm.S, d0-d15), so - * it always needs the registers; Thumb2's block is integer-only. */ + !defined(WOLFSSL_ARMASM_THUMB2) && !defined(WC_SHA3_NO_ASM) && \ + !defined(WOLFSSL_ARMASM_NO_NEON) + /* armv8-32-sha3-asm.S has a NEON block (vpush d8-d15) and an integer-only + * one under WOLFSSL_ARMASM_NO_NEON; only the NEON block needs a claim. */ #define SHA3_BLOCK_VREGS(f) 1 #define SHA3_NEEDS_VREG_CLAIM #endif From 8d0f12d47a6bdf3e45f54df5eab7a6aaf1244ad9 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Fri, 25 Sep 2026 10:26:40 -0600 Subject: [PATCH 08/20] settings: drop KMAC and cSHAKE from FIPS v7 module builds --- configure.ac | 10 ++++++++-- wolfssl/wolfcrypt/settings.h | 9 +++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 3aaeee994d5..b629b412cd7 100644 --- a/configure.ac +++ b/configure.ac @@ -1760,8 +1760,14 @@ then if test "$ENABLED_FIPS" = "no" || test "$HAVE_FIPS_VERSION" -ge 7 then test "$enable_eccencrypt" = "" && test "$enable_ecc" != "no" && enable_eccencrypt=yes - test "$enable_cshake" = "" && enable_cshake=yes - test "$enable_kmac" = "" && enable_kmac=yes + # KMAC and cSHAKE (SP 800-185) are outside the FIPS v7 module boundary, + # so only non-FIPS and the dev/ready prep builds turn them on here. + if test "$ENABLED_FIPS" = "no" || test "$ENABLED_FIPS_DEV" = "yes" \ + || test "$ENABLED_FIPS_READY" = "yes" + then + test "$enable_cshake" = "" && enable_cshake=yes + test "$enable_kmac" = "" && enable_kmac=yes + fi fi AM_CFLAGS="$AM_CFLAGS -DHAVE_AES_DECRYPT -DHAVE_AES_ECB -DWOLFSSL_ALT_NAMES" diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 49617909723..31417f43585 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -6098,6 +6098,15 @@ blinding by defining WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS." #error WC_C_DYNAMIC_FALLBACK requires WC_HAVE_VECTOR_SPEEDUPS #endif +/* KMAC and cSHAKE (SP 800-185) are outside the FIPS v7 module boundary, so a + * validated build drops them however they were requested; the dev and ready + * prep builds keep them. */ +#if FIPS_VERSION3_GE(7,0,0) && !defined(WOLFSSL_FIPS_DEV) && \ + !defined(WOLFSSL_FIPS_READY) + #undef WOLFSSL_KMAC + #undef WOLFSSL_CSHAKE +#endif + /* setup for opt-in DH in FIPS v7+ */ #if FIPS_VERSION3_GE(7,0,0) && !defined(HAVE_DH) && !defined(NO_DH) #define NO_DH From b30d11ab76b774d9f946c315764b74caeca7c9c0 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Fri, 25 Sep 2026 10:26:40 -0600 Subject: [PATCH 09/20] sha3: no run-time C block switch in certifiable FIPS builds --- wolfcrypt/src/sha3.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index 8c782afc590..d070a0722a0 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -171,6 +171,14 @@ #define SHA3_NEEDS_VREG_CLAIM #endif +/* A certifiable build carries one Keccak permutation, so a refused claim is an + * error there instead of a switch to the C block; dev builds keep the switch. + * WOLFSSL_FIPS_DEV covers both dev and dev-no-post. */ +#if defined(USE_INTEL_SPEEDUP) && defined(WC_C_DYNAMIC_FALLBACK) && \ + !(FIPS_VERSION3_GE(7,0,0) && !defined(WOLFSSL_FIPS_DEV)) + #define SHA3_CLAIM_FALLBACK +#endif + #if defined(WOLFSSL_ARMASM) && !defined(__aarch64__) && \ !defined(WOLFSSL_ARMASM_THUMB2) && !defined(WC_SHA3_NO_ASM) && \ !defined(WOLFSSL_ARMASM_NO_NEON) @@ -1029,7 +1037,7 @@ static int Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, word32 p) if (SHA3_BLOCK_VREGS(sha3_block)) { ret = SAVE_VECTOR_REGISTERS2(); if (ret != 0) { -#if defined(USE_INTEL_SPEEDUP) && defined(WC_C_DYNAMIC_FALLBACK) +#ifdef SHA3_CLAIM_FALLBACK sha3_block = BlockSha3; sha3_block_n = NULL; ret = 0; @@ -1206,7 +1214,7 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l if (SHA3_BLOCK_VREGS(sha3_block)) { int ret = SAVE_VECTOR_REGISTERS2(); if (ret != 0) { -#if defined(USE_INTEL_SPEEDUP) && defined(WC_C_DYNAMIC_FALLBACK) +#ifdef SHA3_CLAIM_FALLBACK sha3_block = BlockSha3; #else return ret; @@ -2388,7 +2396,7 @@ int wc_Shake128_SqueezeBlocks(wc_Shake* shake, byte* out, word32 blockCnt) if (SHA3_BLOCK_VREGS(sha3_block)) { int ret = SAVE_VECTOR_REGISTERS2(); if (ret != 0) { -#if defined(USE_INTEL_SPEEDUP) && defined(WC_C_DYNAMIC_FALLBACK) +#ifdef SHA3_CLAIM_FALLBACK sha3_block = BlockSha3; #else return ret; @@ -2705,7 +2713,7 @@ int wc_Shake256_SqueezeBlocks(wc_Shake* shake, byte* out, word32 blockCnt) if (SHA3_BLOCK_VREGS(sha3_block)) { int ret = SAVE_VECTOR_REGISTERS2(); if (ret != 0) { -#if defined(USE_INTEL_SPEEDUP) && defined(WC_C_DYNAMIC_FALLBACK) +#ifdef SHA3_CLAIM_FALLBACK sha3_block = BlockSha3; #else return ret; From 758321fd089d01442404c0260a96a2d1e850c916 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Fri, 25 Sep 2026 12:00:25 -0600 Subject: [PATCH 10/20] configure: refuse explicit kmac and cshake for a validated module --- configure.ac | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/configure.ac b/configure.ac index b629b412cd7..25202d58af2 100644 --- a/configure.ac +++ b/configure.ac @@ -8497,6 +8497,19 @@ fi # clear configure error rather than a confusing compile-time #error. KMAC # implies cSHAKE (ENABLED_CSHAKE was set above), so the SHAKE check on the # cSHAKE branch also covers KMAC. +# An explicit --enable-kmac/--enable-cshake against a validated module version +# fails here, with the clear configure error this file's convention calls for, +# rather than building a module that silently excludes them (SP 800-185). +if test "$ENABLED_FIPS" != "no" && test -n "$HAVE_FIPS_VERSION" \ + && test "$HAVE_FIPS_VERSION" -ge 7 && test "$ENABLED_FIPS_DEV" != "yes" \ + && test "$ENABLED_FIPS_READY" != "yes" +then + if test "$ENABLED_CSHAKE" != "no" || test "$ENABLED_KMAC" != "no" + then + AC_MSG_ERROR([cshake and kmac are not part of the FIPS module boundary for this version]) + fi +fi + if test "$ENABLED_CSHAKE" != "no" then if test "$ENABLED_SHAKE128" = "no" && test "$ENABLED_SHAKE256" = "no" From 311871cfa625a823a316693f592a977492717646 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Fri, 25 Sep 2026 12:02:08 -0600 Subject: [PATCH 11/20] sha3: address review findings in block selection and SetFlags --- wolfcrypt/src/sha3.c | 53 ++++++++++++++++++---------------------- wolfssl/wolfcrypt/sha3.h | 2 ++ 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index d070a0722a0..8477b8a652a 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -134,24 +134,16 @@ #endif #ifdef USE_INTEL_SPEEDUP - /* Block-function selection when USE_INTEL_SPEEDUP: AVX2 on Intel, else - * BMI2, else the C block. Measured single-instance Keccak-f[1600] - * (Ethereum "Optimizing Keccak"; OpenSSL keccak1600-x86_64.pl): AVX2 is - * ~13-17% faster than BMI2 on Intel Haswell..Skylake, tied on Ice Lake, - * but ~2x SLOWER on AMD Zen, so AVX2 is Intel-only. (Single-stream + /* Block-function selection when USE_INTEL_SPEEDUP: BMI2, then AVX2, then + * the C block. BMI2 is preferred because its block uses general + * registers only and so needs no vector-register claim; AVX2 goes first + * only when WOLFSSL_SHA3_AVX2 explicitly asks for it. (Single-stream * AVX-512 is vpermt2q-bound and slower than BMI2 everywhere measured, so * it is not built - see scripts sha3_avx512.rb.) - * Overrides: WOLFSSL_SHA3_AVX2 forces AVX2 on any vendor with it; + * Overrides: WOLFSSL_SHA3_AVX2 puts AVX2 ahead of BMI2; * WOLFSSL_SHA3_NO_AVX2 never uses AVX2. */ - /* SHA3_USE_AVX2() is defined in sha3.h - shared with ML-DSA. */ - - /* True only when AVX2 was explicitly asked for; it then wins over BMI2, - * which is otherwise tried first (see the selection order below). */ -#if !defined(WOLFSSL_SHA3_NO_AVX2) && defined(WOLFSSL_SHA3_AVX2) - #define SHA3_FORCE_AVX2(f) IS_INTEL_AVX2(f) -#else - #define SHA3_FORCE_AVX2(f) 0 -#endif + /* SHA3_USE_AVX2() is defined in sha3.h - shared with ML-DSA, which still + * selects AVX2 first; only SHA-3's own order changed here. */ /* True when the selected block function uses vector registers and so * needs the caller to save/restore them. BMI2 and the C block use only @@ -180,8 +172,7 @@ #endif #if defined(WOLFSSL_ARMASM) && !defined(__aarch64__) && \ - !defined(WOLFSSL_ARMASM_THUMB2) && !defined(WC_SHA3_NO_ASM) && \ - !defined(WOLFSSL_ARMASM_NO_NEON) + !defined(WOLFSSL_ARMASM_THUMB2) && !defined(WOLFSSL_ARMASM_NO_NEON) /* armv8-32-sha3-asm.S has a NEON block (vpush d8-d15) and an integer-only * one under WOLFSSL_ARMASM_NO_NEON; only the NEON block needs a claim. */ #define SHA3_BLOCK_VREGS(f) 1 @@ -953,13 +944,17 @@ static int InitSha3(wc_Sha3* sha3) } else #endif - /* BMI2 first: measured 1.25x AVX2 here, and it uses only general - * registers, so in-kernel it needs no vector-register save. */ - if (SHA3_FORCE_AVX2(cpuid_flags)) { +#if defined(WOLFSSL_SHA3_AVX2) && !defined(WOLFSSL_SHA3_NO_AVX2) + /* WOLFSSL_SHA3_AVX2 asks for AVX2 ahead of BMI2. */ + if (SHA3_USE_AVX2(cpuid_flags)) { SHA3_BLOCK = sha3_block_avx2; SHA3_BLOCK_N = sha3_block_n_avx2; } - else if (IS_INTEL_BMI1(cpuid_flags) && IS_INTEL_BMI2(cpuid_flags)) { + else +#endif + /* BMI2 before AVX2: sha3_block_bmi2 uses general registers only, so + * it needs no vector-register claim. */ + if (IS_INTEL_BMI1(cpuid_flags) && IS_INTEL_BMI2(cpuid_flags)) { SHA3_BLOCK = sha3_block_bmi2; SHA3_BLOCK_N = sha3_block_n_bmi2; } @@ -2119,15 +2114,15 @@ int wc_Sha3_512_Copy(wc_Sha3* src, wc_Sha3* dst) #ifdef WOLFSSL_HASH_FLAGS int wc_Sha3_SetFlags(wc_Sha3* sha3, word32 flags) { +#if FIPS_VERSION3_GE(7,0,0) + /* Keccak-256 is a different hash from SHA3-256, so refuse the request + * instead of accepting it and hashing with the other one (FIPS 202 6.1). + * Checked first, so the answer does not depend on having a context. */ + if ((flags & WC_HASH_SHA3_KECCAK256) != 0) { + return FIPS_NOT_ALLOWED_E; + } +#endif if (sha3) { - #if FIPS_VERSION3_GE(7,0,0) - /* Keccak-256 is a different hash from SHA3-256, so refuse the request - * instead of accepting it and hashing with the other one - * (FIPS 202 6.1). */ - if ((flags & WC_HASH_SHA3_KECCAK256) != 0) { - return FIPS_NOT_ALLOWED_E; - } - #endif sha3->flags = flags; } return 0; diff --git a/wolfssl/wolfcrypt/sha3.h b/wolfssl/wolfcrypt/sha3.h index 1c59e274990..15a3ae228fa 100644 --- a/wolfssl/wolfcrypt/sha3.h +++ b/wolfssl/wolfcrypt/sha3.h @@ -412,6 +412,8 @@ WOLFSSL_LOCAL void BlockSha3(word64 *s); * than BMI2 everywhere measured, so it is not built.) * Every caller of sha3_block_avx2()/sha3_block_n_avx2() must select with * this and not with IS_INTEL_AVX2() alone. + * sha3.c puts BMI2 ahead of AVX2 because the BMI2 block needs no + * vector-register claim; ML-DSA still selects AVX2 first. * Overrides: WOLFSSL_SHA3_AVX2 forces AVX2 on any vendor with it; * WOLFSSL_SHA3_NO_AVX2 never uses AVX2. */ #if defined(WOLFSSL_SHA3_NO_AVX2) From d6f734a0b9ef9fba138c08d418845497685e7c36 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Fri, 25 Sep 2026 12:02:08 -0600 Subject: [PATCH 12/20] test: use the vector-register debug macro, cover the NULL flag --- wolfcrypt/test/test.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 6249fe57684..deb3ffa1ab2 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -8022,6 +8022,10 @@ static wc_test_ret_t sha3_256_kat_test(wc_Sha3* sha, wc_Sha3* shaCopy) return WC_TEST_RET_ENC_EC(ret); ret = wc_Sha3_SetFlags(&ksha, WC_HASH_SHA3_KECCAK256); wc_Sha3_256_Free(&ksha); + if (ret != WC_NO_ERR_TRACE(FIPS_NOT_ALLOWED_E)) + return WC_TEST_RET_ENC_EC(ret); + /* The refusal must not depend on the caller having a context. */ + ret = wc_Sha3_SetFlags(NULL, WC_HASH_SHA3_KECCAK256); if (ret != WC_NO_ERR_TRACE(FIPS_NOT_ALLOWED_E)) return WC_TEST_RET_ENC_EC(ret); ret = 0; @@ -8253,9 +8257,9 @@ static wc_test_ret_t sha3_256_claim_retry_test(void) if (ret != 0) ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out); - wc_debug_vector_registers_retval = WC_ACCEL_INHIBIT_E; + WC_DEBUG_SET_VECTOR_REGISTERS_RETVAL(WC_NO_ERR_TRACE(WC_ACCEL_INHIBIT_E)); ret = wc_Sha3_256_Final(&sha, got); - wc_debug_vector_registers_retval = 0; + WC_DEBUG_SET_VECTOR_REGISTERS_RETVAL(0); if (ret == 0) ERROR_OUT(WC_TEST_RET_ENC_NC, out); if (ret != WC_NO_ERR_TRACE(WC_ACCEL_INHIBIT_E)) @@ -8269,7 +8273,7 @@ static wc_test_ret_t sha3_256_claim_retry_test(void) ret = 0; out: - wc_debug_vector_registers_retval = 0; + WC_DEBUG_SET_VECTOR_REGISTERS_RETVAL(0); if (inited) wc_Sha3_256_Free(&sha); return ret; From 25fbf63e3553faa4d0647cfedcb5e1ecf3f024f3 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Fri, 25 Sep 2026 12:02:08 -0600 Subject: [PATCH 13/20] docs: refresh stale SHA-3 selection notes in mcdc and include.am --- src/include.am | 4 +++- tests/unit-mcdc/test_sha3_whitebox.c | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/include.am b/src/include.am index da815c8fe31..94ac803a53f 100644 --- a/src/include.am +++ b/src/include.am @@ -1030,7 +1030,9 @@ endif # The FIPS v5 and v6 blocks and the non-FIPS block all list these; v7 did not, # so aes.c called AES_*_RISCV64 and ppc64_AES_* with nothing to link against -# and --enable-fips=v7 --enable-riscv-asm failed with 55 undefined references. +# and --enable-fips=v7 --enable-riscv-asm failed with undefined references to +# the AES asm entry points. Placement follows the v5/v6 FIPS blocks, which +# also sit outside BUILD_AES. if BUILD_PPC64_ASM if BUILD_PPC64_ASM_INLINE src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/ppc64/ppc64-aes-asm_c.c diff --git a/tests/unit-mcdc/test_sha3_whitebox.c b/tests/unit-mcdc/test_sha3_whitebox.c index 5c895d95f95..fb5d1ba6890 100644 --- a/tests/unit-mcdc/test_sha3_whitebox.c +++ b/tests/unit-mcdc/test_sha3_whitebox.c @@ -33,8 +33,11 @@ * Sha3Update multi-block fast path (line ~874): * (sha3_block_n != NULL) && (blocks > 0) * - * On a capable host cpuid reports AVX2, so the runtime always takes the AVX2 - * branch: the "cached", BMI, and non-fast-path conditions are unreachable from + * On a capable host the runtime now takes the BMI2 branch, because sha3.c + * prefers the BMI2 block (it needs no vector-register claim) and only puts + * AVX2 first when WOLFSSL_SHA3_AVX2 is defined, which no configure- or + * CMake-reachable build defines. So the AVX2 selection, the "cached" + * condition and the non-fast-path condition are the ones unreachable from * tests/api. This TU #includes sha3.c so those static items are in scope and drives * InitSha3 / Update with cpuid_flags and the block pointers forced. * From e4b80911d12e5f2b44e8ba207e3fb55e30ce0407 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 30 Sep 2026 13:10:51 -0600 Subject: [PATCH 14/20] sha3: gate the init-time C block switch for certifiable builds --- wolfcrypt/src/sha3.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index 8477b8a652a..9a036aa7fc5 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -934,11 +934,15 @@ static int InitSha3(wc_Sha3* sha3) int cpuid_flags_were_updated = cpuid_get_flags_ex(&cpuid_flags); #ifdef WC_C_DYNAMIC_FALLBACK (void)cpuid_flags_were_updated; +#ifdef SHA3_CLAIM_FALLBACK + /* Same gate as the claim-failure sites: a certifiable build must not + * pick a second permutation, so it leaves the choice to cpuid. */ if (! CAN_SAVE_VECTOR_REGISTERS()) { SHA3_BLOCK = BlockSha3; SHA3_BLOCK_N = NULL; } else +#endif #else if ((! cpuid_flags_were_updated) && (SHA3_BLOCK != NULL)) { } From de5f1112d39760a49055f12c7e8e92634e5ea5b7 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 30 Sep 2026 13:10:51 -0600 Subject: [PATCH 15/20] sha3: compile the trailing AVX2 arm only where it can run --- wolfcrypt/src/sha3.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index 9a036aa7fc5..d1dde0dd477 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -962,10 +962,14 @@ static int InitSha3(wc_Sha3* sha3) SHA3_BLOCK = sha3_block_bmi2; SHA3_BLOCK_N = sha3_block_n_bmi2; } +#if !defined(WOLFSSL_SHA3_AVX2) && !defined(WOLFSSL_SHA3_NO_AVX2) + /* AVX2 without BMI2. Only live in the plain build: the overrides + * either select AVX2 above or disable it outright. */ else if (SHA3_USE_AVX2(cpuid_flags)) { SHA3_BLOCK = sha3_block_avx2; SHA3_BLOCK_N = sha3_block_n_avx2; } +#endif else { SHA3_BLOCK = BlockSha3; SHA3_BLOCK_N = NULL; From 0912ac66c5694f00f521e02505ba8fab3fa527e5 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 30 Sep 2026 13:10:51 -0600 Subject: [PATCH 16/20] configure: keep the kmac check from splitting the cshake comment --- configure.ac | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index 25202d58af2..b4d7e41388c 100644 --- a/configure.ac +++ b/configure.ac @@ -8492,11 +8492,6 @@ else AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_NO_SHAKE256" fi -# Set cSHAKE / KMAC flags. Both are built on SHAKE, so re-check here (after any -# later logic - e.g. FIPS < 6 - may have force-disabled SHAKE) and fail with a -# clear configure error rather than a confusing compile-time #error. KMAC -# implies cSHAKE (ENABLED_CSHAKE was set above), so the SHAKE check on the -# cSHAKE branch also covers KMAC. # An explicit --enable-kmac/--enable-cshake against a validated module version # fails here, with the clear configure error this file's convention calls for, # rather than building a module that silently excludes them (SP 800-185). @@ -8510,6 +8505,11 @@ then fi fi +# Set cSHAKE / KMAC flags. Both are built on SHAKE, so re-check here (after any +# later logic - e.g. FIPS < 6 - may have force-disabled SHAKE) and fail with a +# clear configure error rather than a confusing compile-time #error. KMAC +# implies cSHAKE (ENABLED_CSHAKE was set above), so the SHAKE check on the +# cSHAKE branch also covers KMAC. if test "$ENABLED_CSHAKE" != "no" then if test "$ENABLED_SHAKE128" = "no" && test "$ENABLED_SHAKE256" = "no" From 1bbe9ec7112a4fa264f2b308a9465cc76e9c35fb Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 30 Sep 2026 13:19:09 -0600 Subject: [PATCH 17/20] fips v7: build the ppc32 AES assembly too --- src/include.am | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/include.am b/src/include.am index 94ac803a53f..c962d19e44b 100644 --- a/src/include.am +++ b/src/include.am @@ -1029,9 +1029,10 @@ endif endif # The FIPS v5 and v6 blocks and the non-FIPS block all list these; v7 did not, -# so aes.c called AES_*_RISCV64 and ppc64_AES_* with nothing to link against -# and --enable-fips=v7 --enable-riscv-asm failed with undefined references to -# the AES asm entry points. Placement follows the v5/v6 FIPS blocks, which +# so aes.c called AES_*_RISCV64, ppc64_AES_* and ppc32_AES_* with nothing to +# link against, and --enable-fips=v7 with --enable-riscv-asm, --enable-ppc64-asm +# or --enable-ppc32-asm failed with undefined references to the AES asm entry +# points. Placement follows the v5/v6 FIPS blocks, which # also sit outside BUILD_AES. if BUILD_PPC64_ASM if BUILD_PPC64_ASM_INLINE @@ -1041,6 +1042,18 @@ src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/ppc64/ppc64-aes-asm.S endif !BUILD_PPC64_ASM_INLINE endif BUILD_PPC64_ASM +if BUILD_PPC32_ASM +if BUILD_PPC32_ASM_INLINE +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/ppc32/ppc32-aes-asm_c.c +else +if BUILD_PPC32_ASM_INLINE_REG +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/ppc32/ppc32-aes-asm_cr.c +else +src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/ppc32/ppc32-aes-asm.S +endif !BUILD_PPC32_ASM_INLINE_REG +endif !BUILD_PPC32_ASM_INLINE +endif BUILD_PPC32_ASM + if BUILD_RISCV_ASM src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/riscv64/riscv-64-aes-asm.S src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/port/riscv64/riscv-64-aes-asm_c.c From 9865399621e269273174a01260e5de36c3a149cd Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 30 Sep 2026 14:29:51 -0600 Subject: [PATCH 18/20] sha3: keep Keccak-256 in dev builds, refuse it when certifiable --- wolfcrypt/src/sha3.c | 10 +++++----- wolfcrypt/test/test.c | 2 +- wolfssl/wolfcrypt/settings.h | 7 +++++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index d1dde0dd477..0f3f60029ab 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -1230,8 +1230,8 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l !defined(WOLFSSL_WIDE_BYTE) xorbuf(sha3->s, sha3->t, sha3->i); /* SHA3-256 emits the FIPS 202 0x06 pad; the non-approved legacy - * Keccak-256 0x01 pad is excluded from the FIPS module (FIPS 202 6.1). */ -#if defined(WOLFSSL_HASH_FLAGS) && !FIPS_VERSION3_GE(7,0,0) + * Keccak-256 0x01 pad is excluded from a certifiable build (FIPS 202 6.1). */ +#if defined(WOLFSSL_HASH_FLAGS) && !defined(WOLFSSL_NO_KECCAK256) if ((p == WC_SHA3_256_COUNT) && (sha3->flags & WC_HASH_SHA3_KECCAK256)) { padChar = 0x01; } @@ -1241,8 +1241,8 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l #else sha3->t[rate - 1] = 0x00; /* SHA3-256 emits the FIPS 202 0x06 pad; the non-approved legacy - * Keccak-256 0x01 pad is excluded from the FIPS module (FIPS 202 6.1). */ -#if defined(WOLFSSL_HASH_FLAGS) && !FIPS_VERSION3_GE(7,0,0) + * Keccak-256 0x01 pad is excluded from a certifiable build (FIPS 202 6.1). */ +#if defined(WOLFSSL_HASH_FLAGS) && !defined(WOLFSSL_NO_KECCAK256) if ((p == WC_SHA3_256_COUNT) && (sha3->flags & WC_HASH_SHA3_KECCAK256)) { padChar = 0x01; } @@ -2122,7 +2122,7 @@ int wc_Sha3_512_Copy(wc_Sha3* src, wc_Sha3* dst) #ifdef WOLFSSL_HASH_FLAGS int wc_Sha3_SetFlags(wc_Sha3* sha3, word32 flags) { -#if FIPS_VERSION3_GE(7,0,0) +#ifdef WOLFSSL_NO_KECCAK256 /* Keccak-256 is a different hash from SHA3-256, so refuse the request * instead of accepting it and hashing with the other one (FIPS 202 6.1). * Checked first, so the answer does not depend on having a context. */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index deb3ffa1ab2..6ab4032cca0 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -8010,7 +8010,7 @@ static wc_test_ret_t sha3_256_kat_test(wc_Sha3* sha, wc_Sha3* shaCopy) /* this is a software only variant of SHA3 not supported by external * hardware devices */ #if defined(WOLFSSL_HASH_FLAGS) && !defined(WOLFSSL_ASYNC_CRYPT) -#if FIPS_VERSION3_GE(7,0,0) +#ifdef WOLFSSL_NO_KECCAK256 { /* Keccak-256 is a different hash from SHA3-256, so the module refuses * the flag rather than accepting it and hashing with the other one diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 31417f43585..1f9c078a083 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -6098,6 +6098,13 @@ blinding by defining WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS." #error WC_C_DYNAMIC_FALLBACK requires WC_HAVE_VECTOR_SPEEDUPS #endif +/* Keccak-256 uses the legacy 0x01 pad and is not one of the functions FIPS 202 + * specifies, so a certifiable build refuses it. dev and dev-no-post are not + * certifiable and keep it, as they keep the run-time C block switch. */ +#if FIPS_VERSION3_GE(7,0,0) && !defined(WOLFSSL_FIPS_DEV) + #define WOLFSSL_NO_KECCAK256 +#endif + /* KMAC and cSHAKE (SP 800-185) are outside the FIPS v7 module boundary, so a * validated build drops them however they were requested; the dev and ready * prep builds keep them. */ From c7c3f08415f8f3eedada351e515d155efeb569c1 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 30 Sep 2026 14:51:02 -0600 Subject: [PATCH 19/20] sha3: name the C-block switch for what it does, not a claim --- wolfcrypt/src/sha3.c | 29 +++++++++++++++-------------- wolfssl/wolfcrypt/sha3.h | 2 +- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/wolfcrypt/src/sha3.c b/wolfcrypt/src/sha3.c index 0f3f60029ab..96247ce0acb 100644 --- a/wolfcrypt/src/sha3.c +++ b/wolfcrypt/src/sha3.c @@ -136,7 +136,7 @@ #ifdef USE_INTEL_SPEEDUP /* Block-function selection when USE_INTEL_SPEEDUP: BMI2, then AVX2, then * the C block. BMI2 is preferred because its block uses general - * registers only and so needs no vector-register claim; AVX2 goes first + * registers only and so needs no vector-register save; AVX2 goes first * only when WOLFSSL_SHA3_AVX2 explicitly asks for it. (Single-stream * AVX-512 is vpermt2q-bound and slower than BMI2 everywhere measured, so * it is not built - see scripts sha3_avx512.rb.) @@ -163,18 +163,19 @@ #define SHA3_NEEDS_VREG_CLAIM #endif -/* A certifiable build carries one Keccak permutation, so a refused claim is an - * error there instead of a switch to the C block; dev builds keep the switch. - * WOLFSSL_FIPS_DEV covers both dev and dev-no-post. */ +/* Whether a refused SAVE_VECTOR_REGISTERS2() may switch this call to the C + * block. A certifiable build carries one Keccak permutation, so there it is an + * error instead; dev and dev-no-post keep the switch (WOLFSSL_FIPS_DEV covers + * both). */ #if defined(USE_INTEL_SPEEDUP) && defined(WC_C_DYNAMIC_FALLBACK) && \ !(FIPS_VERSION3_GE(7,0,0) && !defined(WOLFSSL_FIPS_DEV)) - #define SHA3_CLAIM_FALLBACK + #define SHA3_MAY_SWITCH_TO_C #endif #if defined(WOLFSSL_ARMASM) && !defined(__aarch64__) && \ !defined(WOLFSSL_ARMASM_THUMB2) && !defined(WOLFSSL_ARMASM_NO_NEON) /* armv8-32-sha3-asm.S has a NEON block (vpush d8-d15) and an integer-only - * one under WOLFSSL_ARMASM_NO_NEON; only the NEON block needs a claim. */ + * one under WOLFSSL_ARMASM_NO_NEON; only the NEON block needs the save. */ #define SHA3_BLOCK_VREGS(f) 1 #define SHA3_NEEDS_VREG_CLAIM #endif @@ -934,9 +935,9 @@ static int InitSha3(wc_Sha3* sha3) int cpuid_flags_were_updated = cpuid_get_flags_ex(&cpuid_flags); #ifdef WC_C_DYNAMIC_FALLBACK (void)cpuid_flags_were_updated; -#ifdef SHA3_CLAIM_FALLBACK - /* Same gate as the claim-failure sites: a certifiable build must not - * pick a second permutation, so it leaves the choice to cpuid. */ +#ifdef SHA3_MAY_SWITCH_TO_C + /* Same gate as the places that handle a refused save: a certifiable + * build must not pick a second permutation, so cpuid alone decides. */ if (! CAN_SAVE_VECTOR_REGISTERS()) { SHA3_BLOCK = BlockSha3; SHA3_BLOCK_N = NULL; @@ -957,7 +958,7 @@ static int InitSha3(wc_Sha3* sha3) else #endif /* BMI2 before AVX2: sha3_block_bmi2 uses general registers only, so - * it needs no vector-register claim. */ + * it needs no vector-register save. */ if (IS_INTEL_BMI1(cpuid_flags) && IS_INTEL_BMI2(cpuid_flags)) { SHA3_BLOCK = sha3_block_bmi2; SHA3_BLOCK_N = sha3_block_n_bmi2; @@ -1040,7 +1041,7 @@ static int Sha3Update(wc_Sha3* sha3, const byte* data, word32 len, word32 p) if (SHA3_BLOCK_VREGS(sha3_block)) { ret = SAVE_VECTOR_REGISTERS2(); if (ret != 0) { -#ifdef SHA3_CLAIM_FALLBACK +#ifdef SHA3_MAY_SWITCH_TO_C sha3_block = BlockSha3; sha3_block_n = NULL; ret = 0; @@ -1217,7 +1218,7 @@ static int Sha3Final(wc_Sha3* sha3, byte padChar, byte* hash, word32 p, word32 l if (SHA3_BLOCK_VREGS(sha3_block)) { int ret = SAVE_VECTOR_REGISTERS2(); if (ret != 0) { -#ifdef SHA3_CLAIM_FALLBACK +#ifdef SHA3_MAY_SWITCH_TO_C sha3_block = BlockSha3; #else return ret; @@ -2399,7 +2400,7 @@ int wc_Shake128_SqueezeBlocks(wc_Shake* shake, byte* out, word32 blockCnt) if (SHA3_BLOCK_VREGS(sha3_block)) { int ret = SAVE_VECTOR_REGISTERS2(); if (ret != 0) { -#ifdef SHA3_CLAIM_FALLBACK +#ifdef SHA3_MAY_SWITCH_TO_C sha3_block = BlockSha3; #else return ret; @@ -2716,7 +2717,7 @@ int wc_Shake256_SqueezeBlocks(wc_Shake* shake, byte* out, word32 blockCnt) if (SHA3_BLOCK_VREGS(sha3_block)) { int ret = SAVE_VECTOR_REGISTERS2(); if (ret != 0) { -#ifdef SHA3_CLAIM_FALLBACK +#ifdef SHA3_MAY_SWITCH_TO_C sha3_block = BlockSha3; #else return ret; diff --git a/wolfssl/wolfcrypt/sha3.h b/wolfssl/wolfcrypt/sha3.h index 15a3ae228fa..6e5e2a912bb 100644 --- a/wolfssl/wolfcrypt/sha3.h +++ b/wolfssl/wolfcrypt/sha3.h @@ -413,7 +413,7 @@ WOLFSSL_LOCAL void BlockSha3(word64 *s); * Every caller of sha3_block_avx2()/sha3_block_n_avx2() must select with * this and not with IS_INTEL_AVX2() alone. * sha3.c puts BMI2 ahead of AVX2 because the BMI2 block needs no - * vector-register claim; ML-DSA still selects AVX2 first. + * vector-register save; ML-DSA still selects AVX2 first. * Overrides: WOLFSSL_SHA3_AVX2 forces AVX2 on any vendor with it; * WOLFSSL_SHA3_NO_AVX2 never uses AVX2. */ #if defined(WOLFSSL_SHA3_NO_AVX2) From baa31e3a767dc10417526d89ee90f6b515422792 Mon Sep 17 00:00:00 2001 From: kaleb-himes Date: Wed, 30 Sep 2026 15:22:03 -0600 Subject: [PATCH 20/20] test: rename the refused-save test for what it checks --- wolfcrypt/test/test.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 6ab4032cca0..fc9842bb186 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -8210,21 +8210,21 @@ static wc_test_ret_t sha3_256_reset_test(wc_Sha3* sha) } #endif -/* Only where a refusal is certain to be seen: the AVX2 lane pinned, no C - * fallback, and no fuzzer failing claims at random. */ +/* Only where a refusal is certain to be seen: the AVX2 lane pinned, no switch + * to the C block, and no fuzzer refusing saves at random. */ #if defined(DEBUG_VECTOR_REGISTER_ACCESS) && \ !defined(DEBUG_VECTOR_REGISTER_ACCESS_FUZZING) && \ !defined(WC_C_DYNAMIC_FALLBACK) && defined(USE_INTEL_SPEEDUP) && \ !defined(WOLFSSL_X86_BUILD) && !defined(WC_SHA3_NO_ASM) && \ defined(WOLFSSL_SHA3_AVX2) && !defined(WOLFSSL_SHA3_NO_AVX2) && \ defined(__GNUC__) - #define SHA3_256_CLAIM_RETRY_TEST + #define SHA3_256_NO_SWITCH_TO_C #endif -#ifdef SHA3_256_CLAIM_RETRY_TEST -/* A refused vector-register claim must leave the context usable: the retry has +#ifdef SHA3_256_NO_SWITCH_TO_C +/* A refused vector-register save must leave the context usable: the retry has * to return the same digest, not one built from a half-absorbed state. */ -static wc_test_ret_t sha3_256_claim_retry_test(void) +static wc_test_ret_t sha3_256_no_switch_to_c_test(void) { wc_Sha3 sha; byte ref[WC_SHA3_256_DIGEST_SIZE]; @@ -8232,7 +8232,7 @@ static wc_test_ret_t sha3_256_claim_retry_test(void) wc_test_ret_t ret; int inited = 0; - /* Without AVX2 the pinned lane never runs, so no claim is made to refuse. */ + /* Without AVX2 the pinned lane never runs, so there is no save to refuse. */ if (!__builtin_cpu_supports("avx2")) return 0; @@ -8278,7 +8278,7 @@ static wc_test_ret_t sha3_256_claim_retry_test(void) wc_Sha3_256_Free(&sha); return ret; } -#endif /* SHA3_256_CLAIM_RETRY_TEST */ +#endif /* SHA3_256_NO_SWITCH_TO_C */ static wc_test_ret_t sha3_256_test(void) { @@ -8307,8 +8307,8 @@ static wc_test_ret_t sha3_256_test(void) if ((ret = sha3_256_reset_test(&sha)) != 0) goto out; #endif -#ifdef SHA3_256_CLAIM_RETRY_TEST - if ((ret = sha3_256_claim_retry_test()) != 0) +#ifdef SHA3_256_NO_SWITCH_TO_C + if ((ret = sha3_256_no_switch_to_c_test()) != 0) goto out; #endif ret = 0;