Adds wc_AesKeyWrap_Pad/wc_AesKeyUnWrap_Pad and their _ex variants plus crypto
callback dispatch, routing blocks through wc_AesEcb* so an ECB only callback works.
The PSS hook can hand back the recovered block through out/outSz/outLen.
A device that reports only a verdict leaves outLen at 0; wolfSSL then
zeroes the buffer and returns saltLen + hLen, and rejects a buffer
smaller than that with RSA_BUFFER_E. A reported length is clamped to the
buffer size, and any positive handler return maps to SIG_VERIFY_E.
Move the Ed448 sign WOLFSSL_CHECK_MEM_ZERO registration below the crypto
callback hook so the device path no longer returns past it, and guard the
RSA-PSS test callback against WOLF_CRYPTO_CB_ONLY_RSA.
Adds tests for the recovered-data, over-claimed-length and undersized
buffer paths, and an os-check config that builds the hooks under
WOLFSSL_CHECK_MEM_ZERO.
The os-check matrix had one NO_SESSION_CACHE entry, dtls13-client-minimal,
and it is client only with no session tickets. Both paths that failed to
link are server side ticket code, so nothing in the matrix covered them.
Add two entries. no-session-cache-session-ticket sets the macro directly
and enables DTLS 1.3 with WOLFSSL_DTLS_NO_HVR_ON_RESUME, covering both
call sites. It also sets HAVE_EXT_CACHE, since an external cache with no
internal one is the only configuration in which the external cache branch
of FreeSessionFromCacheOrExt() survives the preprocessor, and a build can
reach it through --enable-wpas with --enable-lowresource.
lowresource-tls13-session-ticket reaches the src/internal.c paths purely
through configure options, since --enable-lowresource defines
NO_SESSION_CACHE, which is how a user runs into this without setting any
flags by hand. It does not cover src/dtls.c, whose TlsSessionIdIsValid()
needs WOLFSSL_DTLS_NO_HVR_ON_RESUME, so the two entries are not
interchangeable.
Both disable the examples, because tests/unit.test calls
wolfSSL_get1_session() and wolfSSL_set_session(), which NO_SESSION_CACHE
compiles out. The entries are therefore compile coverage for the library.
Making the tests build in this configuration is a larger change.
RFC 8446 Section 6.2 lists failure to validate a PSK binder under
decrypt_error, but TranslateErrorToAlert() mapped BAD_BINDER to
illegal_parameter. That told the peer its ClientHello was malformed, when
in fact the message was well formed and only the binder MAC did not match.
BAD_BINDER is also returned when no offered PSK matched and the server has
no certificate to fall back to. That reuse is deliberate, because it keeps
an unknown PSK identity indistinguishable from a failed binder, so both
conditions still map to a single alert after this change. A comment at the
return site in CheckPreSharedKeys() now records the invariant at both ends.
Splitting BAD_BINDER off illegal_parameter exposed a second path that had to
move with it. FindPsk() raised PSK_KEY_ERROR, which stays mapped to
illegal_parameter, when the server callback recognised an identity carrying
a non-zero obfuscated_ticket_age. That check ran before any binder was
derived, so on a certificate-less server the two alerts would have let an
unauthenticated peer enumerate valid PSK identities without holding a key.
The check is removed rather than made to fail differently: RFC 8446 Section
4.2.11 says that for an externally established identity an
obfuscated_ticket_age of 0 SHOULD be used and servers MUST ignore the value.
Ignoring it satisfies that requirement, closes the oracle, and lets a
conformant client that sends a non-zero age complete a handshake that was
previously rejected. test_tls13_psk_age_no_identity_oracle() asserts a known
and an unknown identity produce the same alert, with a positive control that
the known run really took the matched path.
Also removes an unreachable branch in CheckPreSharedKeys(). Since commit
089f1f7c9 added the earlier !*usingPSK certificate check, the later
certificate check and its BAD_BINDER return can no longer be reached. A
build with certificates returns BAD_BINDER earlier when none is loaded, and
a NO_CERTS build returns it unconditionally. The remaining branch now falls
through to the shared exit so WOLFSSL_LEAVE() is emitted on the certificate
fallback path as well.
Adds an os-check-linux configuration, tls13-psk-no-certs. psk.yml already
covers NO_CERTS with TLS 1.3 PSK through static-psk-lowresource-tls13, but
that config disables DH, so it only reaches the psk_ke branch. This one
leaves DH enabled and covers psk_dhe_ke under NO_CERTS.
Applications that inspect wolfSSL_get_alert_history() will observe alert 51
instead of 47 for these conditions. The affected tests are updated.
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.
The mp API variant of wc_PKCS12_PBKDF_ex builds a working buffer holding
D || S || P, but totalLen was computed as dLen + sLen and so left out the
password block entirely. The fill loop writes pLen bytes starting at
S + sLen, which is past the end of the buffer, and DoPKCS12Hash was called
with the short length so the password was never absorbed into the digest.
A build with this variant enabled therefore derived the same key for every
password over a given salt, and overflowed the heap allocation or the
static buffer by pLen bytes depending on the salt length.
Sum dLen with iLen instead, matching the non-mp variant. That corrects the
allocation size, the hash input length, the I update bound and the
ForceZero coverage in one place.
Fixes F-7306.
The DigestInfo length extraction in wc_SignatureVerifyHash was compiled
only when WOLFSSL_RSA_PUBLIC_ONLY was not defined, but the block depends
on ASN.1 support, not on private key support. In a public only build the
RSA verify path stays reachable and wc_SignatureVerify still DER encodes
the digest, so the length check fell through to the plain digest size
comparison and rejected every valid signature with BAD_LENGTH_E. The
same guard also broke compilation with NO_ASN and RSA enabled, since
asn.h is not included in that case and GetSequence and GetOctetString
are undeclared.
Guard the block on NO_ASN instead, matching the DER encode call site in
wc_SignatureVerify.
Fixes F-7412.
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.
WOLFSSL_CERT_SIGN_CB is set only by --enable-certsigncb, and that flag
appears nowhere under .github/. wc_SignCert_cb() is therefore compiled by
no CI job, and neither is test_wc_SignCert_cb(), the only test covering
it. The buffer bounds check the preceding commits add to that function,
and the test assertions that go with it, would have merged without
anything building them.
Added as its own entry in the os-check Linux config list, in sorted
position. "minutes" is seeded at 8.3 from the sibling --enable-all entries
rather than omitted: the omission defaults it to 1.0, which sorts an
eight minute build last in a list scheduled longest-first and deals it
into whichever shard is already fullest. It also suppresses the stale
estimate annotation that would otherwise prompt the refresh. Replace it
with the real number from the Minutes column of the first run.
Verified by running the entry through the workflow's own driver,
.github/scripts/parallel-make-check.py, with the CFLAGS the workflow
applies at make time (-pedantic -Wdeclaration-after-statement
-Wnull-dereference -Wno-overlength-strings
-DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE) and --private-dir=certs.
The out-of-tree build produces no compiler warnings and make check
reports 17 passed, 6 skipped, 0 failed. Both test_wc_SignCert_buffer_bounds
and test_wc_SignCert_cb run rather than skip.
pq-all and trackmemory restore a seed ccache that already sits at its
cap, so every object a run compiles evicts a seeded one that is still
needed. Both reported a cache 100% full with over 200 cleanups and a
43-44% hit rate, and their configs took 3-5x their warm time. That left
pq-all shards finishing at 23-26 min against a 30 min limit: a healthy
shard passed with 5.6 min to spare, so any hiccup ahead of the build
took the job down. One did, twice, when the prebuilt deb image came up
short and the dependency step fell back to apt for 9.7 min - the only
two jobs out of 47 whose install ran past 2 min are the only two that
timed out. Size both caches to hold the seeded objects and a run's own
alongside them, and widen the limits so a slow install is survivable.
Raise the trackmemory limit as well, for a different reason: its
wolfEntropy configs have overrun it outright, once with six configs
done inside 8 min while all-wolfentropy ran on for another 32. That is
a stall rather than a slow run - 91 jobs have a 17.2 min median and a
17.9 min p90, with nothing between 18 min and the cap - so the higher
limit is headroom, not a fix. It costs nothing on a healthy run.
Recalibrate "minutes", which drives longest-first scheduling and shard
balancing. pq-all and fips-dev-no-post had drifted several-fold in both
directions: two frodokem cross-builds were declared at 4 min but take
0.3, so the pool kept scheduling them first as its longest work. pq-all
is calibrated from runs whose ccache actually hit, which the resized
cache makes the normal case; a value read off a cold run makes every
warm run warn instead. os-check-linux was already calibrated cold and
correct, so only its three genuine outliers change.
Both config lists are re-sorted to restore the documented descending
order, so most of their diff is a mechanical move rather than a value
change. Shard totals come out at 29.4/29.6 for pq-all and 76.9-77.7
across the four os-check shards.
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).
linuxkm/x86_vector_register_glue.c: in wc_save_vector_registers_x86(), properly squelch the hard-IRQ call warning if flags & WC_SVR_FLAG_MAYBE_INHIBIT;
src/internal.c: in AllocKey(), properly set key_inited = 1 if wc_SlhDsaKey_Init() succeeds;
.github/workflows/fips-dev-no-post.yml: test -DWC_FIPS_AESGCM_NO_SHORT_NONCES.
.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().
Notes:
* EXTRA_CPPFLAGS carries -Werror in the base configure line, because the
autotools -Werror is gated on a VCS checkout being detected in the configure
CWD and these are VPATH builds, which would otherwise silently get no -Werror
at all.
* The fuzzing cell's WC_DEBUG_VECTOR_REGISTERS_FUZZING_SEED is $$, which changes
from run to run.
* The two out-of-tree kernel-module cells (linuxkm, linuxkm-all-asm) are
commented out, with a note that out-of-tree module builds don't work yet.
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.
Nothing in CI exercised an asynchronous build that actually returns
WC_PENDING_E from a full feature set, which is why the five preceding
fixes all describe failures no workflow could see.
The async workflow has two groups and neither reaches these paths. The
asynccrypt-all entries pass --enable-asynccrypt --enable-all, but
--enable-all turns on cryptocb, and configure.ac only auto-enables the
software simulator when cryptocb, pkcallbacks, Cavium and Intel QA are all
off. Those builds therefore define WOLFSSL_ASYNC_CRYPT without ever
suspending an operation. The asynccrypt-sw entries do suspend, but they
build only OCSP stapling, so they compile neither TLS 1.3 post-handshake
authentication nor DTLS.
Add one entry that pairs --enable-asynccrypt-sw with --enable-all and
--enable-dtls13, which covers both reproducers: the post-handshake auth
case in tests/test-tls13.conf and the DTLS MTU case in
tests/test-dtls-mtu.conf.
Measured at 1.6 minutes locally. Declared as 3 to match the neighbouring
asynccrypt-all entries, which measure 1.4 locally against their declared 3,
so this machine runs roughly twice as fast as whatever those values were
taken from and 1.6 here projects to about 3.4 there. The value is only a
scheduling weight, and the existing entries are left alone rather than
rewritten from local timings.
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.
Extend the wolfSSL Zephyr module for the wolfPSA-provider and secure-sockets
efforts:
- Native RTOS threading: wolfCrypt's Zephyr port uses k_mutex/k_thread/
k_condvar directly (no CONFIG_POSIX_THREADS), with k_condvar gated on the
kernel version, covered by a native-threading ztest wired into CI.
- Config interface: a user-supplied CONFIG_WOLFSSL_SETTINGS_FILE is
authoritative and the module never layers Kconfig #defines over it. The
module-default block is shaped by build-profile knobs (WOLFSSL_CRYPTO_ONLY,
WOLFSSL_SINGLE_THREADED, which now defaults from !MULTITHREADING) plus new
classic-crypto/TLS/PQC feature knobs (RSA/ECC/ChaCha-Poly/Curve25519/SNI/
session-cache/session-ticket and ML-KEM/ML-DSA/LMS/XMSS/Falcon, each with
its memory-reduction "small" options). A consumer such as wolfPSA validates
its own requirements rather than the module injecting them.
- DRBG seeding: wc_GenerateSeed() on Zephyr draws seed material from the
hardware entropy driver when present (chunked to the entropy API's uint16_t
length, DT_HAS_CHOSEN-guarded) and falls back to sys_rand_get() otherwise;
HAVE_HASHDRBG stays guarded by WC_NO_HASHDRBG.
- z_time(): read the native_sim simulator RTC for a real wall clock on the
native targets regardless of libc.
Verified on native_sim/native/64 and nucleo_h743zi.