With WOLFSSL_SNIFFER defined, testsuite's simple test and echoserver
and the unit test servers in api.c skipped port 0 and listened on the
fixed 11111. The exception dates from when the testsuite traffic was
sniffed live; the sniffer tests now replay pcap captures and
scripts/sniffer-gen.sh drives the example client and server itself, so
nothing relies on it.
tcp_listen() sets SO_REUSEPORT, so a second process of the same user
can listen on 11111 at the same time and the kernel hands it some of our
client's connections. testsuite.test never runs under bwrap, and on a
shared CI node this shows up as "wolfSSL_connect error -308" right after
"listening on port 11111". Reproduced with a decoy SO_REUSEPORT listener
on 11111: testsuite fails 5 of 5 runs before and passes 5 of 5 after.
The -B throughput loop waited once for DEFAULT_TIMEOUT_SEC (2 s) and,
on timeout, skipped the read but still compared the buffers. Later
chunks passed by accident because tx_buffer is reused and rx_buffer
held the previous echo, but a late first echo compared against an
unfilled rx_buffer and failed with "Compare TX and RX buffers failed".
This shows up as a flaky DTLS-MTU suite case under CI load.
Retry the select on timeout for up to 5 periods, and fail with an
explicit timeout or select error instead of comparing unread data.
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.
On macOS, gethostbyname() returns a struct hostent whose h_addr_list array is
not aligned for a pointer, so reading h_addr_list[0] as a char * is undefined
behavior. Under -fsanitize=undefined with -fno-sanitize-recover, as in the
ubsan-mldsa-mlkem pq-all row, build_addr() in wolfssl/test.h aborts the first
test that resolves localhost, and unit.test fails. The TLS benchmark client
does the same load. glibc aligns the array, so Linux CI does not see it.
The library does the same in its gethostbyname() fallbacks, which a build
without HAVE_GETADDRINFO uses: wolfIO_TcpConnect() and the OCSP responder
screening in wolfIO_OcspDestAllowed(). Configured that way on macOS, both
abort under UBSan.
Copy the pointer out with XMEMCPY before using it in all four places. A plain
assignment would be the same misaligned load; memcpy reads the bytes without
an alignment requirement.
Fix max MTU logic in added test
Fix client and server SCTP argument. They should not be setting UDP. This was causing ./configure --enable-sctp --enable-dtls-mtu to fail on master.
With WOLFSSL_NO_MALLOC there is no allocator behind a NULL-heap XMALLOC,
so an allocation made outside any CTX or SSL object can only be served
from the global heap hint. wolfSSL_Init() makes such an allocation: under
OPENSSL_EXTRA it seeds the compatibility-layer RNG, whose _InitRng() call
allocates with a NULL heap. That returned NULL, wolfSSL_Init() reported
WC_INIT_E, and every wolfSSL_CTX_new_ex() that triggered the lazy init
failed, so neither example could establish a connection even though the
pool it had loaded was large enough.
Have each example nominate its own pool, which is what the hint is for.
The server could not do that before: it created its CTX through
wolfSSL_CTX_load_static_memory(), which loads the pool and creates the CTX
in one call, so wolfSSL_Init() ran before the caller ever saw the hint.
Load the pool with wc_LoadStaticMemory() and create the CTX afterwards, as
the client already does.
Claim the hint only when none is set, and drop it again on the way out.
Both pools are local to the example's own function, and testsuite runs the
wolfCrypt test, both examples and the echo server in one process, so an
example that overwrote the hint would leave it pointing at a pool that
dies the moment the example returns.
scripts/resume.test, scripts/tls13.test and testsuite/testsuite.test go
from failing to passing with --enable-staticmemory -DWOLFSSL_NO_MALLOC.
Restrict the claim to the standalone programs (!NO_MAIN_DRIVER). testsuite
and unit.test compile both examples with NO_MAIN_DRIVER and run server_test
on a spawned thread beside client_test, so an in-harness claim would publish
one thread's automatic-storage pool as the process allocator, let the other
thread allocate from it, and then revoke it when the owning frame unwound.
In those builds the harness's own long-lived pool is the one that belongs in
the hint.
Check ctx after wolfSSL_CTX_new_ex() rather than letting the following IO-pool
load report a CTX allocation failure as "unable to load static memory".
myEccKeyGen() generated the key share straight into the library's key object,
and myEccSharedSecret() is handed only the peer's key, so a TLS 1.3 client had
no private key left to reach and every handshake ended with:
wolfSSL_connect error -170, ECC input argument wrong type, invalid input
TEST_PK_PRIVKEY already kept the key on the application's side, which is what
a real PK-callback application does anyway; it just was not the default. Keep
it whenever the connection is TLS or DTLS v1.3 as well, and have the shared
secret callback pick the key by the same question its key gen asked, not by a
flag key gen has not set yet. The union holding it stops being
TEST_PK_PRIVKEY-only, and its comment stops naming TLS v1.2 as the version
that needs it.
Neither example zeroed its PkCbInfo, so hasKeyGen started as stack garbage.
Only the TEST_PK_PRIVKEY paths read it before, which hid that; zero it in
both.
scripts/pkcallbacks.test only ever ran the build's default version, and that
is TLS v1.2 wherever it is compiled in, so none of the above was covered. Run
the default and then each version the build has.
examples/benchmark/dtls_bench.c calls wolfSSL_accept and wolfSSL_connect
unconditionally, so a build that compiles either side out fails to link:
Undefined symbols: "_wolfSSL_accept", referenced from: _main in dtls_bench.o
That is the os-check dtls13-client-minimal entry, which passes
CPPFLAGS=-DNO_WOLFSSL_SERVER; os-check.yml does not pass --disable-examples,
so the benchmark is always built.
Compile each DTLS leg out with its own side, the way examples/server/server.c
does, and report the missing side from main(). pick_method() and set_mtu()
serve only those two legs and go with them, and the send sink is used by the
client leg alone, so none of them are left unused. The plain UDP legs need
neither side and keep working.
The option set ENABLED_TLSV12 to no, which the summary printed and a couple of
derived settings read, but nothing defined WOLFSSL_NO_TLS12, so every line of
the TLS 1.2 implementation was still compiled and still reachable. The one
place configure.ac adds that define sits in the FIPS lean-aesgcm bundle and is
guarded by "TLS 1.2 is still on and the user did not ask for it", which is
false exactly when --disable-tlsv12 was passed. Only the CMake build honoured
the switch.
Define it once the bundles have had their say, so --enable-tinytls13 and the
other bundles that turn the version off get it as well. The case guard keeps
the FIPS bundles from adding it twice.
Add three os-check entries for the configurations this makes reachable:
--disable-tlsv12 on its own, the same with DTLS 1.3, and the same with the
sniffer. The one existing entry that passes the option,
dtls13-client-minimal, already defined WOLFSSL_NO_TLS12 through CPPFLAGS and
strips most of the crypto along with it, so it never covered the option's own
effect.
Compiling the version out reaches code that assumed it was always there.
ProcessCSR_ex parses one certificate_status message and TLS 1.3 reads the
chain's per-certificate entries through it, so it moves out of the version
guard; only its TLS 1.2 wrapper stays behind. BuildMessage's connection-ID
size is read by the TLS 1.2 record path alone, so it is declared with it. The
tests and examples that hard-code a TLS 1.2 method, or a helper that only
those call, are skipped without the version.
CMake rejected three of the four combinations configure does but let
WOLFSSL_OLD_TLS=yes through, defined NO_OLD_TLS behind the user's back and
reported the option as still on. Reject it there too, and drop the NO_OLD_TLS
the block used to add, which the old-TLS option itself now always supplies.
Fix guard around inclunde of chacha20_poly1305.h in internal.h for when
session ticket is not ChaCha20-Poly1305.
Fix port allocation and readiness testing in scripts.
Fix other issues in the scripts as well.
Update test.h code around ready file.
For LTO compiles, all functions must be used.
Make sure the assembly functions are marked as 'used' when not called
internally.
Fix a race in the example client that intermittently failed
scripts/unit.test on TLS 1.3 non-blocking session resumption.
RISC-V 64-bit: s0 needed but functions need to omit frame pointer.
Have source matching generated code again.
With WOLFSSL_SNIFFER defined, the example client and server pin themselves to
a static RSA and static ECC cipher list so a capture can be decrypted. The
guard for that read "version < 4", meant to leave TLS 1.3 alone, but the
examples encode a DTLS version as a negative number and DTLS 1.3 is -4, so a
"-u -v 4" run was handed a TLS 1.2 only cipher list.
The server then found no common TLS 1.3 suite in MatchSuite_ex() while
processing the ClientHello and failed with MATCH_SUITE_ERROR, which reached
the client as a missing_extension alert. Every DTLS 1.3 handshake between the
two examples failed in a sniffer build, which also took out the DTLS cases of
scripts/ocsp-stapling_tls13multi.test. Only the examples were affected; an
application that does not set that cipher list was always able to handshake.
Allow a quantum-safe-only TLS 1.3 stack - ML-KEM key exchange with ML-DSA or
SLH-DSA authentication - with RSA, ECC and DH all disabled (the CNSA 2.0 /
EO 14412 posture).
- Treat a usable standalone ML-KEM group as a valid key exchange and
Falcon/ML-DSA/SLH-DSA as valid authentication, so the "no cipher suites"
#errors no longer fire (ssl.c, internal.h), and extend the CopyDecodedToX509
pkCurveOID guard to match the struct member guards (internal.c).
- asn.c: guard the RSA/ECC-only certificate signing helpers so cert generation
compiles with RSA and ECC disabled, while keeping MakeSignatureCb available
for the WOLFSSL_CERT_SIGN_CB (TPM/HSM) offload path.
- tls.c: advertise the psk_dhe_ke exchange mode when a standalone ML-KEM group
is usable on the client side, that is when the build has both key generation
and decapsulation. Without it an ML-KEM-only build fell back to psk_ke, the
resumption key schedules diverged, and the first AEAD record failed to
authenticate (AES_GCM_AUTH_E), breaking session-ticket resumption.
- tls13.c: hoist the hashAlgo void cast in EncodeSigAlg out of the Ed25519 and
Ed448 cases so a build with no classic signature algorithm still marks the
parameter used.
- Examples and tests: add ML-DSA and SLH-DSA certificate selection (gated on
NO_RSA so it only applies in true PQC-only builds) and default to TLS 1.3
when no classic auth is built in and a post-quantum signature is available
(test.h, client/server/echo*, test_tls13.c). SLH-DSA uses the SHAKE-128s leaf
certificates and their shared 128s root.
- CI: add PQC-only entries to the pq-all.json config list (ML-DSA and SLH-DSA),
each exercising the build, a full TLS 1.3 handshake, and ML-KEM-only
session-ticket resumption.
Whether a build can authenticate with a post-quantum certificate is three
separate questions - is the algorithm compiled in, are test credentials wired
up for the enabled parameter set, and can this build sign - so test.h answers
them with three tiers of macro rather than testing WOLFSSL_HAVE_MLDSA and
WOLFSSL_HAVE_SLHDSA directly:
- TEST_HAVE_MLDSA_CERT_FILES / TEST_HAVE_SLHDSA_CERT_FILES: the credential
paths name real files. ML-DSA needs an enabled parameter level; SLH-DSA needs
a 128s parameter set, since both families share a 128s root and only those
certificates are wired up, and PEM decoding, since the entity certificates
ship as PEM only. Loading a certificate needs verification support alone, so
test_tls13.c uses this tier - it only needs a certificate on the context.
- TEST_HAVE_MLDSA_CERTS / TEST_HAVE_SLHDSA_CERTS: the above, plus the ability
to sign. A verify-only build derives WOLFSSL_MLDSA_NO_SIGN and drops private
key support entirely, so it can neither load the key nor produce a
CertificateVerify. The example credential ladders use this tier.
- TEST_HAVE_PQC_CERT_AUTH: either of those, plus WOLFSSL_TLS13. Both algorithms
are TLS 1.3 only, so folding the version into the macro keeps the version
defaults and the PSK fallbacks in the client, server and echo examples from
disagreeing. Falcon is deliberately absent, having no credentials in the
ladders, so a Falcon-only build keeps the historic TLS 1.2 PSK behavior.
A build that fails a gate now falls back to PSK instead of naming credentials
it cannot use, which removes two hard #errors that were reachable from
configure: --enable-slhdsa with only non-128s parameter sets broke every build
that includes test.h, even ones with RSA and ECC enabled that would never touch
an SLH-DSA credential.
Also in the credential material and the configurations this opens up:
- test.h: pair the DER ML-DSA certificate with mldsa<N>-key.der. The
mldsa<N>_priv-only.der files are standalone PKCS#8 encoding vectors from an
unrelated key pair, so a build without PEM decoding presented a certificate
and signed with a key that did not match it, and the peer rejected the
CertificateVerify.
- echoserver.c: load the SLH-DSA key with CERT_FILETYPE like every neighboring
branch, rather than hardcoding WOLFSSL_FILETYPE_PEM against a certificate
loaded in the build-dependent format.
- internal.c, tls13.c, ssl_load.c: mark ssl, sigOut, keyType and keySz used.
Every reader of these sits in a per-algorithm block, so a build with no
signing algorithm at all left them unused and -Werror rejected the library.
- tests/api.c: skip test_wolfSSL_PKCS8 unless RSA, ECC or PEM decoding is
available. With none of them every key-loading body compiles out, leaving the
file locals unused and nothing for the test to do.
Added documentation of the defines for algorithms and assembly code and math.
user_settings_embedded.h has configuration defines at the top that are set to
0 for off and 1 for on. Setting these turns on the appropriate defines.
A crypto callback that returns WC_PENDING_E for a TLS record cipher
silently corrupted records: Encrypt()/Decrypt() advance the cipher
state to CIPHER_STATE_END before the pending check, so on resume the
record is shipped without re-running the cipher.
WOLF_CRYPTO_CB_ASYNC_POLL gives crypto callback devices the QAT/Nitrox
"poll to fill output" completion model. On WC_PENDING_E the async event
stays queued; wolfSSL_AsyncPoll() re-enters the device with the new
WC_ALGO_TYPE_ASYNC_POLL (wc_CryptoCb_Poll) to finish the job and fill
the output buffer. The re-entry only polls while the event is still
pending, and a device that cannot complete the job (no poll support, or
it reports nothing pending) hard-fails with WC_HW_E rather than
reporting the op done with an unfilled buffer. Only the two async record
ciphers (AES and 3DES markers) are routed to poll completion, and only
when crypto callbacks are the async backend (not QAT/Cavium/SW);
handshake PK keeps the re-invoke model. The wolfSSL_AsyncPop eviction is
gated for poll-capable devices so the existing resume-at-CIPHER_STATE_END
path becomes correct with no record state-machine changes.
Without the feature (and without a software/QAT/Cavium backend) a
pending bulk cipher op now errors out with ASYNC_OP_E instead of
corrupting the record, and configure/cryptocb.c warn about the
unsupported combination.
Tests in tests/api/test_async.c cover direct AES-GCM/CBC/CCM and 3DES
poll completion in both directions at multiple pend depths, negative
cases for cipher types defined but not dispatched (ChaCha, single DES),
and full TLS 1.3 handshake+echo: an encrypt-offload run, a both-
directions run that offloads encrypt and decrypt on both peers using a
per-peer device, and the no-poll failure path.
Add WOLFSSL_TLS_READ_AHEAD (--enable-readahead), toggled at runtime via
wolfSSL_set_read_ahead(). When enabled, the record-header read pulls a
full record in one recv() so the body arrives without a second syscall.
The receive window is configurable with
wolfSSL_CTX/SSL_set_default_read_buffer_len() (OpenSSL-compatible):
0 keeps the one-record default, a larger value coalesces several records
per recv(), a smaller value caps the per-connection buffer footprint.
Records exceeding the window are still received correctly, the buffer
grows on demand and is reallocated back down to the window afterwards so
the retained footprint stays bounded.
Includes docs, API tests, and a benchmark toggle.