1
0
mirror of https://github.com/wolfSSL/wolfssl.git synced 2026-10-24 04:09:07 +03:00

110 Commits

Author SHA1 Message Date
philljj
abeea08c7d Merge pull request #11606 from Frauschi/fenrir
Static memory, session cache, DH reuse and WOLF_C89 print fixes
2026-10-19 11:41:04 -05:00
philljj
25651d0535 Merge pull request #11715 from dgarske/sp_nonblock_fast
Add SP ECC non-blocking support for large implementation
2026-10-16 14:23:04 -05:00
David Garske
fa565101ab mldsa: add WOLFSSL_MLDSA_ALLOW_WEAK_PREHASH for SHA-256-only TPM firmware 2026-10-15 08:26:38 -07:00
Tobias Frauenschläger
e70c246dd5 Build the session-cache-ref CI leg with opensslall
The SSL_SESSION_set1_id and set1_id_context regression test needs
OPENSSL_ALL as well as the client cache reference path. With
NO_SESSION_CACHE_REF now the configure default, the plain
--enable-opensslall legs no longer take the reference path. The
session-cache-ref leg is the only one left on it, and it built with
opensslextra only, so the test skipped everywhere. Adding
--enable-opensslall keeps it running.
2026-10-15 17:13:56 +02:00
philljj
b8db127813 Merge pull request #11746 from Frauschi/bench_hbs_verify_only
Benchmark LMS, XMSS and SLH-DSA in verify-only builds
2026-10-14 16:02:40 -05:00
Emma Stensland
c102b31fde Test the ML-DSA key caches after small-mem key regeneration 2026-10-14 13:48:09 -07:00
Emma Stensland
265e5e5fba Check the ML-DSA inverse NTT input bound in the small-mem UBSan runs 2026-10-14 13:48:09 -07:00
Emma Stensland
9b4d350750 Stop small-mem ML-DSA keygen reducing an unwritten accumulator 2026-10-14 13:48:09 -07:00
Emma Stensland
aa6c5b7e69 Fix ML-DSA small-mem scratch alignment 2026-10-14 13:48:09 -07:00
Tobias Frauenschläger
61528edbd5 Build swdev on macOS
tests/swdev/Makefile localized every swdev symbol except the two entry
points with objcopy --keep-global-symbol, which macOS does not ship. On
Darwin, do the same with a second ld64 relocatable link and
-exported_symbol. The export check strips the Mach-O leading underscore
before comparing. The exported names now live in one SWDEV_EXPORTS list
that feeds both localization commands and the check.

The localization step is picked from configure's host_os rather than
uname, so cross-builds use the target's toolchain.

Add a macOS CI config that builds swdev with WOLF_CRYPTO_CB_ONLY_ED25519,
so make check depends on the ld64-localized swdev object.
2026-10-14 09:05:06 -07:00
philljj
fde1138bda Merge pull request #11367 from julek-wolfssl/chain-verify-cb
Add WOLFSSL_CHAIN_VERIFY_CB to replace peer chain verification
2026-10-14 10:16:45 -05:00
Tobias Frauenschläger
69291355b6 Benchmark SLH-DSA SHA2-128s verify in verify-only builds
The verify-only SLH-DSA benchmark only had a SHAKE-128s vector, because
that is the only verify-only signature in wolfcrypt/test/test.c. Builds
with only the SHA2 parameter sets showed no SLH-DSA row at all.

Add a SHA2-128s vector: a deterministic, empty-context signature of
"Hello World!" by the existing bench_slhdsa_sha2_128s_key.der. It was
generated with OpenSSL 4.0.3; wolfSSL verifies it, rejects it once a
message bit is flipped, and its own deterministic signature from the
same key is byte-identical. gen-slhdsa-mldsa-certs.sh gains the step
that produces it, and a rerun reproduces the committed files. The pq-all
verify-only benchmark config enables sha2 and requires this row too.

bench_slhdsa_verify() now takes the parameter set and vector. The row
uses the full benchmark's name, SLH-DSA-SHA2-S, and takes its strength
from the public key size, as the full benchmark does. Verify-only
builds accept -slhdsa-sha2-128s when SHA2-128s is built.

Tested on the host with --enable-slhdsa=yes,sha2,verify-only on its
own, with --enable-smallstack and with --disable-rng, with sha2 only,
with SHAKE only and 192f only (no SHA2 row, no warnings), and with the
full sha2 build.
2026-10-14 10:59:13 +02:00
Tobias Frauenschläger
cfdf7bb831 Benchmark LMS, XMSS and SLH-DSA verify in verify-only builds
The benchmark measured LMS, XMSS and SLH-DSA verify only after
generating a key and signing with it, so a build with
WOLFSSL_LMS_VERIFY_ONLY, WOLFSSL_XMSS_VERIFY_ONLY or
WOLFSSL_SLHDSA_VERIFY_ONLY compiled the algorithm out of the benchmark
entirely. Targets that only check signatures, such as a boot loader
verifying an image, got no numbers at all.

