Commit Graph
1147 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
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 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 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
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
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
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
Tobias Frauenschläger ac75f181cd Add SLH-DSA support for the TLS 1.3 and DTLS 1.3 handshake
Implement SLH-DSA (SPHINCS+, FIPS 205) as an entity authentication
algorithm for the TLS 1.3 and DTLS 1.3 handshake, following
draft-reddy-tls-slhdsa. All twelve parameter sets (SHAKE and SHA2 families,
128/192/256 in the f and s variants) are wired into the handshake for
signing and verifying the CertificateVerify message; test certificates and
configs cover the 128f and 128s sets.

Handshake integration:
- Map the SLH-DSA signature schemes to and from the wire in the
  signature_algorithms extension and CertificateVerify. The mapping,
  advertisement, and OID handling are gated per parameter set so a build
  only offers, accepts, and maps the variants actually compiled in
  (including partial SHA2 builds).
- Sign and verify the CertificateVerify with an SLH-DSA entity key, and
  load SLH-DSA private keys and certificates (ssl_load.c, ssl.c,
  ssl_api_pk.c, asn.c).
- Preserve the verify return code on a failed SLH-DSA CertificateVerify
  rather than flattening every non-zero result to SIG_VERIFY_E.
  wc_SlhDsaKey_Verify already returns SIG_VERIFY_E on a real mismatch, so
  the failure semantics are unchanged while WC_PENDING_E (async crypto
  callbacks) and hard errors now propagate, matching ML-DSA and Falcon.

Protocol version gating:
- SLH-DSA is defined for TLS 1.3 only, so the schemes are no longer offered
  to a TLS 1.2 peer, and MatchSigAlgo and PickHashSigAlgo pin an SLH-DSA
  certificate both to the scheme for its exact parameter set and to
  TLS 1.3.
- Reject a Falcon, ML-DSA or SLH-DSA key in the TLS 1.2 CertificateVerify
  with SIG_TYPE_E. No signature scheme below TLS 1.3 covers a post-quantum
  key, the record is reserved for a classic signature, and the signing
  switches have no post-quantum case, so continuing would have sent the
  reserved buffer's uninitialized tail.

Streamed CertificateVerify send:
- SLH-DSA signatures are large (up to ~50 KB). When the CertificateVerify
  body exceeds a single record, generate the signature into a
  connection-level buffer and emit it one record at a time so the output
  buffer never has to hold the whole signature. This keeps peak memory near
  one signature plus a single fragment and resumes correctly across a
  non-blocking WANT_WRITE without recomputing the randomized signature.
  Gated by WOLFSSL_TLS13_STREAM_CERT_VERIFY (TLS 1.3, non-async, PQC
  signatures); DTLS and WOLFSSL_ASYNC_CRYPT keep the existing in-place
  fragmented path.
- Drop a half-sent streamed CertificateVerify in wolfSSL_clear. Left in
  place, the resume guard would fire on the next handshake and re-send the
  previous one's signature into a different transcript.
- Dual-algorithm (WOLFSSL_DUAL_ALG_CERTS, BOTH) CertificateVerify bodies are
  streamed as well. The combined two-signature body may include a
  variable-length signature, so the body buffer is sized from the
  per-signature upper bounds and the exact length is recorded after signing;
  the small trailing slack is never sent.

Buffer sizing:
- Keep MAX_X509_SIZE a fixed 9 KB for post-quantum builds. It sizes a
  static per-certificate slot embedded by value in every cached session, so
  it must not scale with a post-quantum signature; nor may it derive from
  the enabled ML-DSA level, or a level-restricted build would silently drop
  certificates that a full build keeps.
- Add MAX_CERT_WIRE_SZ for the largest certificate that may appear in a
  handshake message, sized from the enabled post-quantum signatures, and
  derive MAX_CERTIFICATE_SZ from it instead of from MAX_X509_SIZE.
