Commit Graph
5101 Commits
Author SHA1 Message Date
Takashi Kojo 997cfdaeb2 PKCS#8 algId check and DER-only typed d2i for ML-DSA 2026-08-07 07:38:29 +09:00
Takashi Kojo 69749e8f99 Reset MlDsaKey between decode attempts in X509_set_pubkey 2026-08-07 07:38:29 +09:00
Takashi Kojo 9f1dfdb65b Allow NULL md for ML-DSA X509 signing, add test 2026-08-07 07:38:29 +09:00
Takashi Kojo ca4986e2bd Size X509 gen buffer from subject public key too 2026-08-07 07:38:29 +09:00
Takashi Kojo da4f1b0236 Explicit prePopulated flag for d2iTryMlDsaKey 2026-08-07 07:38:29 +09:00
Takashi Kojo 19cec7fe89 Dynamic buff size based on expected cert size 2026-08-07 07:38:28 +09:00
Takashi Kojo e9ca064696 Add ML-DSA test coverage for X509_REQ_sign 2026-08-07 07:38:28 +09:00
Takashi Kojo 52766c5b69 OpenSSL compat with ML-DSA 2026-08-07 07:38:28 +09:00
David Garske 65836b4069 Merge pull request #11061 from Frauschi/fix-cert-evp-buffer-overruns
Fix buffer overruns in certificate signing and the EVP_PKEY populate paths
2026-08-06 15:29:00 -07:00
Tobias Frauenschläger 63dc0dc827 Reset stale key metadata when d2i reuses an EVP_PKEY
d2i_make_pkey() replaces the key data, size and type of a caller-supplied
WOLFSSL_EVP_PKEY, but left pkcs8HeaderSz, mldsaOID, pkey_curve and
save_type describing the key the object held before.

A stale pkcs8HeaderSz is the damaging one. pkcs8_encode() and
wolfssl_i_evp_pkey_get_der() both encode from pkey.ptr + pkcs8HeaderSz,
so after

    d2i_PrivateKey(EVP_PKEY_RSA, NULL, &p, pkcs8RsaDer);
    d2i_PrivateKey_EVP(&pkey, &q, traditionalEccDer);

PEM_write_bio_PKCS8PrivateKey() reports success while wrapping the ECC
key with its first 26 bytes cut off, and the resulting PEM cannot be
read back.

Only the d2i_PUBKEY and d2i_PrivateKey_EVP routes are affected;
d2i_PrivateKey and d2i_AutoPrivateKey go through d2i_evp_pkey(), which
allocates a fresh object and recomputes the header size.

The same branch also drops the data and the key object of the previous
key without releasing either. pkey.ptr is overwritten with a fresh
allocation, and wolfSSL_EVP_PKEY_free() only disposes of the object
matching the type currently set, so the object of a key whose type has
since changed is never freed. The sequence above leaks the 1219 byte RSA
encoding together with the WOLFSSL_RSA and its bignums, 13 allocations
in all. The data is released after the new encoding has been copied in,
since the caller may be decoding out of it.

Clear the metadata and dispose of the previous key on the reuse branch,
so a reused object decodes to the same state as a new one, and add a
regression test comparing the PKCS#8 output of a reused key against a
freshly decoded one.
2026-08-06 22:19:42 +02:00
Tobias Frauenschläger 009ded5b83 Tighten EC key ownership on the EVP_PKEY
This is hardening rather than a fix for a reachable defect. Both branches
below are wrong as written, but no entry point tested reaches them:
wolfSSL_d2i_PrivateKey() and wolfSSL_d2i_PUBKEY() both leave pkey->ecc
populated, so wolfSSL_EVP_PKEY_get1_EC_KEY() always takes its up_ref path.

wolfSSL_EVP_PKEY_get1_EC_KEY() has a branch that builds an EC_KEY when the
pkey does not carry one, caches it on the pkey and returns it. It did that
without taking a second reference and without setting ownEcc, so the
single reference the key was created with was handed to the caller while
the pkey kept an unowned pointer to it. A caller releasing what get1 gave
it, as the contract requires, would leave pkey->ecc dangling. The pkey now
keeps the reference the key was created with and the caller gets one of
its own.

The same branch freed the key when neither DER load succeeded but left
pkey->ecc pointing at it. That pointer is now cleared.

wolfSSL_EVP_PKEY_keygen() set ownEcc on the EC path whether or not it had
created the key, so a key placed on the pkey by something that did not
transfer ownership would gain a second owner. Ownership is now claimed
where the key is created.

Adds test_wolfSSL_EVP_PKEY_get1_EC_KEY_reuse(), which releases the
reference get1 returns and then calls get1 again. It covers the path a
decoded pkey actually takes and pins the reference contract; it passes
with and without the change above, which the comment on the test says
plainly so it is not mistaken for a regression test.
2026-08-06 22:19:42 +02:00
Tobias Frauenschläger e266d46e14 Release the previous DH key in EVP_PKEY_keygen
The DH case of wolfSSL_EVP_PKEY_keygen() assigned straight over
pkey->dh:

    case WC_EVP_PKEY_DH:
        pkey->dh = wolfSSL_DH_new();