Verify-only builds now time verify against a fixed vector: LMS
L1_H10_W8, XMSS-SHA2_10_256 and SLH-DSA-SHAKE-128s. The vectors are the
verify-only KATs already in wolfcrypt/test/test.c, stored as binary
files under certs/lms, certs/xmss and certs/slhdsa and emitted into
certs_test.h by gencertbuf.pl. The rows use the same names as the full
benchmark, and a parameter set without a vector, or one that is not
compiled in, is skipped. If loading the key fails, the failing call is
printed and no row is shown.

In a verify-only build, -lms_hss, -xmss_xmssmt(_sha256),
-slhdsa-shake128s and -slhdsa are accepted only when their vector
applies, and the other XMSS and SLH-DSA parameter options are rejected,
so no option silently runs nothing.

Verify needs no RNG, so the asymmetric result printing is built
whenever one of these vectors applies, also with WC_NO_RNG. A
verify-only build with no vector no longer counts as an asymmetric
algorithm. This also fixes an LMS verify-only build with no other
asymmetric algorithm, which failed -Werror on master with an unused
bench_stats_asym_finish(). The SRTP-KDF, scrypt and Argon2 benchmarks
also call it, so it is now built for them too: on master each of them
failed to compile with no asymmetric algorithm, which an LMS
verify-only build used to hide.

The benchmark exits 0 even when verify fails, so a new pq-all CI config
builds all three verify-only with no other asymmetric algorithm and
-Werror=unused-function, runs the benchmark, and fails on any reported
benchmark error, such as a key that does not import, or on a missing
verify row.

Tested on the host with --enable-{lms,xmss,slhdsa}=yes,verify-only on
its own, with --enable-smallstack, with --disable-rng, and with
--enable-all-crypto; with edge configurations where no vector applies
(SLH-DSA 192f only, LMS max height 5, XMSS min hash size 512), both
with --disable-rng and with RNG but no other asymmetric algorithm; with
SRTP-KDF, scrypt or Argon2 as the only such user, with and without an
RNG and with LMS verify-only; and
with --enable-all and the full LMS/XMSS/SLH-DSA build, whose benchmark
output is unchanged. The new CI config passes under
parallel-make-check.py and fails with a corrupted signature byte, a
truncated public key, a missing row and an unused static function.
2026-10-14 10:59:13 +02:00
David Garske
2f7c94fe7e sp: support ECC non-blocking for the large implementation with tables and windowing 2026-10-13 21:08:35 -07:00
night1rider
43a1f274b2 tests: cover the AES Direct crypto callback path, and build it in CI
Decline, fail, device result, device-owned key, no key, no device and find modes; aesofb in two cryptocb CI builds.
2026-10-13 16:13:51 -07:00
Juliusz Sosinowicz
b4e77e8951 Document what accepting means and re-check the callback's restrictions
Documentation:
- wolfSSL_get_verify_result() reports WOLFSSL_X509_V_OK once the callback
  accepts. wolfSSL verified nothing, so say that this records the
  callback's verdict rather than a check wolfSSL made.
- The OCSP stapling restriction is client-side. Both guards test
  side != WOLFSSL_SERVER_END: a server staples its own status, which is not
  part of verifying the peer. A server that asks the client to staple is
  not refused, and no stapled response is checked for it either, because
  ProcessPeerCertLeafRevocation is skipped with the callback.
- The list of what still applies was neither "two" nor complete. Add
  PEER_KEY_ERROR and SCR_DIFFERENT_CERT_E, note MAX_CERTIFICATE_SZ next to
  MAX_CHAIN_DEPTH, and state that 0 is the only accepting value, so
  returning WOLFSSL_SUCCESS rejects.
- DoChainVerifyCb no longer allocates; drop MEMORY_E from its comment.

Code:
- NULL-check the certificate manager in ChainVerifyCbCheckSsl, as
  ChainVerifyCbCheckCtx already does for ctx->cm.
- Re-run ChainVerifyCbCheckSsl in TLS_ASYNC_BUILD. A deferred verdict
  resumes there, so TLS_ASYNC_BEGIN ran on the first pass only.

Tests:
- Pin both halves of the headline contract on the accept path: the verify
  callback set with wolfSSL_set_verify() is not called, and
  wolfSSL_get_verify_result() reports WOLFSSL_X509_V_OK.
- Cover a callback returning WOLFSSL_SUCCESS, which rejects.
- Rename test_tls13_chain_verify_cb_async to _defer. It exercises deferral
  over TLS 1.3, not async crypto.

CI:
- Add chain-verify-cb-async, the first build combining the callback with
  WOLFSSL_ASYNC_CRYPT, and the only one with async crypt and CRL but
  without WOLFSSL_NONBLOCK_OCSP.

