Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
0a9aeae
sp x86_64: separate lane selection from vector-register ownership
kaleb-himes Sep 6, 2026
b076e1a
unit-mcdc: sp x86_64 whitebox comments follow the fail-closed dispatch
kaleb-himes Sep 6, 2026
0c19693
cpuid: FIPS v7 reads CPU features once at power on, changes are ignored
kaleb-himes Oct 2, 2026
6071663
unit-mcdc: fail the sp x86_64 whitebox if a refused save still runs
kaleb-himes Sep 9, 2026
489853f
unit-mcdc: a build with no instrumented SP call is not a failed check
kaleb-himes Oct 2, 2026
1c70b65
slhdsa: separate lane selection from vector-register ownership
kaleb-himes Sep 11, 2026
59a3b28
mlkem: separate lane selection from vector-register ownership
kaleb-himes Sep 11, 2026
d1c525a
mldsa: separate lane selection from vector-register ownership
kaleb-himes Sep 11, 2026
b504681
configure: allow WC_C_DYNAMIC_FALLBACK only in FIPS dev builds
kaleb-himes Sep 12, 2026
c22e5aa
settings: refuse WC_C_DYNAMIC_FALLBACK in validated FIPS builds
kaleb-himes Sep 12, 2026
47870ef
settings: keep the save fuzzer out of files with no C fallback
kaleb-himes Sep 12, 2026
176e3a4
mldsa: pass the small-mem W0 range check through the save contract
kaleb-himes Sep 13, 2026
f48a5af
mlkem: treat a refused save as a ciphertext mismatch
kaleb-himes Sep 24, 2026
4c6df50
mldsa: say not valid on a refused save, gate the hint count
kaleb-himes Sep 24, 2026
093dadc
slhdsa: clear the seed state and partial signature on refusal
kaleb-himes Sep 24, 2026
b1a3bdf
settings: describe what pins the lane in these files
kaleb-himes Sep 24, 2026
428a4b9
configure: limit the fallback lockout to FIPS v7 builds
kaleb-himes Sep 24, 2026
238328e
unit-mcdc: check the add_points return in the whitebox sweep
kaleb-himes Sep 24, 2026
371fdcf
slhdsa: wipe the slot the failing iteration may have written
kaleb-himes Sep 24, 2026
172d309
mlkem: correct the return docs for paths that can now fail
kaleb-himes Sep 24, 2026
af4bc8a
mldsa: correct the NTT flavor note after the lane pinning
kaleb-himes Sep 24, 2026
e3d7351
slhdsa: fail closed on the AVX512 lanes upstream added
kaleb-himes Oct 1, 2026
d7ee43a
lms: fail closed on the n-way batch lanes
kaleb-himes Oct 1, 2026
3398e4a
xmss: fail closed on the n-way batch lanes
kaleb-himes Oct 1, 2026
bfa8f74
settings: keep the save fuzzer out of the LMS and XMSS lanes
kaleb-himes Oct 1, 2026
11db6e8
mlkem: do not decapsulate after a refused save in decompression
kaleb-himes Oct 1, 2026
47da9b6
slhdsa: wipe the auth-path slot that holds a private key value
kaleb-himes Oct 1, 2026
3cabac8
configure: gate the fallback on the same test settings.h uses
kaleb-himes Oct 1, 2026
627ebcc
tests: w1 encode test checks status instead of pinning a refusal
kaleb-himes Oct 1, 2026
4a97c10
mldsa: stop signing when the small-mem NTT reports an error
kaleb-himes Oct 1, 2026
05f682d
mldsa: keep the verify error instead of the next sampler status
kaleb-himes Oct 1, 2026
9bc8469
linuxkm: pin AES-XTS to the C lane when there is no fallback
kaleb-himes Oct 1, 2026
e287ed4
sp x86_64: base lane saves only when the ct table lookup runs
kaleb-himes Oct 1, 2026
19fe1ce
unit-mcdc: check verify still runs when the base lane needs no save
kaleb-himes Oct 1, 2026
0b9bb0a
linuxkm: stop re-running the CASTs with the registers disabled
kaleb-himes Oct 1, 2026
f219010
linuxkm: correct the hardirq comment about falling back to C
kaleb-himes Oct 1, 2026
80d2ce6
linuxkm: no shim C fallback in validated FIPS v7 builds
kaleb-himes Oct 1, 2026
6252ca2
linuxkm: require the native vector save in FIPS v7 kernel builds
kaleb-himes Oct 1, 2026
5896d12
sp x86_64: define the ct save macros before the per-curve guards
kaleb-himes Oct 1, 2026
8b3a380
unit-mcdc: mldsa whitebox follows the make_hint valid out-parameter
kaleb-himes Oct 1, 2026
e4914cf
mlkem: a failed public key decode leaves the key unusable
kaleb-himes Oct 1, 2026
5314276
unit-mcdc: whitebox comments describe a refused save as an error
kaleb-himes Oct 1, 2026
2ff5efd
mldsa: wrap four status lines to the 80-column convention
kaleb-himes Oct 1, 2026
79a8949
settings: say how to get WC_C_DYNAMIC_FALLBACK in the FIPS v7 error
kaleb-himes Oct 1, 2026
40d9d88
tests: a refused ML-KEM public key decode leaves the key unusable
kaleb-himes Oct 1, 2026
1fc0ee3
mlkem: a decoded key drops the matrix cached from the key it replaces
kaleb-himes Oct 2, 2026
2d0897e
tests: zero the ML-KEM reuse test keys so a failed init frees safely
kaleb-himes Oct 2, 2026
130dc92
mlkem: refuse software decapsulation before decrypting without a publ…
kaleb-himes Oct 3, 2026
121521b
mlkem: drop the old key flags before a decode replaces its buffers
kaleb-himes Oct 4, 2026
1a0cfe2
tests: keep the ML-KEM allocator hooks inside their memory guard
kaleb-himes Oct 4, 2026
c29c247
slhdsa: clear the signature buffer when a sign fails
kaleb-himes Oct 5, 2026
feb1c87
mldsa: clear the signature buffer when a sign fails
kaleb-himes Oct 5, 2026
ae5c820
mlkem: clear the ciphertext when an encapsulation fails
kaleb-himes Oct 5, 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
16 changes: 15 additions & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -5111,7 +5111,13 @@ then
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_AESNI"
if test "$KERNEL_MODE_DEFAULTS" = "yes"
then
AM_CFLAGS="$AM_CFLAGS -DWC_C_DYNAMIC_FALLBACK"
# FIPS v7 and later pin one lane at build time; dev builds may
# still switch. Older validated modules keep what they were
# validated with. Tested numerically, and on the same condition
# settings.h uses, so a new v7.x or lean-* string cannot slip past.
AS_IF([test "${HAVE_FIPS_VERSION_MAJOR:-0}" -ge 7 && \
test "x$ENABLED_FIPS_DEV" != "xyes"],[],
[AM_CFLAGS="$AM_CFLAGS -DWC_C_DYNAMIC_FALLBACK"])
fi
if test "$CC" != "icc"
then
Expand Down Expand Up @@ -14223,6 +14229,14 @@ AM_CFLAGS="$AM_CFLAGS $EXTRA_CFLAGS"
AM_CCASFLAGS="$AM_CCASFLAGS $EXTRA_CCASFLAGS"
AM_LDFLAGS="$AM_LDFLAGS $EXTRA_LDFLAGS"