- Add MAX_CERT_MSG_DEPTH for the chain depth assumed when sizing the
  certificate message. MAX_CHAIN_DEPTH bounds how deep a chain may be
  verified, while this sizes a buffer an unauthenticated peer can make us
  allocate, so it is trimmed to 5 when a post-quantum certificate has
  inflated the per-certificate size. Classic builds are unchanged.
- Size the CertificateVerify buffers from the actual signature length
  instead of the worst-case WC_MAX_CERT_VERIFY_SZ, which balloons with
  SLH-DSA. WC_MAX_CERT_VERIFY_SZ is retained for API compatibility and its
  growth is documented in README.md.
- Order Scv13Args widest member first so it carries no interior padding and
  still fits ssl->async->args under WOLFSSL_ASYNC_CRYPT together with
  WOLFSSL_DUAL_ALG_CERTS.

Dual-algorithm certificates:
- Reserve the two signature length prefixes in the in-place
  CertificateVerify sizing that the streamed path already accounted for.
- Build the PreTBS for an alternative signature check from the certificate
  size minus both signatures, and retry once at a size the canonical
  re-encode cannot exceed when that estimate turns out short. The estimate
  keeps the allocation small on constrained targets, and wc_GeneratePreTBS
  reports an encoder failure as WOLFSSL_FAILURE, which is zero, so a
  non-positive result is now an error instead of silently skipping
  ConfirmSignature and reading as a verified signature.

Device held private keys:
- Support an SLH-DSA private key that lives in a device and is referenced
  by id or label. The parameter set cannot be recovered from a device side
  identifier, so it is carried from the key type down to
  wc_SlhDsaKey_Init_id and wc_SlhDsaKey_Init_label, and the key is released
  with wc_SlhDsaKey_Free once the certificate and key pair is checked.

Robustness:
- Check the SlhDsaParamToType, wc_SlhDsaKey_PublicSizeFromParam and
  wc_SlhDsaParamToOid results in the certificate and key load paths.
- Zeroize an SLH-DSA key before wc_SlhDsaKey_Init, which can return
  NOT_COMPILED_IN before it clears the object, in both the certificate load
  path and AllocKey.
- Take the alternative key's parameter set from the certificate's sapkiOID
  rather than keyOID, which describes the native key.
- Re-initialise across hash families in wc_SlhDsaKey_PublicKeyDecode as
  wc_SlhDsaKey_PrivateKeyDecode already does. The hash objects share a union
  selected by family, so importing across families writes the new family's
  state over the old one's and orphans it.
- Copy pkCurveOID in SetSSL_CTX when only SLH-DSA is enabled, matching the
  struct member guard. Without it the field stayed zero and the signature
  scheme matching above was dead in exactly that build.
- Derive the per parameter set WOLFSSL_SLHDSA_PARAM_NO_* macros from the
  group level exclusions, and select WC_SLHDSA_DEFAULT_PARAM with those
  same macros, so the parameter table and the TLS mappings cannot disagree.
- Add SLH-DSA to the lean build WOLFSSL_MAX_SIGALGO carve-out, since twelve
  more entries no longer fit the small list.
- Prefix the new SLHDSA_ALL_NO_* macros in the installed header with WC_.

Tests and certificates:
- Add SLH-DSA entity (client and server) certificates for the SHAKE and
  SHA2 128f and 128s parameter sets, and update the generation script.
- Add TLS 1.3 and DTLS 1.3 entity-cert CertificateVerify test configs
  covering the fragmented (128f) and single-record (128s) send paths for
  both hash families, wired into suites.c. These sign with the entity key,
  so they are excluded from verify-only builds.
- Interrupt the streamed CertificateVerify with one WANT_WRITE and with
  several on the same record, and assert the handshake still completes and
  re-emits identical bytes, which the blocking .conf handshakes never
  exercise. The record to interrupt is counted first, because the server's
  record batching differs between builds. Where the flight is flushed as a
  single write the send is retried below SendTls13CertificateVerify, so
  these do not by themselves cover the fragOffset resume path.