Three behaviour changes to existing configurations, introduced earlier in
this series and not called out until now:
- IsHsSuspendErr() recognizes OCSP_WANT_READ only under
  WOLFSSL_NONBLOCK_OCSP, where the sites it replaced tested
  WOLFSSL_ASYNC_CRYPT || WOLFSSL_NONBLOCK_OCSP. The keep-state re-entry
  branch it replaced was itself under WOLFSSL_NONBLOCK_OCSP, so an
  async-only build rewound into reset args and a freed pendingMsg. It now
  fails cleanly instead.
- DoCertificate no longer advances serverState to SERVER_CERT_COMPLETE
  when ProcessPeerCerts suspended.
- ReceiveData admits a suspended handshake error rather than refusing the
  read, which under WOLFSSL_NONBLOCK_OCSP now includes OCSP_WANT_READ.
2026-10-13 05:25:00 +00:00
Juliusz Sosinowicz
797e34a271 Add WOLFSSL_CHAIN_VERIFY_CB to replace peer chain verification
Adds an opt-in callback that takes over the peer certificate trust
decision. wolfSSL builds no chain, verifies no signature and checks no
date, revocation status, key usage or host name; the verify callback is
not called either. The callback gets only the DER certificates from the
Certificate message as WOLFSSL_BUFFER_INFO entries, certs[0] first. It
is consulted even under WOLFSSL_VERIFY_NONE, and its rejection fails the
handshake either way.

wolfSSL still decodes every certificate with ParseCert(NO_VERIFY) before
the callback runs, so malformed DER fails the handshake and the callback
is never handed it. Content wolfSSL does not understand - an unknown
critical extension, an unsupported key or signature algorithm, a
signature algorithm mismatch - is not a decoding failure and is left for
the callback to judge. The check runs once, not again on re-entry after
a deferred verdict.

The callback may return CHAIN_VERIFY_WANT_E to suspend the handshake and
be asked again with the same certificates when the application re-enters
wolfSSL_connect(), wolfSSL_accept(), wolfSSL_read() or wolfSSL_write().
A certificate received after the handshake (TLS 1.3 post-handshake
authentication) arrives inside wolfSSL_read() and is resumed by reading
again: ReceiveData() re-enters on any handshake-suspend error, and the
ticket-sent accept state leaves a suspended message alone. Rejection
fails with CHAIN_VERIFY_CB_E and one bad_certificate alert, and sets
peerVerifyRet so wolfSSL_get_verify_result() does not report X509_V_OK.

Still applied: an empty Certificate message keeps the existing
mutual-auth policy (the callback is not called for it), and the minimum
peer key sizes still gate the peer's own key, which the handshake uses
directly.

DTLS, raw public keys and OCSP stapling are not supported with the
callback. wolfSSL_CTX_SetChainVerifyCb() and wolfSSL_SetChainVerifyCb()
refuse with CHAIN_VERIFY_UNSUPPORTED_E when the context or object is
already configured for one of them, and a connection that uses one of
them anyway fails with the same error and an internal_error alert before
the callback is called. The DTLS paths are otherwise untouched.

The callback and its user context can be set on the context or on the
SSL object, the object's taking precedence. The feature is part of
--enable-all, has a CMake option and Doxygen entries.

The suspend machinery is shared with WOLFSSL_NONBLOCK_OCSP and
WOLFSSL_ASYNC_CRYPT. The open-coded pairs of comparisons against
WC_PENDING_E and OCSP_WANT_READ are replaced by IsHsSuspendErr(), and the
matching build guards by WOLFSSL_HAVE_HS_SUSPEND. That also closes three
holes which affected WOLFSSL_NONBLOCK_OCSP builds:

 - DoHandShakeMsg() freed ssl->pendingMsg on a non-WC_PENDING_E suspend
   while ProcPeerCertArgs still pointed into it.
 - wolfSSL_connect()/wolfSSL_accept() and their TLS 1.3 counterparts
   called FreeAsyncCtx() when buffered output flushed, discarding the
   saved certificate state.
 - ProcessPeerCerts() XFREE'd args in WOLFSSL_SMALL_STACK builds without
   WOLFSSL_ASYNC_CRYPT, where args points inside ssl->async.
2026-10-13 05:24:59 +00:00
Tobias Frauenschläger
229051446c Keep the client identity for post-handshake auth
FreeHandshakeResources() unloads SSL-owned certificates and keys once
the handshake is done. The side check that limits this to the server
was compiled out when WOLFSSL_POST_HANDSHAKE_AUTH is defined, so a PHA
build unloaded the client's identity too, which is the side that needs
it to answer a post-handshake CertificateRequest. The client then sent
an empty Certificate and the server failed with NO_PEER_CERT, or
carried on unauthenticated when it did not require a certificate.

This hits TLS 1.3 clients that load their certificate and key on the
WOLFSSL object in builds with post-handshake auth but without
OPENSSL_EXTRA or WOLFSSL_WPAS_SMALL, both of which compile the unload
out. Identities loaded on the CTX are not owned by the SSL and were
never unloaded. A client that authenticated during the handshake was
hit as well on the first post-handshake request.

Unload on the server only, in every configuration.