# FIPS v7 and later never switch lanes at run time, however the define
# arrives. Same condition as the auto-add above and as settings.h.
AS_IF([test "${HAVE_FIPS_VERSION_MAJOR:-0}" -ge 7 && \
test "x$ENABLED_FIPS_DEV" != "xyes"],
[AS_CASE([" $AM_CPPFLAGS $AM_CFLAGS $CPPFLAGS $CFLAGS "],
[*-DWC_C_DYNAMIC_FALLBACK*],
[AC_MSG_ERROR([WC_C_DYNAMIC_FALLBACK is not allowed with --enable-fips=$FIPS_VERSION; use --enable-fips=dev or --enable-fips=dev-no-post])])])

CREATE_HEX_VERSION
AC_SUBST([AM_CPPFLAGS])
AC_SUBST([AM_CFLAGS])
Expand Down
28 changes: 19 additions & 9 deletions linuxkm/lkcapi_aes_glue.c
Original file line number Diff line number Diff line change
Expand Up @@ -2235,8 +2235,14 @@ static int ccmAesAead_rfc4309_loaded = 0;
#error LKCAPI registration of AES-XTS requires WOLFSSL_AESXTS_STREAM (--enable-aesxts-stream).
#endif

#if defined(WOLFSSL_AESNI) && !defined(WC_C_DYNAMIC_FALLBACK) && !defined(WC_DEBUG_FORCE_KERNEL_SETTINGS)
#error LKCAPI registration of AES-XTS with AESNI requires WC_C_DYNAMIC_FALLBACK.
/* AES-XTS asm needs a vector save on every call. Without the fallback the
* whole context is pinned to C at setkey, so no save is ever taken. */
#if defined(WOLFSSL_AESNI) && !defined(WC_C_DYNAMIC_FALLBACK) && \
!defined(WC_DEBUG_FORCE_KERNEL_SETTINGS)
#define WC_LINUXKM_XTS_NO_AESNI
#ifndef WC_FLAG_DONT_USE_VECTOR_OPS
#error AES-XTS without WC_C_DYNAMIC_FALLBACK needs WC_FLAG_DONT_USE_VECTOR_OPS.
#endif
#endif