- Drive the streamed path with an ML-DSA leaf under a negotiated
  max_fragment_length, covering it for a non SLH-DSA algorithm.
- Reject a TLS 1.2 handshake that presents an SLH-DSA client certificate.
- Map every compiled-in scheme from its wire code point to the key OID, and
  extend the exhaustive SaToNid coverage with the twelve new algorithms.
- Accept an SLH-DSA private key referenced by id and by label.

Build configuration:
- configure.ac: --enable-slhdsa now keeps the certificate/ASN code enabled
  (as --enable-mldsa does), so an SLH-DSA-only build with RSA, ECC and DH
  disabled configures instead of erroring that ASN is off.
- Guard the WOLFSSL, WOLFSSL_CTX and WOLFSSL_X509 pkCurveOID members for
  WOLFSSL_HAVE_SLHDSA, so an SLH-DSA-only build declares the field the
  handshake and CopyDecodedToX509 already reference under an SLH-DSA guard.
- Mark checkKeySz used in the SLH-DSA branch of ProcessBufferCertPublicKey;
  SLH-DSA is the only certificate signature algorithm with no minimum-size
  check, so an SLH-DSA-only build otherwise tripped -Wunused-parameter.
- Propagate haveSlhDsaSig in wolfSSL_set_SSL_CTX, which copied the Falcon
  and ML-DSA flags but not the SLH-DSA one.
- CI: add a SHA2-only SLH-DSA build (--enable-slhdsa=sha2) so the
  SHAKE-disabled combined-maxima guards are exercised, and an async crypto
  build with dual-algorithm certificates, which is the only configuration
  that compiles the in-place fragmented CertificateVerify send.
2026-08-04 22:23:03 +02:00
David Garske e100f72548 Merge pull request #10938 from yosuke-wolfssl/fix/f_6767
Avoid writing into caller ikm buffer in wc_Tls13_HKDF_Extract
2026-08-04 10:43:24 -07:00
David Garske 5d5199b01e Merge pull request #10937 from embhorn/zd22160
Fix PKCS7 SignerIdentifier SKID to implicit [0] tagging
2026-08-04 10:33:15 -07:00
David Garske 7e5610debf Merge pull request #10911 from rizlik/der_import_trusted
wolfssl: expose trusted argument in ed25519/ed448 der export
2026-08-04 10:29:42 -07:00
Paul Adelsbach d1548c89c9 Curve25519 crypto cb full 2026-08-03 16:30:31 -07:00
David Garske 47cdd2c3aa Merge pull request #11026 from kojo1/sig_min_hash_test
Honor WC_SIG_MIN_HASH_TYPE override in signature decision test
2026-08-03 11:17:12 -07:00
John Safranek 396a160954 Merge pull request #10984 from embhorn/zd22162
Enforce RFC 5746 renegotiation_info in TLS 1.2 client by default
2026-08-03 10:11:25 -07:00
David Garske 80631786fc Merge pull request #10958 from Frauschi/fenrir
wolfCrypt security hardening, portability fixes, and negative test coverage
2026-08-03 09:13:36 -07:00
David Garske 74ef67f5b0 Merge pull request #11018 from Frauschi/fenrir_3
Security and correctness fixes, plus a DTLS 1.3 scheduled work API
2026-08-03 09:13:21 -07:00
Tobias Frauenschläger 6690e94562 Fix for check-source-text false-positive.
Reword a comment to not trigger a failure in check-source-text CI job
due to an apparently missing WC_NO_ERR_TRACE in a comment.
2026-08-03 16:28:42 +02:00
Tobias Frauenschläger c9b027d054 Merge pull request #11028 from danielinux/mcdc-eliminate-dead-code
Remove dead/unreachable code
2026-08-03 14:48:20 +02:00