test_tls13_pha stopped at wolfSSL_request_certificate() and never ran
the exchange. It now drives two post-handshake rounds to completion,
after an initial handshake with and without client auth, with the
client identity on the SSL. Both cases fail with NO_PEER_CERT without
the fix in a build with --enable-postauth and without OPENSSL_EXTRA.

Add a postauth-no-compat entry to the Linux os-check matrix, so a
GitHub Actions build runs the test with the unload compiled in;
--enable-all brings OPENSSL_EXTRA along and compiles it out.
2026-10-12 20:16:40 +02:00
Sean Parkinson
39331d5c51 Configuration options: add --enable-enterprise
Add an enterprise option that includes all items that would be required.

Update CMake build to match autotools. Added enterprise option.

Added CI loops for configuration options.

Add document that maps the OpenSSL configuration options to wolfSSL's.
Checked against various versions of OpenSSL.
2026-10-08 18:22:27 +10:00
philljj
799bd17483 Merge pull request #11654 from dgarske/mldsa_small_mem
Fix two problems with ML-DSA small-memory builds
2026-10-07 20:54:16 -05:00
David Garske
8639f5259c Fix the ML-DSA benchmark verifying a baked signature against the wrong key 2026-10-07 07:50:20 -07:00
David Garske
c55f09c545 sp: fix ECC non-blocking build with the SP assembly implementations 2026-10-07 06:58:53 -07:00
philljj
e09a10ecd5 Merge pull request #11625 from dgarske/cryptonly_nomalloc
wolfCrypt: support zero-malloc cert encoding and clean up CMake/platform defines
2026-10-07 08:38:22 -05:00
Tobias Frauenschläger
c021c04c48 Drop the ML-DSA key caches when small memory key generation replaces a key
The default key generation clears aSet, privVecsSet and pubVecSet on
success, but the small memory arm never did, so a key generated into an
object that had imported or used another key kept that key's matrix A and
vectors, and its signatures failed to verify. The full-vector verifier has
the same exposure on master; reading the cached public vector in the small
memory verify widened it.

Turn the signing KAT into a key reuse test: generate the KAT key into a
fresh object and into one that already signed and verified with a
different key, and require both to produce the same signature and to
verify it. A sign and verify on the reused object alone does not catch
this, because the signer and the verifier read the same stale caches and
agree. The comparison needs no known answer, so it also runs for the draft
and CHECK_Y/CHECK_W0 signers, which only skip the digest check.

Add pq-all rows for cached matrix A on the default signer, the only build
where the cache allocation test's assertions are compiled, and for every
cache with the small memory key generation, the only configuration where
the reuse test reaches the fix.
2026-10-06 16:07:38 +02:00
Tobias Frauenschläger
dc95f4f2b4 Harden the ML-DSA signing range checks
Signing retries until a candidate passes four infinity norm checks.
mldsa_check_low() examines every coefficient of a polynomial, but the signer
stops at the first polynomial that fails. For z and w0-cs2 that reveals
nothing: both are masked by the uniform vector y, and the count of y values
that push a coefficient out of range is 2*beta+1 whatever the secret
contributes, so every polynomial rejects with the same probability. The
reference implementation relies on the same argument. Those loops keep
their early exit.

ct0 is different: it is c*t0 with no mask, so the index of a failing
polynomial does depend on t0. Check every polynomial of ct0 rather than
stopping at the first failure. This costs nothing when the check passes,
since every polynomial is then checked anyway. The smallest and small
memory signers compute ct0 in the same row loop as w0-cs2, so they
accumulate the ct0 result in a flag of its own: the loop still stops at
the first rejecting w0-cs2 row, but a failing ct0 row no longer ends it.
The remaining hints are only cleared for a valid attempt, as a rejected
one can leave more hints than omega.

The optional WOLFSSL_MLDSA_SIGN_CHECK_Y and WOLFSSL_MLDSA_SIGN_CHECK_W0
checks run on values derived from the secret mask. Accumulate them in both
small memory signers instead of leaving the loop on the first rejecting
polynomial, so the amount of matrix A generated does not depend on which
one failed. The small memory signer's row pass still skips a round whose y
was rejected, as its columns were never accumulated, and it now checks w0
a polynomial at a time, so the mldsa_vec_check_low() guard narrows to the
y check.

Cover the range checks in the ML-DSA white-box test: both boundaries of
the accepted range, and a bad coefficient in the last position of a
polynomial and in the last polynomial of a vector, which only fail because
neither helper exits early. Add pq-all rows for the checks on the default
signer and for the w0 check alone on the small memory signer.
2026-10-06 16:07:38 +02:00
Tobias Frauenschläger
9f7e700922 Fix ML-DSA option derivation and build gates
Derive the canonical option implications in wc_mldsa.h, straight after its
include of dilithium.h, and unconditionally: SIGN_SMALLEST_MEM, PRECALC and
PRECALC_A imply WOLFSSL_MLDSA_SIGN_SMALL_MEM, and VERIFY_SMALLEST_MEM
implies WOLFSSL_MLDSA_VERIFY_SMALL_MEM. They lived in the legacy name gate
region of dilithium.h, so WOLFSSL_NO_DILITHIUM_LEGACY_GATES silently gave
those builds the full memory signer, and the PRECALC pair lived in
wc_mldsa.c, where other translation units did not see it. Every route to
the key structure passes this point, so the key layout no longer depends
on include order, and dilithium.h now reads the build configuration before
deriving anything. The tests see the implications too, so the matrix A
cache test no longer asserts a cache that a PRECALC_A build's small memory
signer never fills. Reject a PRECALC_A value below one, including one
defined without a value.