struct km_AesXtsCtx {
Expand Down Expand Up @@ -2286,6 +2292,15 @@ static int km_AesXtsSetKey(struct crypto_skcipher *tfm, const u8 *in_key,
int err;
struct km_AesXtsCtx * ctx = crypto_skcipher_ctx(tfm);

#ifdef WC_LINUXKM_XTS_NO_AESNI
/* Set before the key schedule is built so the C schedule is the one made. */
ctx->aesXts->aes.use_aesni = WC_FLAG_DONT_USE_VECTOR_OPS;
ctx->aesXts->tweak.use_aesni = WC_FLAG_DONT_USE_VECTOR_OPS;
#ifdef WC_AES_XTS_SUPPORT_SIMULTANEOUS_ENC_AND_DEC_KEYS
ctx->aesXts->aes_decrypt.use_aesni = WC_FLAG_DONT_USE_VECTOR_OPS;
#endif
#endif

err = wc_AesXtsSetKeyNoInit(ctx->aesXts, in_key, key_len,
AES_ENCRYPTION_AND_DECRYPTION);

Expand All @@ -2296,12 +2311,6 @@ static int km_AesXtsSetKey(struct crypto_skcipher *tfm, const u8 *in_key,
return -EINVAL;
}

/* It's possible to set ctx->aesXts->{tweak,aes,aes_decrypt}.use_aesni to
* WC_FLAG_DONT_USE_VECTOR_OPS here, for WC_LINUXKM_C_FALLBACK_IN_SHIMS in
* AES-XTS, but we can use the WC_C_DYNAMIC_FALLBACK mechanism
* unconditionally because there's no AES-XTS in Cert 4718.
*/

#ifdef WOLFKM_DEBUG_AES
pr_info("info: exiting km_AesXtsSetKey: %d\n", key_len);
#endif /* WOLFKM_DEBUG_AES */
Expand All @@ -2321,7 +2330,8 @@ static int km_AesXtsSetKey(struct crypto_skcipher *tfm, const u8 *in_key,
typeof(wc_AesXtsEncryptUpdate_fips) wc_AesXtsEncryptUpdate;
#endif

#if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && !defined(WC_LINUXKM_SVR_NO_BATCHING)
#if defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && \
!defined(WC_LINUXKM_SVR_NO_BATCHING) && !defined(WC_LINUXKM_XTS_NO_AESNI)
#ifndef WC_LINUXKM_XTS_SVR_BATCH
#define WC_LINUXKM_XTS_SVR_BATCH (16 * 4096)
#endif
Expand Down
18 changes: 17 additions & 1 deletion linuxkm/lkcapi_glue.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,13 @@
#define LKCAPI_HAVE_ARCH_ACCEL
#endif

#if defined(LKCAPI_HAVE_ARCH_ACCEL) && \
/* v7 pins one lane per algorithm, so the shims keep no second key schedule.
* Tried and failed to make a refused save reach one: skcipher from hardirq is
* refused (crypto/skcipher.c:449), softirq always has SIMD (fpu/core.c:76). */
#if defined(HAVE_FIPS) && FIPS_VERSION3_GE(7,0,0) && \
!defined(WOLFSSL_FIPS_DEV) && !defined(WOLFSSL_FIPS_DEV_NO_POST)
#undef WC_LINUXKM_C_FALLBACK_IN_SHIMS
#elif defined(LKCAPI_HAVE_ARCH_ACCEL) && \
(!defined(WC_C_DYNAMIC_FALLBACK) || \
(defined(HAVE_FIPS) && FIPS_VERSION3_LT(6,0,0))) && \
!defined(WC_LINUXKM_C_FALLBACK_IN_SHIMS)
Expand All @@ -106,6 +112,16 @@
#undef WC_LINUXKM_C_FALLBACK_IN_SHIMS
#endif

/* With one lane and no fallback, the save has to be available in every context
* the kernel may call us from. The module's own XSAVE/FXSAVE area supplies it
* in hardirq and NMI as well (linuxkm/x86_vector_register_glue.c). */
#if defined(HAVE_FIPS) && FIPS_VERSION3_GE(7,0,0) && \
!defined(WOLFSSL_FIPS_DEV) && !defined(WOLFSSL_FIPS_DEV_NO_POST) && \
defined(CONFIG_X86) && defined(WOLFSSL_USE_SAVE_VECTOR_REGISTERS) && \
!defined(WC_SVR_USE_NATIVE_REG_BUFS)
#error FIPS v7 LKCAPI needs WC_SVR_USE_NATIVE_REG_BUFS: one lane, no fallback.
#endif

#if defined(WC_LINUXKM_C_FALLBACK_IN_SHIMS) && !defined(CAN_SAVE_VECTOR_REGISTERS)
#error WC_LINUXKM_C_FALLBACK_IN_SHIMS is defined but CAN_SAVE_VECTOR_REGISTERS is missing.
#endif
Expand Down
52 changes: 2 additions & 50 deletions linuxkm/module_hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -1262,61 +1262,13 @@ static int wolfssl_init(void)
#ifdef WC_LINUXKM_SVR_DYNAMIC_AUDITING
{
long long unsigned int svr_disallowed_count = wc_svr_disallowed_count_current();
long long unsigned int svr_disallowed_snapshot;
if (svr_disallowed_count > 0) {
pr_err("ERROR: wc_svr_disallowed_count_current() returned %llu after wc_RunAllCast_fips().\n", svr_disallowed_count);
(void)libwolfssl_cleanup();
return -ECANCELED;
}

#ifdef WC_LINUXKM_HAVE_STACK_DEBUG
{
unsigned long stack_usage;
wc_linuxkm_stack_hwm_prepare(0xee);
#endif

ret = DISABLE_VECTOR_REGISTERS();
if (ret != 0) {
pr_err("ERROR: DISABLE_VECTOR_REGISTERS() for wc_RunAllCast_fips() returned %d.\n", ret);
(void)libwolfssl_cleanup();
return -ECANCELED;
}

/* See the snapshot rationale in the wolfCrypt_IntegrityTest_fips()
* block above. */
svr_disallowed_snapshot = wc_svr_disallowed_count_current();

ret = wc_RunAllCast_fips();

REENABLE_VECTOR_REGISTERS();

#ifdef WC_LINUXKM_HAVE_STACK_DEBUG
stack_usage = wc_linuxkm_stack_hwm_measure_rel(0xee);
pr_info("STACK INFO: rel usage by wc_RunAllCast_fips() with DISABLE_VECTOR_REGISTERS(): %lu\n", stack_usage);
/* shush up false stack HWM reading by kernel: */
wc_linuxkm_stack_hwm_prepare(0);
}
#endif

svr_disallowed_count = wc_svr_disallowed_count_current();
if (svr_disallowed_count <= svr_disallowed_snapshot) {
pr_err("ERROR: wc_svr_disallowed_count_current() returned %llu after wc_RunAllCast_fips() with DISABLE_VECTOR_REGISTERS() (snapshot %llu): inhibited-save instrumentation was not exercised.\n", svr_disallowed_count, svr_disallowed_snapshot);
(void)libwolfssl_cleanup();
return -ECANCELED;
}

if (ret != 0) {
pr_err("ERROR: wc_RunAllCast_fips() with DISABLE_VECTOR_REGISTERS() returned %d.\n", ret);
(void)libwolfssl_cleanup();
return -ECANCELED;
}

ret = wolfCrypt_GetStatus_fips();
if (ret != 0) {
pr_err("ERROR: wolfCrypt_GetStatus_fips() failed with code %d: %s\n", ret, wc_GetErrorString(ret));
(void)libwolfssl_cleanup();
return -ECANCELED;
}
/* The CASTs are not re-run with the registers disabled: CPUID picks
* one lane per algorithm, so a refused save is an error there. */
}

#endif /* WC_LINUXKM_SVR_DYNAMIC_AUDITING */
Expand Down
4 changes: 2 additions & 2 deletions linuxkm/x86_vector_register_glue.c
Original file line number Diff line number Diff line change
Expand Up @@ -599,8 +599,8 @@ WARN_UNUSED_RESULT int wc_save_vector_registers_x86(enum wc_svr_flags flags)
* Note that this is not actually an abnormal condition -- e.g. with
* LINUXKM_DRBG_GET_RANDOM_BYTES, get_random_u32() and the like called from
* hard IRQ handlers can land here, and we return success if
* WC_SVR_USE_NATIVE_REG_BUFS, else WC_ACCEL_INHIBIT_E for graceful fallback
* to C.
* WC_SVR_USE_NATIVE_REG_BUFS, else WC_ACCEL_INHIBIT_E. Callers whose lane
* is pinned report that error rather than computing the answer in C.
*/
if ((cur_preempt_count & (NMI_MASK | HARDIRQ_MASK)) != 0) {
#ifdef WC_SVR_USE_NATIVE_REG_BUFS
Expand Down
32 changes: 16 additions & 16 deletions tests/api/test_mldsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -30073,10 +30073,10 @@ int test_mldsa_encode_w1_large_values(void)

#if defined(DEBUG_VECTOR_REGISTER_ACCESS) && \
defined(DEBUG_VECTOR_REGISTER_ACCESS_FUZZING)
/* Pin dispatch to the C path: under SVR2 fuzzing the two calls can
* otherwise take different (AVX2 vs C) implementations, which are only
* specified - and only equal - on the valid input domain. */
WC_DEBUG_SET_VECTOR_REGISTERS_RETVAL(WC_NO_ERR_TRACE(SYSLIB_FAILED_E));
/* Let every save succeed for this test. A refused save is an error that
* writes nothing, so two refused calls would compare equal while encoding
* nothing; pinning saves ON keeps one lane for both calls instead. */
WC_DEBUG_SET_VECTOR_REGISTERS_RETVAL(0);
#endif

/* ---- 6-bit encoding (mldsa_encode_w1_88 path) ---- */
Expand All @@ -30093,8 +30093,8 @@ int test_mldsa_encode_w1_large_values(void)

XMEMSET(enc_a, 0, sizeof(enc_a));
XMEMSET(enc_b, 0, sizeof(enc_b));
wc_mldsa_encode_w1_88(w1, enc_a);
wc_mldsa_encode_w1_88(w1, enc_b);
ExpectIntEQ(wc_mldsa_encode_w1_88(w1, enc_a), 0);
ExpectIntEQ(wc_mldsa_encode_w1_88(w1, enc_b), 0);

/* Determinism: same input must produce same output */
ExpectIntEQ(XMEMCMP(enc_a, enc_b, sizeof(enc_a)), 0);
Expand All @@ -30106,8 +30106,8 @@ int test_mldsa_encode_w1_large_values(void)
}
XMEMSET(enc_a, 0, sizeof(enc_a));
XMEMSET(enc_b, 0, sizeof(enc_b));
wc_mldsa_encode_w1_88(w1, enc_a);
wc_mldsa_encode_w1_88(w1, enc_b);
ExpectIntEQ(wc_mldsa_encode_w1_88(w1, enc_a), 0);
ExpectIntEQ(wc_mldsa_encode_w1_88(w1, enc_b), 0);
ExpectIntEQ(XMEMCMP(enc_a, enc_b, sizeof(enc_a)), 0);

/* Ascending pattern: each element differs */
Expand All @@ -30116,8 +30116,8 @@ int test_mldsa_encode_w1_large_values(void)
}
XMEMSET(enc_a, 0, sizeof(enc_a));
XMEMSET(enc_b, 0, sizeof(enc_b));
wc_mldsa_encode_w1_88(w1, enc_a);
wc_mldsa_encode_w1_88(w1, enc_b);
ExpectIntEQ(wc_mldsa_encode_w1_88(w1, enc_a), 0);
ExpectIntEQ(wc_mldsa_encode_w1_88(w1, enc_b), 0);
ExpectIntEQ(XMEMCMP(enc_a, enc_b, sizeof(enc_a)), 0);
}
#endif /* !WOLFSSL_NO_ML_DSA_44 */
Expand All @@ -30136,8 +30136,8 @@ int test_mldsa_encode_w1_large_values(void)

XMEMSET(enc_a, 0, sizeof(enc_a));
XMEMSET(enc_b, 0, sizeof(enc_b));
wc_mldsa_encode_w1_32(w1, enc_a);
wc_mldsa_encode_w1_32(w1, enc_b);
ExpectIntEQ(wc_mldsa_encode_w1_32(w1, enc_a), 0);
ExpectIntEQ(wc_mldsa_encode_w1_32(w1, enc_b), 0);

ExpectIntEQ(XMEMCMP(enc_a, enc_b, sizeof(enc_a)), 0);
}
Expand All @@ -30148,8 +30148,8 @@ int test_mldsa_encode_w1_large_values(void)
}
XMEMSET(enc_a, 0, sizeof(enc_a));
XMEMSET(enc_b, 0, sizeof(enc_b));
wc_mldsa_encode_w1_32(w1, enc_a);
wc_mldsa_encode_w1_32(w1, enc_b);
ExpectIntEQ(wc_mldsa_encode_w1_32(w1, enc_a), 0);
ExpectIntEQ(wc_mldsa_encode_w1_32(w1, enc_b), 0);
ExpectIntEQ(XMEMCMP(enc_a, enc_b, sizeof(enc_a)), 0);

/* Ascending pattern */
Expand All @@ -30158,8 +30158,8 @@ int test_mldsa_encode_w1_large_values(void)
}
XMEMSET(enc_a, 0, sizeof(enc_a));
XMEMSET(enc_b, 0, sizeof(enc_b));
wc_mldsa_encode_w1_32(w1, enc_a);
wc_mldsa_encode_w1_32(w1, enc_b);
ExpectIntEQ(wc_mldsa_encode_w1_32(w1, enc_a), 0);
ExpectIntEQ(wc_mldsa_encode_w1_32(w1, enc_b), 0);
ExpectIntEQ(XMEMCMP(enc_a, enc_b, sizeof(enc_a)), 0);
}
#endif /* !WOLFSSL_NO_ML_DSA_65 || !WOLFSSL_NO_ML_DSA_87 */
Expand Down
Loading
Loading