Files
wolfssl/wolfcrypt
Daniel Pouzzner 70cb3f029a Fix WC_C_DYNAMIC_FALLBACK in various quantum-resistant alg implementations:
Under WC_C_DYNAMIC_FALLBACK, SAVE_VECTOR_REGISTERS2() can fail on any call, so
two calls on the same object can dispatch differently.  Each of these
algorithms had state that silently assumed a single dispatch for its lifetime.

wolfcrypt/src/wc_mldsa.c: add MLDSA_NTT_AVX2()/MLDSA_INVNTT_AVX2() selecting
the "full" AVX2 NTT/invNTT under WC_C_DYNAMIC_FALLBACK.  The non-full variants
leave NTT-domain coefficients in a permuted, lane-interleaved order that only
their matching consumers understand, whereas the full variants and the C
implementations use standard order.  NTT-domain data at rest (cached s1/s2/t0
vectors, the challenge polynomial) can be produced and consumed by
differently-dispatched calls, so its representation must be dispatch-invariant.
Without fallback, dispatch is invariant and the ~2%/~4% faster permuted-order
variants are kept.  Both pipelines are bit-identical end to end.

wolfcrypt/src/wc_mlkem_poly.c: in mlkem_derive_secret(), re-initialize the
shared SHAKE-256 object under WC_C_DYNAMIC_FALLBACK.  The buffer-stuffing
shortcut assumes a freshly initialized (zeroed) sponge, which no longer holds
once the C fallback legs of mlkem_gen_matrix()/mlkem_get_noise() drive the XOF
on that object and leave it mid-squeeze.

wolfcrypt/src/wc_slhdsa.c: in slhdsakey_fors_sign(), replace the
CAN_SAVE_VECTOR_REGISTERS() test with an actual SAVE_VECTOR_REGISTERS2() == 0
acquisition and a matching RESTORE_VECTOR_REGISTERS(), so the region is held
rather than merely predicted to be available.

wolfcrypt/src/wc_frodokem_mat.c: in the AES row kernels of
frodokem_mul_add_as_plus_e_aes() and frodokem_mul_add_sa_plus_e_aes(), re-key
with wc_AesSetKeyDirect() when IS_INTEL_AESNI() but !aes->use_aesni.  The
kernels consume aes->key directly, which holds an AES-NI-layout schedule only
if SetKey ran with vector registers available; under fallback a failed
SAVE_VECTOR_REGISTERS2() inside SetKey returns success having keyed only the
C-fallback schedule.  Re-keying happens inside the held region, where the
nested save always succeeds.  Loop conditions gain (ret == 0) so a re-key
failure stops the run.

wolfssl/wolfcrypt/settings.h: with the above, ML-KEM, ML-DSA, SLH-DSA and
FrodoKEM are fuzzer-clean, so the DEBUG_VECTOR_REGISTER_ACCESS_FUZZING
exclusion narrows from the _WC_BUILDING_WC_MLKEM_POLY_C / _WC_BUILDING_WC_MLDSA_C
/ _WC_BUILDING_WC_SLHDSA_C set to _WC_BUILDING_FALCON_C alone.  Falcon stays
excluded because it uses FP or vector registers in all of its asm
implementations and there is no option yet to build the C-no-FP implementation
alongside them.

tests/api/test_mldsa.c: in test_mldsa_encode_w1_large_values(), pin dispatch to
the C path with WC_DEBUG_SET_VECTOR_REGISTERS_RETVAL() for the duration of the
test and restore it afterward.  The two calls being compared are only specified
-- and only equal -- on the valid input domain, so letting the fuzzer send them
down different (AVX2 vs C) implementations is not a meaningful comparison.
2026-08-05 13:53:46 -05:00
..