Derive WC_ENABLE_ASYM_KEY_IMPORT and WC_ENABLE_ASYM_KEY_EXPORT from
WOLFSSL_HAVE_MLDSA rather than the legacy HAVE_DILITHIUM, so an ML-DSA
build that opts out of the legacy gates still gets the RFC 5958 helpers it
calls. settings.h maps HAVE_DILITHIUM onto WOLFSSL_HAVE_MLDSA before either
use, so the canonical name covers both spellings.

--enable-mldsa=<level> named only a parameter set and turned key
generation, signing and verification all off, so nothing was built. Each
operation now defaults on unless one of them is named.

WOLFSSL_MLDSA_NO_CHECK_KEY did not compile: wc_CheckPrivateKey() called
wc_MlDsaKey_CheckKey() unguarded while the function is compiled out. Return
NOT_COMPILED_IN for ML-DSA there, as the RSA arm does. Such a build cannot
confirm that a certificate and key match, so the *_check_private_key()
calls report a mismatch; test_wolfSSL_X509_check_private_key_mldsa now
builds in that configuration and expects it.

Several whole-vector helpers are only called by the full-vector key
generation but their guards still admitted
WOLFSSL_MLDSA_MAKE_KEY_SMALL_MEM, so MAKE_KEY_SMALL_MEM with NO_CHECK_KEY
compiled static functions with no references. Give them the exclusion
their siblings have.

Add pq-all rows for the legacy gate opt-out, a heapless verify-only build
named only by the canonical options, cached matrix A with PRECALC_A,
sign-only builds, and NO_CHECK_KEY with and without the small memory key
generation.
2026-10-06 16:07:38 +02:00
Tobias Frauenschläger
198b09a093 Transform each polynomial of y once when signing with small memory
Small memory signing walked matrix A a row at a time and copied and
transformed the polynomial of y it needed for every element of the row.
Each polynomial of y was therefore transformed k times with an identical
result: 156 transforms per ML-DSA-65 signature where 31 are needed.

Walk the matrix a column at a time instead. Every row of the matrix uses
the same column of y, so the transform is done once and the products are
accumulated down the column into w0, which is already a vector of k
polynomials. Inverting, decomposing and encoding then run as a second
pass over the rows, with w0 replaced in place and the scratch polynomial
holding w1. w is reduced before the inverse transform on both column
accumulating paths.

Vector y stays resident, so unlike the smallest memory implementation
this does not have to regenerate it, and it keeps generating y as a whole
vector so the assembly generators are still used.

With WOLFSSL_MLDSA_SIGN_SMALL_MEM_PRECALC_A the pre-calculated rows are
multiplied as a vector, and w is sized for all k rows: the ML-DSA-44
decompose kernels take no dimension and touch every row, so the rows above
the pre-calculated ones are cleared, and mldsa_vec_encode_w1() now honours
the dimension it is given. The whole-vector helpers that path drives are
gated on a new MLDSA_SIGN_VEC_HELPERS, which names the set of callers.
Under WC_MLDSA_FAULT_HARDEN the column loop checks that the polynomial of
y it reads, walked independently of the column index, lies inside y.

Memory is unchanged: w0 was already held for the hints and the row
accumulator becomes the scratch the transform runs in.
WOLFSSL_MLDSA_SMALL_MEM_POLY64 no longer applies to signing as a 64-bit
accumulator would now be needed for every row of w, which also returns
the 2KB it was allocating. The option now only affects small memory key
generation and verification; the option list says so.

Signatures are unchanged. Signing is 12 to 29 percent quicker with the C
code and 6 to 12 percent quicker with AVX-512, which makes it faster than
the smallest memory implementation on every target rather than only where
the assembly runs. Add pq-all rows for PRECALC_A split part way, saturated
past k, and on the Intel assembly paths, and a -Wconversion row.
2026-10-06 16:07:38 +02:00
Tobias Frauenschläger
d899ad8dc9 Separate ML-DSA smallest memory verify from pinned buffers
WOLFSSL_MLDSA_VERIFY_SMALLEST_MEM defined WOLFSSL_MLDSA_VERIFY_NO_MALLOC,
so streaming vector z a polynomial at a time was only available with the
verify buffers pinned against the key for the life of the key. The two are
independent, so let them be selected independently. The pinned buffers are
still available by defining WOLFSSL_MLDSA_VERIFY_NO_MALLOC as well.