A caller supplied EVP_PKEY can already hold a DH object.
wolfSSL_EVP_PKEY_set1_DH() takes a reference and sets ownDh, and
wolfSSL_EVP_PKEY_assign_DH() installs one outright, so keygen on such a
pkey dropped the only pointer the EVP_PKEY had to that object without
releasing its reference, and nothing freed it afterwards.

The case now generates into a temporary and frees the previous key when
the pkey owned it, which is the shape the RSA case in the same switch
uses.

Adds test_wolfSSL_EVP_PKEY_keygen_dh_reuse(), which loads DH parameters,
puts them on an EVP_PKEY with set1_DH so the pkey holds a reference, and
then runs keygen on that same pkey. The leak itself is not asserted by the
test: it needs an allocation tracker, and the smoke-test sanitize-asan job
provides one, since it builds with AddressSanitizer and sets no
ASAN_OPTIONS, so LeakSanitizer runs by default there. What the test does
locally is drive the path and show it stays free of double frees under
AddressSanitizer.
2026-08-06 22:19:42 +02:00
Tobias Frauenschläger ecabfeb301 Fix buffer overrun in the NO_REALLOC EVP_PKEY populate paths
Under WOLFSSL_NO_REALLOC, PopulateRSAEvpPkeyDer() and
ECC_populate_EVP_PKEY() emulate XREALLOC by allocating a buffer sized for
the NEW encoding and then copying pkey_sz bytes, the size of the OLD one,
into it:

    derBuf = (byte*)XMALLOC((size_t)derSz, pkey->heap, DYNAMIC_TYPE_DER);
    if (derBuf != NULL) {
        XMEMCPY(derBuf, pkey->pkey.ptr, (size_t)pkey->pkey_sz);

Whenever the replacement key encodes shorter than the one already on the
EVP_PKEY the copy runs past the end of the new allocation. Putting a
public key on a pkey holding a 2048-bit private key copies 1192 bytes
into a 294 byte buffer.

The copy serves no purpose: both functions fill the new buffer with a
fresh encoding immediately afterwards. It is removed rather than bounded.
ECC_populate_EVP_PKEY() also gains the pkey_sz reset that
PopulateRSAEvpPkeyDer() already has, so a failure between the allocation
and the encoding cannot leave the size describing a buffer that holds no
encoding.

The outgoing buffer is now wiped with ForceZero() before it is
reallocated or freed. On a private key it holds a complete RSA or ECC DER,
so returning it to the allocator intact leaves the key recoverable from
the free pool through a later heap over-read, a core dump or a swap page.
wolfSSL_RSA_To_Der_ex() establishes the same convention two frames away.
wolfSSL_EVP_PKEY_free() gets the same treatment, since it releases that
buffer on every normal teardown, as does the PKCS#8 branch of
PopulateRSAEvpPkeyDer(), which frees the unwrapped PKCS#1 key on its
success path once the wrapped copy has been built.

In ECC_populate_EVP_PKEY() that covers all three sites which release the
previous encoding, the two private-key branches as well as the public
one. clearEVPPkeyKeys() leaves pkey.ptr in place, so a pkey decoded from
a private key still carries that DER when a public-only key replaces it.
The wipe there happens before the allocation, since XREALLOC consumes the
old pointer, and pkey_sz and pkcs8HeaderSz are dropped with the contents
so a failed allocation cannot leave either describing a buffer that no
longer holds an encoding.

Where the allocation of the new buffer fails, pkcs8HeaderSz is cleared
along with pkey_sz for the reason given in the previous commit.

ECC_populate_EVP_PKEY() clears pkcs8HeaderSz when it installs a public
key. A SubjectPublicKeyInfo has no PKCS#8 wrapper, but neither
wolfSSL_EVP_PKEY_set1_EC_KEY() nor clearEVPPkeyKeys() resets the field, so
putting a public key on a pkey decoded from a PKCS#8 EC key left the
export paths starting that many bytes inside the new encoding and
returning it short under a success return.

The traditional private-key branch needs the same reset. It runs whenever
the incoming EC key carries no header size of its own, a generated key for
instance, and writes a bare SEC1 ECPrivateKey. Seeding an EVP_PKEY from
certs/ecc-keyPkcs8.der and then calling wolfSSL_EVP_PKEY_set1_EC_KEY()
with a generated key made wolfSSL_i2d_PrivateKey() return 92 bytes
beginning in the middle of the private scalar rather than the 121 byte
encoding. Every export path is affected, including the PKCS#8 encryption
in wolfSSL_PEM_write_bio_PKCS8PrivateKey(), which encrypts that same
misaligned slice.

Adds test_wolfSSL_EVP_PKEY_set1_shrinking_der(), which replaces the key
on an EVP_PKEY with a public-only one for both RSA and ECC and requires
the stored encoding to shrink. The smoke-test job
opensslextra-norealloc-asan builds exactly this configuration under
AddressSanitizer, which is where the over-copy is caught.

The test gates each algorithm on its own prerequisites rather than on one
shared list. WOLFSSL_KEY_TO_DER is defined by settings.h only when RSA is
enabled, so requiring it for the whole test compiled the ECC half out of
any build without RSA, and that half is the only coverage the ECC
over-copy has. The ECC half is seeded from a PKCS#8 wrapped key so that
pkcs8HeaderSz starts non-zero, and its size assertion is exact rather than
a comparison against the previous size, so an export starting at a stale
header shows up as a mismatch rather than passing.

test_wolfSSL_EVP_PKEY_set1_EC_KEY_no_pkcs8() covers the private-key case.
It compares the encoding exported after the replacement against the one a
pkey that never held a wrapped key produces from the same EC key, so a
carried over header size shows up as a size and content mismatch.
2026-08-06 22:19:42 +02:00
David Garske 073d4e23fb Merge pull request #11025 from aidankeefe2022/test-fix-for-md5-and-sha-enabled
Fix: Test memory leak and Failing test when md5 and sha are enabled
2026-08-06 11:43:06 -07:00
Tobias Frauenschläger ea911b8c9b Fix buffer overrun in EVP_PKEY_keygen on a populated EVP_PKEY
The RSA branch of wolfSSL_EVP_PKEY_keygen() passed &pkey->pkey.ptr
directly to wolfSSL_i2d_RSAPrivateKey():

    pkey->pkey_sz = wolfSSL_i2d_RSAPrivateKey(pkey->rsa,
            (unsigned char**)&pkey->pkey.ptr);

Following the i2d convention, wolfSSL_RSA_To_Der_ex() treats a non-NULL
*outBuf as a caller supplied buffer: it encodes into it with no size
check and then advances the pointer past the encoding. When ppkey points
at an EVP_PKEY that already carries a DER encoding, the generated private
key is written into that older, typically smaller allocation and
pkey.ptr is left pointing into the middle of it, which the eventual
XFREE() then trips over. Decoding a 2048-bit public key and calling
keygen on the same EVP_PKEY writes about 1190 bytes into a 294 byte
buffer.

The branch now installs the generated key on the pkey and hands the
encoding to PopulateRSAEvpPkeyDer(), which is what
wolfSSL_EVP_PKEY_set1_RSA() already does and what the sibling EC branch
does through ECC_populate_EVP_PKEY(). That function sizes the encoding
first and allocates its own buffer, so i2d is never shown a populated
pkey, and the three copies of the free, encode and assign sequence become
one. It also allocates against pkey->heap, which is the heap every site
that later releases pkey.ptr passes, while i2d deliberately allocates
with a NULL hint because its result is returned to the user.

The old RSA key is released before the new one is installed, which fixes
the previous unconditional overwrite of pkey->rsa leaking the old object,
and success is no longer reported when the encoding fails.

pkcs8HeaderSz is taken from the newly generated key rather than left as
it was. It describes the DER currently held in pkey.ptr, and
PopulateRSAEvpPkeyDer() adds a PKCS#8 wrapper only when the RSA key
carries a header size. d2i_PrivateKey(), d2i_AutoPrivateKey() and
PEM_read_bio_PrivateKey() set the field to 26 for a wrapped RSA key, and a
pkey obtained that way and then reused for keygen kept the 26 while the
encoding underneath was no longer wrapped. Every export path that trusts
the pair then sliced 26 bytes off the front of the new key:
wolfssl_i_evp_pkey_get_der() behind i2d_PrivateKey(), pkcs8_encode()
behind i2d_PKCS8PrivateKey(), and wolfssl_pkey_encrypt() behind
PEM_write_bio_PrivateKey(), returning a corrupt encoding under a success
return. wolfSSL_EVP_PKEY_set1_RSA() already maintains this field.

Adds test_wolfSSL_EVP_PKEY_keygen_reuse(), which runs keygen on an
EVP_PKEY populated from a public key DER and requires the resulting
encoding to decode back to the generated key, plus a second pass seeded
from a PKCS#8 key since the public key seed leaves pkcs8HeaderSz at zero
and cannot catch the stale header.

The test is gated on OPENSSL_EXTRA rather than OPENSSL_ALL, since nothing
it calls needs the latter, and on !NO_ASN and !NO_PWDBASED because
wolfSSL_i2d_PrivateKey() is compiled only under those. The PKCS#8 pass
additionally needs !NO_CERTS, which is what load_file() is gated on.

The forward declaration is gated on the same condition as the definition
rather than on WOLFSSL_KEY_GEN, which settings.h only happens to derive
WOLFSSL_KEY_TO_DER from today.

The sibling cases in the same switch are left alone deliberately. The DH
case does not free a previous pkey->dh, and the EC case promotes a
borrowed pkey->ecc to owned, both of which are the same ownership class
this change fixes for RSA. They are pre-existing, they need their own
tests, and folding them in here would widen a buffer overrun fix into a
rework of EVP_PKEY_keygen ownership across four algorithms.
2026-08-06 08:50:30 +02:00
Tobias Frauenschläger 757f866fd8 Fix buffer overrun in SignCert when sizing the signature wrapper
SignCert() checked the output buffer with

    requestSz + MAX_SEQ_SZ * 2 + sigSz > buffSz

before handing the buffer to AddSignature(). That accounts for the outer
SEQUENCE header but not for the signatureAlgorithm AlgorithmIdentifier
(OID plus optional NULL parameters) or the signatureValue BIT STRING
header that AddSignature() also writes, an under-count of about 13 bytes.
AddSignature() takes no buffer size of its own, so any certificate whose
final encoding lands in that narrow band just under buffSz passed the
check and was written past the end of the buffer.

The same estimate was used in wc_SignCert_cb().

Both call sites now ask AddSignature() for the exact encoding size by
passing a NULL buffer first, then compare that against buffSz. This is
the two-pass idiom already used when signing CRLs in SignCrl(), in
wolfssl_x509_make_der() and in wolfSSL_X509_CRL_sign(). Both the template
and the original ASN.1 encoders support the NULL buffer sizing call.

The comparison is made unsigned, matching the pre-flight in SignCrl().
Casting buffSz to int made a buffer larger than INT_MAX compare negative
and rejected every signature for it.

Both functions also now bound requestSz against buffSz up front.
MakeSignature() and MakeSignatureCb() hash requestSz bytes out of buf
before any size check runs, so a caller passing the two mismatched got an
out of bounds read of up to requestSz - buffSz bytes before the function
returned. Only a negative requestSz was rejected before. Reaching this
needs the application to pass values that disagree, so it is API misuse
rather than attacker controlled input, but the read side now carries the
same guarantee as the write side.

Reachable from the OpenSSL compatibility layer through
wolfSSL_X509_sign() and wolfSSL_X509_REQ_sign(), where the caller
controls the certificate contents that steer the encoded size into the
band.

Adds test_wc_SignCert_buffer_bounds(), which signs into buffers sized
across the band below the exact encoding size and requires BUFFER_E and
an untouched guard region for each, while still accepting the exact size.
test_wc_SignCert_cb() gains the same check for the callback entry point,
using its RSA half where the PKCS#1 v1.5 signature is fixed length, in
both directions so that an over-conservative estimate is caught too.

The bounds test covers ECDSA as well as RSA. IsSigAlgoNoParams() drops
the NULL parameters from the AlgorithmIdentifier, so the width an
estimate under-counts by differs between the two: 24 bytes of wrapper
against the 12 byte estimate for RSA, but only 19 for ECDSA, putting the
capacities that used to be accepted and overrun within 8 bytes of the
exact size.

An ECDSA encoding size cannot be measured once and reused, because the
DER INTEGERs holding r and s change length with the leading zero bytes of
each new signature. The sweep measures a fresh reference size every
iteration and, rather than requiring BUFFER_E for a capacity that the
next signature might genuinely fit, asserts what has to hold either way:
the call returns BUFFER_E or a size within the capacity, and the guard
region past the capacity is untouched. That covers the whole band instead
of trading it away for a margin wide enough to absorb the jitter.

The prerequisites are split into one condition macro per algorithm rather
than one shared list. Gating the whole test on the RSA prerequisites
would have compiled the ECDSA sweep out of a build without RSA, which is
exactly where it is the only coverage that exists.

Both tests set an explicit serial number. wc_InitCert() leaves serialSz at
zero, so wc_MakeCert() generates a random serial, and GenerateInteger()
does not shrink the length after dropping leading zero bytes, which lets
the promoted byte carry the MSB and makes the encoder pad the INTEGER with
an extra 0x00. Measured over 200000 generated bodies, 813 of them, 0.406
percent, came out one byte longer, which would have made the swept
capacities disagree with the reference size for roughly one run in 128.
2026-08-06 08:50:30 +02:00
Tobias Frauenschläger eab70a1e88 Merge pull request #11031 from douzzer/20260727-fips-dev-no-post
20260727-fips-dev-no-post
2026-08-06 08:47:10 +02:00
David Garske e941761ace Merge pull request #11060 from Frauschi/async_pha
Make `--enable-all --enable-asynccrypt-sw` work end to end
2026-08-05 21:32:17 -07:00
David Garske a0da7663a7 Merge pull request #11059 from Frauschi/sfhb_reload_fix
LMS/XMSS: don't skip the software reload when a read callback is set
2026-08-05 21:29:54 -07:00
David Garske 55e25428d8 Merge pull request #10517 from Roy-Carter/bugfix/wolfSSL_add0_chain_cert_not_incrementing
Bugfix- wolf ssl add0 chain cert not incrementing certchain and causing TLS1.3 to fail
2026-08-05 21:29:30 -07:00
Tobias Frauenschläger 7a3d42459e LMS/XMSS: don't skip the software reload when a read callback is set
wc_LmsKey_Reload and wc_XmssKey_Reload return success without doing any
work whenever key->devId != INVALID_DEVID, on the assumption that a
device-bound key has its private state inside that device. That assumption
does not hold for a caller that sets devId only to route primitives to a
hardware accelerator while keeping the key state in its own storage.

A wolfHSM server is exactly that caller. It configures a server-wide devId
so AES, ECC and RSA reach the platform accelerator, then for LMS/XMSS it
installs read/write callbacks and calls Reload to rebuild the expanded
private key before signing. With a hardware devId configured, Reload
returned 0 immediately and the key was left unusable for the first sign
that follows. The failure needs no invalid input, only a server built with
an accelerator.

For LMS that first sign is a crash: key->priv_data stays NULL, wc_hss_sign
finds priv.inited clear and calls wc_hss_init_auth_path, which derives its
first read from a NULL priv pointer. On a target without a mapping at low
addresses that is a bus fault. For XMSS the skipped reload never allocates
key->sk, leaving both the pointer NULL and sk_len 0, so the outcome depends
on the caller's read callback: one that honours the length it is given
returns nothing and the sign fails with IO_FAILED_E, while one that writes
a fixed-size record faults on the NULL destination.

Key generation was unaffected and hid the problem: wc_LmsKey_MakeKey
already treats devId as "offer the operation to the callback, fall back to
software on CRYPTOCB_UNAVAILABLE", so it populates the key correctly when
the accelerator declines. Reload had no equivalent fall-through, so the
same key and the same devId were interpreted two different ways by the
same API.

Qualify the short-circuit with key->read_private_key == NULL. A caller that
has installed a read callback is asking for the software reload to fetch
the state through it, whereas a genuinely device-backed key installs no
such callback. Key generation keeps offering the operation to the crypto
callback, so a port with real stateful-hash-signature hardware is not
prevented from using it.

The reference POSIX wolfHSM server runs with INVALID_DEVID, which is why
this was not caught by existing tests. test_wc_LmsKey_reload_devid and
test_wc_XmssKey_reload_devid cover both arms of the new condition: a key
whose read callback is set must come back from Reload with its private key
expanded (priv_data for LMS, sk for XMSS) and able to sign, while a key on
the same devId with no read callback must still short-circuit. Both tests
register a crypto callback that declines every operation with
CRYPTOCB_UNAVAILABLE, which is the accelerator this fix is about. Asserting
on the expanded key means the old behaviour fails the assertion rather than
the NULL dereference it leads to. The rest of the LMS and XMSS suite is
unaffected, as every other key there uses INVALID_DEVID.

The XMSS test needs the H10 SHA-256 parameter set, which is only in the
algorithm table when both the hash and the height are compiled in, so it
carries a guard for that. The crypto callback's own guard is the exact
union of the two test guards, or a build with only one of the two
algorithms would emit it with no caller and fail -Werror.

Both the Reload implementation comments and the published Doxygen now state
that the read callback, not the devId, decides whether the software reload
runs.
2026-08-05 22:23:43 +02:00
Aidan Keefe 276f865d8c revert DoExpect to Expect and memset rng so on init fail the object is safe to free 2026-08-05 13:53:33 -06:00
Daniel Pouzzner 67df4928c5 fixes from peer review, post-rebase test results, and test coverage expansion:
wolfcrypt/src/wc_mldsa.c: WC_C_DYNAMIC_FALLBACK fixes for AVX512.

tests/api/test_frodokem.c, wolfcrypt/test/test.c: fixes for WC_DEBUG_CIPHER_LIFECYCLE.

wolfcrypt/src/fe_x25519_asm.S, wolfcrypt/src/port/arm/armv8-32-aes-asm.S,
wolfcrypt/src/port/arm/armv8-aes-asm.S, wolfcrypt/src/port/arm/armv8-aes-asm_c.c,
wolfcrypt/src/port/arm/thumb2-aes-asm.S, wolfcrypt/src/sha3_asm.S,
wolfcrypt/src/wc_mldsa_asm.S: regenerate from scripts#647

.github/workflows/fips-dev-no-post.yml:
* update "minutes" for tests using empirical data;
* add --enable-experimental --enable-all-quantum-crypto to kernel-settings-all-asm scenario and rename it kernel-settings-all-pqc-asm; add all-pqc-asm-fallback-fuzzer scenario.

wolfcrypt/src/wolfentropy.c: remove WC_FIPS_LL_CRYPTO (it is not a FIPS file except in FIPS v5.2.4).

wolfcrypt/src/sha256.c: fix for rebase error (stray #endif).
2026-08-05 13:55:14 -05:00
Daniel Pouzzner c249a09b38 wolfcrypt/src/sha256.c, wolfcrypt/src/sha512.c: clean up and fix up gating around WC_C_DYNAMIC_FALLBACK: use WC_SHA{256,512}_RAW_BE_BUFFER to indicate big-endian buffer.
tests/swdev/swdev.c: fix typos (FIPS_VERSION3_LT, not FIPS_VERSION_LT).
2026-08-05 13:53:47 -05:00
Daniel Pouzzner 83b175ce95 fixes and improvements from AI review:
.github/workflows/fips-dev-no-post.yml: add reporting of fuzzing seed.

wolfcrypt/src/aes.c, tests/api/test_aes.c, .wolfssl_known_macro_extras:

* change FIPS AES-GCM nonce size restrictions from from opt-out (WC_FIPS_AESGCM_ALLOW_SHORT_NONCES) to opt-in (WC_FIPS_AESGCM_NO_SHORT_NONCES).
* apply restrictions only on encryption operations, never on decryption.

configure.ac: fix HAVE_FIPS_VERSION of fips-ready; fix enable_dh setup in KERNEL_MODE_DEFAULTS setup; fix help message for --enable-dh.

linuxkm/x86_vector_register_glue.c: add dump_stack() on each BUG/WARNING message that didn't already have it.

src/tls.c: fix a couple leaks in TLSX_KeyShare_GenDhKey().

tests/swdev/swdev.c: gate src->sha_method access in swdev_sha256_copy_state() and swdev_sha512_copy_state() appropriately.

tests/unit.c: conditionally include dh.h, to assure wc_dh_enable() is available.

wolfcrypt/src/dh.c:
* in wc_InitDhKey_ex(), zero the key at entry unless null, remove duplicate key->trustedGroup = 0, and call wc_FreeDhKey() on error at end.
* add missing wc_dh_enabled checks in  wc_DhGeneratePublic() and wc_DhGenerateParams().

wolfcrypt/src/error.c: fix missing space in FIPS_UNAPPROVED_E string.

b/wolfssl/wolfcrypt/settings.h: sense __SIZEOF_INT128__ and if defined, but HAVE___INT128_T and/or HAVE___UINT128_T are undefined, define them.

wolfcrypt/src/falcon.c: tweak the gate on __uint128_t availability to lean solely on HAVE___UINT128_T.

wolfcrypt/src/random.c: fix a couple missed WC_NO_ERR_TRACE() wrappers.

wolfcrypt/src/rng_bank.c: properly tolerate WC_ACCEL_INHIBIT_E as a retval from bank->affinity_lock_cb().

wolfcrypt/src/sha256.c, wolfcrypt/src/sha512.c: move #undef WC_C_DYNAMIC_FALLBACK for WOLFSSL_AESNI without USE_INTEL_SPEEDUP to follow all includes, assuring no struct layout conflict.

wolfcrypt/src/sha512.c: fix wrong call in intelasm Transform_Sha512() !WC_C_DYNAMIC_FALLBACK SHA512_C path.

wolfcrypt/test/test.c: fix double-WC_TEST_RET_ENC_EC() in mldsa_param_*_vfy_test().
2026-08-05 13:53:47 -05:00
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
Daniel Pouzzner 2a3e1473b9 tests/api/test_cmac.c: add && !defined(WOLFSSL_KCAPI) to two build guards. 2026-08-05 13:53:46 -05:00
Daniel Pouzzner 4803bbd58e wolfssl/test.h: convert err_sys() and err_sys_with_errno() to err_sys_func() /
err_sys_with_errno_func() taking __FILE__ and __LINE__, with function-like
macros preserving every existing call site; failure messages now carry file and
line ("wolfSSL error, %s L %d: %s").

tests/suites.c: client_test / server_test failure reports go to stderr
(printf -> fprintf(stderr, ...)).
2026-08-05 13:53:46 -05:00
Daniel Pouzzner 1b5307345d wolfcrypt/src/hmac.c: in wc_HmacSetKey_ex reject WC_MD5 under HAVE_FIPS
(unversioned defined(HAVE_FIPS), not a version arm -- hmac.c is in-boundary and
master's copy only compiles at v7+/MAJOR=8). This structurally closes the
old-TLS MD5 PRF: wc_PRF_TLSv1 -> wc_PRF(md5_mac) -> wc_HmacSetKey(WC_MD5) ->
BAD_FUNC_ARG. Separately, in wc_HKDF_Expand_ex, add `else if (ret == 0) return
BAD_FUNC_ARG;` after the wc_HmacSizeByType call: the existing code guarded
ret < 0 but not ret == 0, and hashSz is the divisor in the
`outSz/hashSz + ((outSz % hashSz) != 0) > 255` check three lines below.

wolfcrypt/src/kdf.c: delete the two WC_HASH_TYPE_MD5_SHA guards in wc_PRF /
wc_PRF_TLS -- they were a domain error (that arg is wc_MACAlgorithm, where
WC_HASH_TYPE_MD5_SHA == 9 == sm3_mac, so the guard blocked SM3, not MD5-SHA),
and the hmac.c reject is the correct layer.

wolfcrypt/src/evp.c: drop the MD5 EVP mapping at FIPS >= 5 (evp.c is out of
boundary, so the version arm is live and correct here).

tests/api/test_kdf.c: derive secLen from MAX_PRF_HALF rather than hardcoding
521/261 -- MAX_PRF_HALF is config-dependent (516 under HAVE_FFDHE_8192, 388
under FFDHE_6144, else 260), so the hardcoded value made the BUFFER_E
expectation config-dependent.
2026-08-05 13:53:45 -05:00
Daniel Pouzzner 290553f006 wolfcrypt/src/aes.c: in FIPS builds, reject ivSz < GCM_NONCE_MID_SZ (12) with
FIPS_BAD_VALUE_E in the GCM IV-construction paths under HAVE_FIPS, overridable
by WC_FIPS_AESGCM_ALLOW_SHORT_NONCES. Decrypt accepts any supported length per
SP 800-38D (IV construction requirements bind encryption only).

The floor takes two shapes, both correct by construction:
  - wc_AesGcmInit_local (reached from wc_AesGcmInit and
    wc_AesGcmEncryptInit_ex, which pass a decrypt_p flag):
        (ret == 0) && (! decrypt_p) && (ivSz > 0) && (ivSz < GCM_NONCE_MID_SZ)
    The `ivSz > 0` clause is load-bearing -- iv is an optional argument there,
    and the key-only re-init form (iv == NULL, ivSz == 0) used for
    module-generated-IV streaming must still pass.
  - wc_AesGcmSetIV / wc_AesGcmSetExtIV: bare `ivSz < GCM_NONCE_MID_SZ`, no
    ivSz>0 clause needed -- CheckAesGcmIvSize() already admits only {8,12,16},
    so ivSz == 0 cannot reach the floor.

tests/api/test_aes.c: update tests for new FIPS nonce size restrictions.

.wolfssl_known_macro_extras: add WC_FIPS_AESGCM_ALLOW_SHORT_NONCES.
2026-08-05 13:53:45 -05:00
Daniel Pouzzner eedceef23d wolfssl/wolfcrypt/dh.h, wolfcrypt/src/dh.c: add wc_dh_enable/disable/
is_enabled, WC_DH_INITIAL_RUNTIME_ENABLEMENT, WC_DH_HAVE_RUNTIME_ENABLEMENT.
Place the enablement check AFTER key->heap/trustedGroup init in the five entry
points (wc_InitDhKey_ex, wc_DhGenerateKeyPair, wc_DhAgree, wc_DhAgree_ct,
_DhSetKey) so a disabled-DH early return never leaves a half-initialized key for
wc_FreeDhKey to mp_clear on garbage.

configure.ac: add --enable-dh=conditional; when DH is enabled (directly or via
all-crypto) set it initially usable under FIPS v7 with
-DWC_DH_INITIAL_RUNTIME_ENABLEMENT=1; remove the FIPS-v7 DH force-off (in FIPS
v7+, disable build by default, unless building in kernel mode with DH
registration enabled).

linuxkm/lkcapi_glue.c: bracket LKCAPI registration with
`need_dh_disable = (wc_dh_enable() == 0)` ... `if (need_dh_disable)
wc_dh_disable();`, so DH is disabled on every exit path, and only by the caller
that actually enabled it (wc_dh_enable returns ALREADY_E if DH was already on,
so this never disables a DH some other context legitimately enabled).

tests/unit.c, wolfcrypt/test/test.c: bracket the DH tests with enable/disable so
they succeed regardless of runtime initial default enablement.
2026-08-05 13:53:45 -05:00
Daniel Pouzzner be988c508d tests/unit.h: pull in wolfssl/wolfcrypt/fips_test.h, for the WOLFSSL_FIPS_DEV_NO_POST stubs. 2026-08-05 13:53:45 -05:00
Tobias Frauenschläger ff730fec9e Keep the record size probe clear of a suspended build
Second review follow-up on the async record layer series.

Stop the probe reselecting the cipher side. BuildMessage()'s BUILD_MSG_BEGIN
case can call SetKeysSide() for DTLS with secure renegotiation, which swaps
the active encryption state and clears recordSzOverhead. That is not part of a
size calculation, and after the previous commit the suspended build survives
to resume against whatever side the probe last chose, so a DTLS 1.2 record
suspended for PREV_ORDER could resume against the renegotiation keys. Skip it
when sizeOnly is set; the sizes are the same either way.

The probe itself has to keep running. Not re-entering BuildMessage at all
while a build is suspended looks tidier, but wolfssl_local_GetMaxPlaintextSize()
derives the DTLS fragment size from this result, so falling back to the upper
bound there shrinks fragments inconsistently between calls and the MTU
reproducer fails its buffer comparison. Saving and restoring the two fields is
what keeps the answer exact.

Resume inside the record when handshake content is left. The previous commit
declined to skip the padding for a fragmented or coalesced
certificate_request, which was right, but left processReply at doProcessInit
with the index inside the record, so the resume still started a fresh record
parse in the middle of one. Mirror both halves of the end of record block
instead: set runProcessingOneMessage when content remains, advance past the
padding only at the boundary.

Note the shared state at the source. BuildMessage() and BuildTls13Message()
write ssl->options.buildMsgState even for a sizeOnly probe with asyncOkay
clear, where everything else goes to the caller's own arguments. Nothing said
so at those sites, so the next sizeOnly caller would reintroduce this.

Record why only one of the three wc_ecc_make_key_ex() calls in eccsi.c needs
a wait: the other two are preceded by wc_ecc_free(), which clears the marker
their pending path is gated on. Moving either free would make them pend.

Test changes. Force the overhead cache cold before probing, otherwise an AEAD
suite answers from the cache without ever calling BuildMessage and the
assertions hold no matter what the probe did. Compare against BuildMessage's
own figure rather than only checking the size is positive, and run the whole
thing for TLS 1.3 as well as TLS 1.2, since BuildTls13Message() clobbers the
state by a different route: its sizeOnly return bypasses exit_buildmsg
entirely. Checked by stubbing the restore out again, which fails the test.

Also spell the new guard in cryptocb_test() as #if defined(WOLFSSL_ASYNC_CRYPT)
to match the rest of that file, which uses that form 170 times against 4.
2026-08-05 20:39:57 +02:00
David Garske e5b3fb118e Merge pull request #11046 from yosuke-wolfssl/fix/f_7352
Zero EncryptedInfo in ProcessChainBufferCRL and reset info->set on parse
2026-08-05 08:49:18 -07:00
David Garske 556b969195 Merge pull request #11045 from yosuke-wolfssl/fix/f_7522
Declare QUIC record length from bytes remaining
2026-08-05 08:47:18 -07:00
David Garske 589b98ff47 Merge pull request #10957 from SparkiDev/arm32_cpuid_flags
ARM32 assembly: Get CPU Id flags to choose assembly.
2026-08-05 07:34:34 -07:00
Tobias Frauenschläger b6ae49ce66 Merge pull request #11056 from padelsbach/f7398-mldsa-param-checks
F-7398: add checking in MLDSA
2026-08-05 10:33:46 +02:00
Tobias Frauenschläger 1c5382f169 Harden the async record layer fixes and cover the size probe
Review follow-up on the two preceding commits.

Only skip the padding when the record content is actually consumed. The
post-handshake auth advance ran on any pending return that left processReply
at doProcessInit, which assumes the certificate_request was the last message
in its record. Two states break that: a fragmented certificate_request makes
DoTls13HandShakeMsg() rewind inOutIdx so the fragment can be reprocessed, and
RFC 8446 section 5.1 lets a peer coalesce several handshake messages into one
record, leaving the index inside the record. Adding padSz in either case
points the index at record content. Both states are already mishandled
without this series, since post-handshake auth forces processReply to
doProcessInit regardless, so this is a narrower guard rather than a
regression, and there is no memory safety consequence either way: the largest
possible index is the end of the record. The new test mirrors the end of
record check that follows the message handlers.

Also restore buildArgsSet across the record size probe. The probe borrows
buildMsgState and buildArgsSet from the suspended build; the previous commit
put back only the first. FreeBuildMsgArgs() clears buildArgsSet
unconditionally on the way out, and the resumed call skips the block that
would set it again, so the flag stays clear for the rest of the record. That
is currently harmless, because the only thing it guards is freeing a
dynamically allocated IV and no cipher in the tree needs one, but it leaves
the fix one field short of its own premise.

Add test_record_size_preserves_build_msg_state, which parks a connection at
BUILD_MSG_ENCRYPT with the arguments flag set, runs the probe, and requires
both fields to survive. It fails without the restore and passes with it.

Guard the post-handshake auth block on WOLFSSL_POST_HANDSHAKE_AUTH as well.
The only code that leaves processReply at doProcessInit from inside
DoTls13HandShakeMsg() is the post-handshake auth branch, which is itself
behind that macro, so the check cannot fire without it.

Say that the oversized record is rejected rather than that it overflows the
buffer. BuildMessage() catches it and returns BUFFER_E; the old wording read
like memory corruption.

Verified with --enable-all --enable-asynccrypt-sw and with plain --enable-all,
both warning free under the os-check CFLAGS. The two reproducers from the
preceding commits still complete.
2026-08-05 08:34:09 +02:00
Yosuke Shimizu f6bd188eee Declare QUIC record length from bytes remaining 2026-08-05 14:12:26 +09:00
Sean Parkinson 4444e0cf26 ARM32 assembly: Get CPU Id flags to choose assembly.
Added flag determination for Linux/Android/BSD/Windows/privilege-mode.
Made AES and SHA-256 runtime dispatch.
Updated the ARM32 assembly to support having multiple implementations compiled
in.
2026-08-05 14:45:19 +10:00
Yosuke Shimizu 9909d0226c Zero EncryptedInfo in ProcessChainBufferCRL and reset info->set on parse 2026-08-05 13:02:45 +09:00
David Garske aa0d44dac5 Merge pull request #11030 from cconlon/csrExtCritical
Encode basicConstraints critical flag and pathlen into generated CSRs
2026-08-04 20:52:21 -07:00
David Garske b802e461d8 Merge pull request #10955 from padelsbach/curve25519-cryptocb-only-full
Extend curve25519 crypto cb and cb-only for footprint savings
2026-08-04 18:46:35 -07:00
David Garske 5f272267c0 Merge pull request #10901 from Frauschi/slhdsa_tls_handshake
Add SLH-DSA support for the TLS 1.3 and DTLS 1.3 handshake
2026-08-04 18:46:01 -07:00
Paul Adelsbach 5d46b4bfe6 Add additional MLDSA checks 2026-08-04 17:02:54 -07:00
Paul Adelsbach 693e4354a3 F-7398: add checks in MLDSA 2026-08-04 16:33:33 -07:00
David Garske d6708600a2 Merge pull request #11027 from Frauschi/fenrir_2
Fixes for OCSP stapling, cert manager, and certificate_status_request_v2 handling
2026-08-04 15:54:59 -07:00
Aidan Keefe 2f9fc44c56 test fixes for signature now properly test hash strength check and do not leak memory on failure 2026-08-04 16:01:36 -06:00
Aidan Keefe 589088c71e review fix 2026-08-04 15:55:18 -06:00
aidan 1ce0c03a38 tests: guard weak-hash checks on WC_SIG_MIN_HASH_TYPE, fix RNG cleanup 2026-08-04 15:53:12 -06:00