Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
820885d
sha3: bracket the block permute and gate non-approved services
kaleb-himes Sep 10, 2026
9caeb26
fips v7: build the ppc64 and riscv64 AES assembly
kaleb-himes Sep 10, 2026
83233ee
sha3: claim the vector registers before the final absorb
kaleb-himes Sep 11, 2026
560ffb1
sha3: refuse the Keccak-256 flag in a FIPS v7 build
kaleb-himes Sep 11, 2026
c99ae8f
test: cover the retry after a refused vector-register claim
kaleb-himes Sep 11, 2026
b369889
test: require the claim refusal before the SHA-3 retry
kaleb-himes Sep 12, 2026
a18674c
sha3: only claim vector registers for the NEON arm32 block
kaleb-himes Sep 25, 2026
8d0f12d
settings: drop KMAC and cSHAKE from FIPS v7 module builds
kaleb-himes Sep 25, 2026
b30d11a
sha3: no run-time C block switch in certifiable FIPS builds
kaleb-himes Sep 25, 2026
758321f
configure: refuse explicit kmac and cshake for a validated module
kaleb-himes Sep 25, 2026
311871c
sha3: address review findings in block selection and SetFlags
kaleb-himes Sep 25, 2026
d6f734a
test: use the vector-register debug macro, cover the NULL flag
kaleb-himes Sep 25, 2026
25fbf63
docs: refresh stale SHA-3 selection notes in mcdc and include.am
kaleb-himes Sep 25, 2026
e4b8091
sha3: gate the init-time C block switch for certifiable builds
kaleb-himes Sep 30, 2026
de5f111
sha3: compile the trailing AVX2 arm only where it can run
kaleb-himes Sep 30, 2026
0912ac6
configure: keep the kmac check from splitting the cshake comment
kaleb-himes Sep 30, 2026
1bbe9ec
fips v7: build the ppc32 AES assembly too
kaleb-himes Sep 30, 2026
9865399
sha3: keep Keccak-256 in dev builds, refuse it when certifiable
kaleb-himes Sep 30, 2026
c7c3f08
sha3: name the C-block switch for what it does, not a claim
kaleb-himes Sep 30, 2026
baa31e3
test: rename the refused-save test for what it checks
kaleb-himes Sep 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -8486,6 +8492,19 @@ else
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_NO_SHAKE256"
fi

# 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

# 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
Expand Down
31 changes: 31 additions & 0 deletions src/include.am
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,37 @@ 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, 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
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_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
endif BUILD_RISCV_ASM

if BUILD_SHA
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/sha.c
endif
Expand Down
7 changes: 5 additions & 2 deletions tests/unit-mcdc/test_sha3_whitebox.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
125 changes: 94 additions & 31 deletions wolfcrypt/src/sha3.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -131,21 +134,21 @@
#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 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.)
* 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. */
/* 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
* 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)
Expand All @@ -160,6 +163,23 @@
#define SHA3_NEEDS_VREG_CLAIM
#endif

/* 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_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 the save. */
#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)

Expand Down Expand Up @@ -910,30 +930,47 @@ 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
(void)cpuid_flags_were_updated;
#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;
}
else
#endif
#else
if ((! cpuid_flags_were_updated) && (SHA3_BLOCK != NULL)) {
}
else
#endif
/* See the selection comment above: AVX2 on Intel, otherwise BMI2. */
#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 save. */
if (IS_INTEL_BMI1(cpuid_flags) && IS_INTEL_BMI2(cpuid_flags)) {
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;
Expand Down Expand Up @@ -1004,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 WC_C_DYNAMIC_FALLBACK
#ifdef SHA3_MAY_SWITCH_TO_C
sha3_block = BlockSha3;
sha3_block_n = NULL;
ret = 0;
Expand Down Expand Up @@ -1177,10 +1214,25 @@ 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) {
#ifdef SHA3_MAY_SWITCH_TO_C
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);
#ifdef WOLFSSL_HASH_FLAGS
/* SHA3-256 emits the FIPS 202 0x06 pad; the non-approved legacy
* 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;
}
Expand All @@ -1189,7 +1241,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 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;
}
Expand All @@ -1207,22 +1261,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) {
#ifdef 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) {
Expand Down Expand Up @@ -2077,6 +2123,14 @@ int wc_Sha3_512_Copy(wc_Sha3* src, wc_Sha3* dst)
#ifdef WOLFSSL_HASH_FLAGS
int wc_Sha3_SetFlags(wc_Sha3* sha3, word32 flags)
{
#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. */
if ((flags & WC_HASH_SHA3_KECCAK256) != 0) {
return FIPS_NOT_ALLOWED_E;
}
#endif
if (sha3) {
sha3->flags = flags;
}
Expand Down Expand Up @@ -2339,14 +2393,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();
Comment thread
kaleb-himes marked this conversation as resolved.
if (ret != 0) {
#ifdef WC_C_DYNAMIC_FALLBACK
#ifdef SHA3_MAY_SWITCH_TO_C
sha3_block = BlockSha3;
#else
return ret;
Expand Down Expand Up @@ -2656,14 +2710,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();
Comment thread
kaleb-himes marked this conversation as resolved.
if (ret != 0) {
#ifdef WC_C_DYNAMIC_FALLBACK
#ifdef SHA3_MAY_SWITCH_TO_C
sha3_block = BlockSha3;
#else
return ret;
Expand Down Expand Up @@ -2733,6 +2787,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
Expand Down
Loading
Loading