Peak verify heap with allocated buffers, in bytes:

               small mem   smallest mem
  ML-DSA-44         8776           5704
  ML-DSA-65         9800           5704
  ML-DSA-87        12104           5960

sizeof(wc_MlDsaKey) is 7928 bytes either way; with the buffers pinned
instead it is 20168 bytes for the small memory verify and 14024 bytes for
the smallest memory verify.

Make WOLFSSL_NO_MALLOC select the small memory verify along with
WOLFSSL_MLDSA_VERIFY_NO_MALLOC. The pinned verify buffers only exist under
the small memory verify, so the no-malloc option alone selected nothing and
every verification failed with MEMORY_E. It applies only where
verification is compiled in, and WOLFSSL_MLDSA_VERIFY_ALLOW_MALLOC keeps
the allocating verify for a build that still has an allocator.

Document WOLFSSL_MLDSA_VERIFY_SMALLEST_MEM and
WOLFSSL_MLDSA_VERIFY_ALLOW_MALLOC in the option list, and add pq-all rows
for the smallest memory verify with allocated buffers and with the default
signer.
2026-10-06 16:07:38 +02:00
Tobias Frauenschläger
8531d630a9 Fix ML-DSA cached vector build and test failures
Two combinations of the ML-DSA cache options did not work.

WC_MLDSA_CACHE_PUB_VECTORS declares a cached t1 vector in wc_MlDsaKey and
WOLFSSL_MLDSA_VERIFY_NO_MALLOC declares a pinned t1 scratch polynomial in
the same structure, so enabling both failed to compile with a duplicate
member. Rename the pinned scratch to vt1, and widen the mldsa_vec_decode_t1
guard so the cache builds alongside the small memory verify.

Read the cached public vector in the small memory verify. Nothing consumed
the cache there: every public key import paid an allocation and a full
decode and transform of t1 that was then discarded and redone a polynomial
at a time. The cached vector is already in the form the row loop wants, so
the multiply reads it in place.

The matrix A cache regression test asserts that signing populates key->a.
That holds for the full signing implementation, which is what the test was
written for, but the small memory implementations stream matrix A rather
than caching it, so they leave key->a as NULL and the test failed. Check
the cache only when small memory signing is off and keep the round trip
for the others. WC_MLDSA_CACHE_PRIV_VECTORS enables WC_MLDSA_CACHE_MATRIX_A,
so it saw the same failure. Add a pq-all row for the cached public vector
with the pinned verify buffers.
2026-10-06 16:07:38 +02:00
Tobias Frauenschläger
487f673556 Reduce ML-DSA small memory heap use and add a smallest memory signer
Small memory signing kept vector w1 as k full polynomials although it is
only ever consumed in its encoded form, once by the commit hash and once
by MakeHint, which only tests each coefficient for zero. Keep the encoded
w1 and build it a polynomial at a time from a single scratch polynomial
of w. This also removes the separate w1 encode buffer that was allocated
and freed on every rejection attempt.

Add WOLFSSL_MLDSA_SIGN_SMALLEST_MEM. It generates matrix A a column at a
time so that one polynomial of y is held instead of the whole vector and
is transformed once rather than once per row, decomposes w into w0 in
place, and regenerates y for the z calculation. It cannot be combined with
the PRECALC options, and WOLFSSL_MLDSA_SMALL_MEM_POLY64 has no effect on
signing in this mode. The z polynomial, which is not live at either
expansion, serves as the mask expansion scratch, so the signer makes one
allocation per signature.

Because y is derived twice per rejection round, bind the two derivations:
each polynomial of y is checksummed when it is first expanded and checked
when it is regenerated for z, and a mismatch fails with BAD_COND_E rather
than producing z from a different mask than the commitment used. The
checksum is a pair of Fletcher style sums, which catch any change to one
or two coefficients of a y polynomial without multiplying the secret
coefficients, as a multiply chain is not constant time on every core. The
checksums are zeroized on return. The key's SHAKE object, whose last use
regenerates a polynomial of y and would otherwise leave rho'' recoverable
from the key, is re-initialized, which clears its state but keeps the hash
type a crypto callback free needs.

Small memory key generation held t as a full vector for one final vector
encode. Encode t a polynomial at a time and overlay t and the single
decoded s2 polynomial on the s2 vector, which is dead once s2 has been
encoded into the private key.

Peak heap, in bytes, measured with a counting allocator:

                  sign before   sign after   smallest   keygen before   after
  ML-DSA-44            16200        13896       9800           14152   10056
  ML-DSA-65            21320        16968      11848           19272   13128
  ML-DSA-87            27464        21320      14152           25416   17224

Signatures are unchanged. Add a signing KAT to testwolfcrypt: signing is
deterministic given the key seed and the signing seed, so every signer must
produce the same bytes, and the test compares a SHAKE-256 digest of the
signature with the default signer's. tests/api holds full signing KATs,
but unit.test is not built in the --enable-cryptonly CI rows that cover
the small and smallest memory signers.

Problems found while making these changes are fixed here too:

- mldsa_vec_expand_mask dispatched to AVX2 generators that only handle
  dimensions 4, 5 and 7, and silently left y untouched for any other
  dimension. Test the dimension before saving the vector registers and
  fall through to the C implementation otherwise.
- WOLFSSL_MLDSA_SIGN_SMALL_MEM_PRECALC_A greater than k indexed A, w0
  and w1 past their allocations, as the loops used the raw macro rather
  than min(macro, k).
- WOLFSSL_MLDSA_SIGN_CHECK_W0 passed two arguments to the three argument
  mldsa_vec_check_low, so the option never compiled with small memory
  signing.
- WOLFSSL_MLDSA_VERIFY_SMALLEST_MEM did not build alongside small memory
  signing, as mldsa_vec_ntt_full and mldsa_vec_check_low were unused.
- WOLFSSL_MLDSA_VERIFY_SMALLEST_MEM sized z at l polynomials while only
  ever using one.

Add pq-all and -Wconversion CI rows for the smallest memory signer, with
and without the y and w0 checks and verification.
2026-10-06 16:07:38 +02:00
philljj
338180e65f Merge pull request #11519 from Frauschi/fixes
Deprecate the `wolfSSL_get_session()` session cache reference
2026-10-06 01:23:37 -05:00
David Garske
910b7fb7ad Size key identifier buffers from the hash the build actually uses 2026-10-05 00:09:44 -07:00
David Garske
f04673ac94 cryptocb: add ML-DSA keygen, sign and verify test coverage 2026-10-02 18:29:19 -07:00
philljj
c07e10c40d Merge pull request #11095 from rizlik/dtls13_export
(d)tls13: implement session export feature
2026-10-02 15:10:17 -05:00
Marco Oliverio
7e1409350e dtls13 export: address review comments
- don't serialize cid when no negotiated
- abort export w/ post HS in flight
- abort export before the handshake is done
- abort export of a dup'ed session
- export the KeyUpdate response, resumption secret and ticket nonce
- doc/comments/tests improvements
2026-10-02 11:07:17 +00:00
Marco Oliverio
7379d804ae dtls13: support session export and import
wolfSSL_dtls_export()/wolfSSL_dtls_import() only ever carried a DTLS 1.2
session. A DTLS 1.3 connection additionally keeps its epochs, each with
its own traffic secret, record keys, sequence numbers and replay window,
and none of that was serialized.

Export version 7 appends the DTLS 1.3 record layer state and the
Connection ID as two trailing, length-prefixed sections, so version 6
blobs still import.

Refusals, so a blob never describes a connection that cannot be
restored: a KeyUpdate in progress, a half sent fragmented message, work
parked in a write dup, and state only export/import of a DTLS 1.3
session (state only blobs stay stamped export version 6).

CI: build the session export job with Connection ID support.
2026-10-02 11:07:17 +00:00
Paul Adelsbach
57096053c0 Add crypto callback for SHAKE absorb and squeeze ops 2026-10-01 09:48:22 -07:00
Tobias Frauenschläger
70090dcb38 Make the deprecated wolfSSL_get_session() cache reference opt-in
Without NO_SESSION_CACHE_REF, wolfSSL_get_session() returns a reference into
the process-global client cache rather than a session object. That reference
is valid only while the slot it names still holds the session it was issued
for, which the caller cannot observe, so the safe use of the API is the one
the macro selects. Only the integration recipes defined it, and none of them
for that reason: --enable-opensslextra and --enable-opensslall, which is what
an application written against SSL_get_session() reaches for, left it unset.

settings.h now defines NO_SESSION_CACHE_REF unless WOLFSSL_SESSION_CACHE_REF
is defined, so every build selects it, including the user_settings.h, IDE and
bare-metal builds that have no configure step. Putting the rule in the header
rather than only in configure and CMake means no project has to add a macro to
keep the safe behaviour, and the ones that want the deprecated reference ask
for it instead. --enable-session-cache-ref and -DWOLFSSL_SESSION_CACHE_REF=yes
define that opt-in macro, and options.h records it alongside the negative one
so an application cannot disagree with the library it links.

A build that opts in warns at compile time and the warning names
WOLFSSL_SESSION_CACHE_REF_WARNED, which silences it while keeping the path. It
sits inside the NO_SESSION_CACHE region, so a build without a session cache
and a wolfCrypt-only build stay quiet, and it takes the #warning / #pragma
message form already used for WOLFSSL_KEYLOG_EXPORT_WARNED.

The recipes that define NO_SESSION_CACHE_REF on their own keep doing so and
still win over the option. Both build systems say so rather than leaving a
reported option disagreeing with the build: configure warns and clears its
summary line, CMake warns and syncs the cached option. configure also treats
TITAN_SESSION_CACHE as a conflict, because settings.h derives the macro from
it and that never reaches the compile line for a flag scan to find.

The os-check session-cache-ref entry existed because --enable-opensslextra
left the macro unset. It now asks for the reference path explicitly, or
test_wolfSSL_client_cache_id_overwrite would skip and the generation check
added with it would be covered nowhere.

Anything migrating has one lifetime difference to mind: the deprecated
reference stayed valid after wolfSSL_free(), the session object does not, so
code holding the result of wolfSSL_get_session() past the WOLFSSL it came from
has to move to wolfSSL_get1_session() and wolfSSL_SESSION_free().
2026-09-30 08:33:30 +02:00
Paul Adelsbach
5d5fab72d7 CI: smoke test improvements: new configs, CFLAGS fix 2026-09-25 14:34:04 -07:00
JacobBarthelmeh
852b57f5b6 Merge pull request #11210 from kaleb-himes/DRBG-Threading
drbg: add thread-safe option, --disable-rng-autolock and --disable-rng-autofork to opt out
2026-09-21 14:48:10 -06:00
JacobBarthelmeh
e56a7c8f14 Merge pull request #11500 from Frauschi/fixes
Certificate verification and session cache hardening
2026-09-21 10:23:59 -06:00
Tobias Frauenschläger
2067a14924 Bind a client session cache reference to the entry contents it was issued for
Without NO_SESSION_CACHE_REF, wolfSSL_get_session() hands back a ClientSession
reference into the process-global cache rather than a session object, and
ClientSessionToSession() resolves it against nothing but a hash of the entry's
session ID. That ID does not change when the entry is overwritten, so a
reference kept across an overwrite still resolved, to contents it was never
issued for.

Give every cache write a generation number and record it in the ClientSession,
so a reference resolves only while the entry still holds the contents it was
issued for. The numbers come from a counter on the SessionRow rather than from
the entry, so that under SESSION_CACHE_DYNAMIC_MEM a freed and reallocated
entry cannot restart at a value an outstanding reference still holds.

The generation is not advanced when an overwrite carries the same master
secret, the benign re-add of an unchanged session; without that an older
reference would silently fall back to a full handshake. The comparison runs
through ConstantCompare so its timing says nothing about the cached secret.

WOLFSSL_CACHE_VERSION goes to 3: the ClientCache, the SessionRow prefix and
the WOLFSSL_SESSION layouts written by the session-cache save functions all
change.

The test builds the sequence out of two real handshakes on separate contexts,
behind two controls so it cannot pass by refusing everything. Every profile in
the os-check matrix that builds the unit tests defines NO_SESSION_CACHE_REF,
which compiles the reference path out, so a session-cache-ref entry configures
one that does not.
2026-09-19 16:37:57 +02:00
Tobias Frauenschläger
c31f2d8e2d Report a small-verify signature failure ahead of the parse errors that follow it
Under WOLFSSL_SMALL_CERT_VERIFY, ProcessPeerCertParse() merged sigRet back
only when the parse returned 0, so any parse error hid the signature result.

ParseCertRelative(VERIFY) runs its checks in a fixed order: decode, signer
lookup, ConfirmSignature(), name constraints, validity dates, critical
extensions. That order is what makes overriding date errors a sound policy
for a device with no real-time clock, since a date error can only reach the
application for a certificate whose signature already verified. Splitting the
signature check out inverted it.

Let a non-zero sigRet take precedence over exactly those five errors that
ParseCertRelative() only reaches after the signature has been confirmed.
Errors raised before that point keep priority as they do in a full verify, a
parse error is still reported unchanged when the signature was good, and the
raw public key exemption for the leaf is unaffected.

The test builds a leaf naming the 2048-bit test root as its issuer, signed
either by the root or by another key, in and out of its validity window, and
runs a memio handshake with a date tolerant callback. It anchors the expected
result to what wc_CheckCertSignature() makes of each leaf.
2026-09-19 16:37:57 +02:00
kaleb-himes
5c0105b16c Rename the RNG lock configure options to rng-autolock and rng-autofork 2026-09-18 16:37:36 -06:00
kaleb-himes
e34398e651 Keep HAVE_GETPID out of CMake user_settings builds and scan EXTRA_CFLAGS 2026-09-18 16:37:34 -06:00
kaleb-himes
9706e05db2 Tighten the fork handler edges skoll flagged and add a single-threaded CI build 2026-09-18 16:37:33 -06:00
kaleb-himes
8eacfc67c5 Add a per-instance WC_RNG lock and POSIX-conforming fork handlers 2026-09-18 16:37:33 -06:00
Aidan Garske
8dda9e1c5f Fix custom RNG initialization cleanup 2026-09-18 14:25:43 -07:00
JacobBarthelmeh
31b9da2c8d Merge pull request #10594 from kareem-wolfssl/zd21873
Fix a few TLS compliance issues
2026-09-17 16:39:10 -06:00
JacobBarthelmeh
fad92fa095 Merge pull request #11399 from night1rider/ecies-dev-id-change
Ecies dev id change
2026-09-15 14:31:49 -06:00