Added tests
Restored NULL cipher suite tests into new conf files
Restored previous eNULL handling
Fix session timeout check and clear ticket when not resuming
Document WOLFSSL_TLS13_NULL_CIPHER_IN_DEFAULT
Two PRB findings, unrelated to each other.
tests/unit-mcdc/test_ssl_certman_whitebox.c and test_ssl_sess_whitebox.c were
never added to EXTRA_DIST, so make dist left them out. Confirmed by building a
tarball before and after: the two were the only files this branch adds that
were missing, and both are in it now. Every other file the branch adds was
already listed.
test_wolfSSL_ocsp_stapling_accessors wrote cssl->ocspProducedDateFormat
directly. Expect* records a failure and carries on rather than returning, so
on the path where wolfSSL_new() failed that is a dereference of NULL, which is
what the static analyser reported at the ExpectNotNull above it. The three
direct field writes are guarded; the calls that merely pass cssl are safe
either way, because the API checks it.
Checked the rest of the file for the same shape rather than fixing only the
reported line: of the raw dereferences of Expect-obtained pointers, the DTLS
ones were already inside if (dssl != NULL) blocks and the remainder are inside
Expect* macros, which short-circuit once a previous one has failed. These
three were the only unguarded ones.
Five configurations failed, four of them at build time, each the same mistake
in a different place: a guard that describes what the test needs rather than
what the build actually provides.
Also registers the four tests/unit-mcdc files that were missing from
EXTRA_DIST.
Verified by building all five configurations locally: all pass. Campaign
unaffected -- dtls, ssl_api and tls_core re-measured identical, gates green,
white-box smoke 82 passed 0 failed.
ProcessPeerCertLeafRevocation decides what a revocation answer MEANS, and its
guards discriminate between specific codes: an explicit assertion that the
certificate is revoked, a responder that does not know it, one that could not
be reached, a certificate naming no responder, a lookup still in flight, and
the CRL equivalents. The difference between them is the difference between
failing a handshake and continuing it. Producing them for real needs four
separate responder deployments, one per vector, so none of these arms had
been taken.
This translation unit already #includes internal.c, so the revocation entry
points it calls but does not define are redirected to fakes with a #define
ahead of the include -- the idiom mcdc_fault_hash.h already uses for wolfcrypt
primitives. CheckCertOCSP_ex, CheckCertCRL, CheckCertCRL_ex and
OcspNoUrlPolicy live in ocsp.c and crl.c, so this rewrites only the driver's
copy and leaves the library untouched. Each fake returns the code the vector
chose, which is the point: the answer is the input under test.
The answers are only half of it. The guards below them also read ocspEnabled,
crlEnabled, crlCheckAll, tls1_3, totalCerts and whether the decoded
certificate has a CA; a first version pinned all six while sweeping only the
codes and gained 2 conditions. Sweeping them one at a time from both
saturated ends, crossed with the codes, gained 13.
7168 vectors. internal.c 786/1732 -> 799/1732.
Three more error-classification clusters, none needing a peer.
CsrDoStatusVerifyCb lets an application override the library's OCSP verdict,
and the interesting arms are the disagreements: the callback forcing an error
on a good status, and clearing one on a bad status. No in-tree test installs a
callback that disagrees, so neither arm had been taken. A mock callback
returning a chosen value against a chosen incoming result sweeps the matrix,
including the invalid positive return.
DoCertificateStatus compares the declared status length against the record
size; a conforming peer always makes them agree, so the mismatch arms need
bytes no real peer sends. Driven with crafted input, no fixture.
SendData's opening guards ask whether the connection is resuming from a
blocked write, which a test that writes successfully never sets up. The
oversized-length guard above them returns before any IO happens.
internal.c 780/1732 -> 784/1732.
Error propagation is the largest uncovered category in internal.c, and most of
it needs the failing value produced by something upstream. These two functions
are the exception: they classify an error handed to them, so the failing value
is an argument and every arm is reachable by passing the code that arm names.
DoCertFatalAlert maps a verification failure onto the alert the peer is sent.
A handshake produces one failure at a time and most of them not at all -- an
expired certificate, then a path-length-invalid one, then a revoked one, each
needing its own chain -- so the arms are mutually exclusive per run and never
pair. The mapping is security-relevant: it decides what a rejected peer learns
about why. Swept over every code it discriminates on, two it does not, and
both tls1_3 settings, since NO_PEER_CERT branches again on that.
ProcessPeerCertCheckKey enforces the per-algorithm minimum key size. The
minimums are configuration, fixed for the life of a connection, and the
negative sentinel is never set by a working one, so both operands of each
guard are constant in any real run. Swept over each key OID the switch names
plus one it does not, four minimums including the sentinel, and verifyNone
both ways.
75 vectors, no fixture, no fault injection, no certificate chain -- a zeroed
WOLFSSL and a DecodedCert filled in by hand. internal.c 762/1732 -> 780/1732.
The largest remaining category in dtls13.c is not NULL guards but
ssl->options.side comparisons. A connection has one side for its whole life,
so each of those decisions is taken the same way on every call that endpoint
makes, and a test owning both endpoints does not help: MC/DC wants both
outcomes of the SAME decision in one binary's profile. Setting the side by
hand is the only way to pair them.
New driver test_dtls13_role_whitebox.c, 348 vectors over a zeroed WOLFSSL with
its ctx pointed at a client CTX -- these functions read options, keys and
dtls13Rtx and take scalars; none needs a peer or a handshake:
Dtls13AcceptFragmented side x type x encryption x ChFrag x dtlsStateful
Dtls13CheckEpoch side x type x epoch, over the whole switch
Dtls13SaveOrFlushClientHello side x connectState across the range bounds
Dtls13SetEpochKeys stored epoch side vs requested side, all nine pairs
dtls13.c 70/132 -> 79/132.
CRL and OCSP get the null guards their callers cannot reach:
StoreCRL(crl == NULL || path == NULL) -- both operands; every in-tree caller
validates both before reaching it.
FreeOcspEntry(entry == NULL || !entry->ownStatus) -- an entry with a borrowed
status list is what the multi-response path builds, and freeing one must be a
no-op rather than a double free.
CheckOcspRequest's ioCtx selection (ssl && ssl->ocspIOCtx != NULL) -- an ssl
with no per-connection IO context, which falls back to the manager's, is
produced by no existing test.
crl.c 29/48 -> 31/48, ocsp.c 21/47 -> 24/47. Smoke: 78 drivers, 0 failed.
Two more file-static functions (SupportedHashSigAlgo, GetCtxOcspLock,
BuildCertificateStatusWithStatusCB, InvalidateSessionOnFatalAlert,
dtlsRecordIsNewest, FreeCachedHandshakeMessages, ParseCipherList,
SendHandshakeMsg, DecodePrivateKey_ex, GetRealSessionID, EcMakeKey,
SetCurveId, RpkIsTrusted, CopyDecodedPubKey, CopyDecodedSig,
ProcessPeerCertParse, ProcessCSR_ex) get their null-guard conditions closed
via two new drivers (test_internal_nullguard_whitebox.c,
test_internal_peerkey_whitebox.c), plus a third and fourth driver for
HashSkeData/GetDhPublicKey (test_internal_dhskehash_whitebox.c) and
EdDSA_Update (test_internal_eddsa_whitebox.c).
Fixed a real bug in the first two drivers: SendHandshakeMsg's and
DecodePrivateKey_ex's rejecting vectors had no self-contained accepting
partner, on the assumption the campaign union could borrow one from the real
handshake corpus. It cannot -- llvm-cov computes the covered bit per
condition from one binary's own profile, and the campaign's union is a
logical OR of those already-computed bits, not a merge of raw execution
traces, so an accepting call recorded by one binary can never pair with a
rejecting call recorded by another. Confirmed against the real campaign run:
those two decisions, plus ProcessPeerCertParse's three-operand guard, stayed
in GAPS.md after the first union despite being individually exercised by an
isolated local measurement. Every driver here now supplies its own false/true
partner in the same binary; verified locally per-driver and against the real
tls_core aggregate before committing.
src/internal.c union MC/DC: 690/1722 -> 752/1722.
The error paths in internal.c are most of what is left uncovered there, and a
conforming pair of endpoints never enters them. Reaching them needs a peer
that sends something wrong -- not a transport.
test_memio already runs both endpoints through a byte buffer with credentials
from certs/, and both sides are ours, so the buffer between them can be edited
between rounds. test_tls_wire_mangle flips a bit at a chosen offset of a
chosen round, which selects which handshake message gets hit and where: record
type, record length, handshake type, handshake length, or inside the body.
test_tls_wire_sequence uses the harness's own drop, duplicate, reorder and
length-rewrite helpers, which reach the ordering and retransmit logic a byte
flip cannot -- a flipped byte still arrives once, in sequence, at the right
length.
Both assert only that a corrupted handshake fails rather than crashes; the
coverage is in the paths it takes on the way out. Both run in the ssl_hs
group, which five of the six protocol modules already measure.
internal.c 650/1722 -> 690/1722. The DTLS arms of both tests currently
contribute nothing -- see the commit that follows.
Four batches, measured together.
crl.c 18->29. CompareCRLnumber over hex strings a parser would never emit
(non-hex, empty, a number that went backwards); FindRevokedSerial with a
same-length different-serial vector; BufferStoreCRL's five-operand guard,
driven by hand-linking a CRL_Entry that is missing each field in turn and
unlinking it before FreeCRL walks the list; LoadCRL over a real directory and
over NULL arguments.
wolfio.c 47->53. wolfIO_DecodeUrl against the malformed URLs an attacker
supplies and a certificate never carries: unterminated IPv6 bracket, CR/LF
smuggled into the host, a host and a port at the length cap, a colon with no
digits, a port past the 16-bit ceiling. MAX_URL_ITEM_SIZE is private to
wolfio.c, so the API test has to mirror it and hope; the white-box does not.
ocsp.c 18->21. A CbOCSPIO mock is a complete responder for this function --
it can return a negative error, a zero-length body, or a positive length with
a NULL buffer on demand, which no real responder can be made to do.
keys.c 26->33. The file has no group and no caller outside internal.c and
tls13.c, and everything in it runs from values a handshake has already fixed:
one cipher suite, one version, enc and dec never NULL. GetCipherSpec swept
over every suite-table selector, SetKeys called twice on the same objects so
the lazy-allocation guards get both halves, SetKeysSide over the four
dtls/1.3 combinations.
GetRecordHeader is the first code to look at bytes off the wire, before any
key or MAC. Its decisions are the version-mismatch tolerance rules -- which
peer, in which handshake state, may send which version -- plus the length and
record-type checks.
From tests/api the header always comes from wolfSSL's own record writer, so
pvMajor and pvMinor equal ssl->version on every call and the whole mismatch
block is dead: downgrade, connectState, acceptState and the
alert-before-negotiation carve-out are evaluated only for a header the local
writer never produces.
The fixture is five bytes and a struct. inputBuffer.buffer is a pointer, so it
points at a local array; the function writes only through its out-parameters
and stores nothing. Rows are one flip from a well-formed TLS 1.2 record, with
the baseline re-run between rows so the DTLS replay window does not carry.
61 calls. internal.c 630/1722 -> 642/1722.
SanityCheckMsgReceived refuses a handshake message that arrives on the wrong
side, arrives twice, or arrives out of order. Every condition in it is a
rejection, so a conforming peer takes all of them false and no number of
handshakes produces an independence pair; the vectors have to be malformed on
purpose.
The fixture is a zeroed WOLFSSL with its ctx pointed at a client CTX -- the
function reads only options, msgsReceived and specs, and dereferences nothing
else unguarded except SSL_CM(ssl)->ocspMustStaple in the ServerHelloDone arm.
No certificate, no transport, no peer.
Sweeping from all-clear and all-set alone measured 15 conditions: both
saturated ends are refused by the prerequisite or the duplicate check before
the out-of-order chain is ever evaluated. Each arm therefore also gets the
state in which its message is accepted, and one flipped bit from there puts
exactly one chain operand true with the rest false. That took it to 47.
3552 calls. internal.c 587/1722 -> 630/1722.
MatchDomainName decides whether a presented certificate name matches the host
being connected to. It is pure -- two strings, two lengths, a flags word, no
ssl, no allocation -- but nearly uncovered, because callers reach it only after
a completed verification: the name comes from a parsed SAN or CN and the host
from local configuration, so an empty pattern, a bare star, a wildcard that is
not leftmost, a wildcard with no dot after it, or a zero length on one side
only never arrive. Those are the inputs an attacker picks.
38 vectors over both wildcard-policy flag settings, each row flipping one named
operand with an accepting partner differing in a single field.
internal.c 573/1722 -> 587/1722.
wolfio.c reads as needing a transport and does not. It needs a byte source and
a descriptor, and both can be supplied locally.
wolfIO_HttpProcessResponseGenericIO takes a WolfSSLGenericIORecvCb, which is
int (*)(char*, int, void*), so the whole HTTP response state machine is
drivable from a memory buffer. The mock caps bytes per call, which forces the
reassembly loop to iterate and produces split headers and split chunks that a
real socket would not reproduce on demand, and it can return -1 to inject a
read error with no transport involved.
socketpair(AF_UNIX) covers the descriptor half: wolfIO_SockIsDGram wants an fd,
not a peer, and a stream pair, a datagram pair and a closed descriptor give its
getsockopt branch all three outcomes. No ports, no DNS, no listener.
wolfio.c 43/87 -> 47/87.
InitSuites is a long table of 'tls1_2 && haveX && haveAES128' rows over twelve
have* flags. No handshake can drive it: every caller derives those flags from
what the build compiled in, so on one binary they are constant and no operand
has an independence pair. A one-at-a-time sweep from an all-ones baseline, over
six protocol versions and both sides, gives the pair for every operand of every
row in n+1 calls instead of 2^n. 156 calls, internal.c 532/1730 -> 573/1722.
Also extends the crl driver to CheckCertCRLCm's cm != NULL && cm->cbMissingCRL
and cm != NULL && cm->crlCb guards, whose operand 0 is true by construction
from wolfSSL_CertManagerCheckCRL.
The ocsp group runs 3 of its 8 tests on the campaign option list, so most of
ocsp.c is never entered from tests/api. GetOcspEntry's cache-match compares an
entry against the request by issuer hash and issuer key hash; a caller coming
through the public API builds both from the same certificate, so neither
operand has a false case from outside and the loop body needs a seeded cache
to execute at all. ocsp.c 7/47 -> 9/47.
crl.c measured 3 of 48: there is no crl test group, CRL lives in certman, and
certman runs 11 of 36 on this option list. Drives CheckCertCRLCm's
(serial == NULL || serialSz == 0) && serialHash == NULL guard with the four
vectors MC/DC needs. crl.c 3/48 -> 6/48.
Both drivers were including the target .c before wolfssl/options.h. config.h
carries no feature macros, so the smoke build compiled them with WOLFSSL_DTLS
and HAVE_CRL undefined: they took their skip stubs, exited 0, and were recorded
as passing while testing nothing. options.h now comes first, which is the rule
AGENTS.md already states. Under the smoke build they now drive 22 and 4 vectors
respectively.
Every uncovered condition in dtls.c is in a file-static helper on the stateless
path, where the public API fixes most arguments: no caller can ask
CreateDtls12Cookie for a NULL secret or FindExtByType for a length that
overruns its own vector. The dtls group already runs 87 of 103 and still leaves
46 of 56 uncovered, so the API limit is reached before the file is.
Drives CreateDtls12Cookie, FindExtByType, ClientHelloSanityCheck,
TlsCheckSupportedVersion and DtlsCidGetSize with paired vectors in one binary.
dtls.c 10/56 -> 16/56.
Seven new white-boxes (asn, dh, lms, pkcs7, pkcs12, tsp, xmss) drive each
operand of the NULL/size OR-guards true in isolation against the all-false
row in the same binary -- the row the API-level tests only ever produce
all-false, which is why those independence pairs stayed open.
The four sp_* lane white-boxes gain direct sp_* calls with crafted mp_int
operands for the RSA/DH bound checks and degenerate ECC points.
wc_xmss.c reaches 96/96; wc_lms.c 134/136. +559 conditions overall.
Also list all of tests/unit-mcdc/ in EXTRA_DIST -- 43 of the 74 files were
missing, including every asn/pkcs7/pkcs12/lms/xmss white-box.
puf_test() drives enroll/reconstruct/derive/identity with well-formed
arguments, so each entry guard of the form (ctx == NULL || x == NULL) is
only ever seen all-false and neither operand gets an independence pair.
The lane has no per-module main() and cannot #include-and-trim, so this
rides as a lane_extra_source constructor and reaches puf.c's counters
through its public entry points, the same arrangement
test_sp_cortexm_whitebox.c uses.
puf.c 4/12 -> 11/12 on the lane.
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.
Add test_wc_FalconDecisionCoverage to the falcon API group, covering the
public wc_falcon_* wrapper decisions (level checks, import/export and
sign/verify argument guards, init_id/init_label) with per-condition MC/DC
independence cases.
Add tests/unit-mcdc/test_falcon_whitebox.c, a standalone binary that
#includes falcon.c and drives its file-static encode/decode/zint/modp/
sampler/keygen-solver/sign guards -- including the small-mem
falcon_do_sign_dyn twin -- with both halves of each independence pair, plus
a real Falcon-512 make/sign/verify round-trip for the proceed halves.
Register the whitebox in EXTRA_DIST (test-only; it is not part of the
library build).
Add the tests/unit-mcdc white-box drivers produced by the coverage campaign's
fault-injection and SP-ARM emulator-lane passes, and extend the EXTRA_DIST
listing to cover them (test-only; standalone main() + #include the target .c;
never built into libwolfssl or unit.test -- see the comment in tests/include.am).
Fault-injection (mcdc_fault_alloc.h: a fail-after-N wolfSSL_SetAllocators mock,
swept across allocation sites to drive the FALSE half of (err==MP_OKAY)&&step
success-chain guards): dsa, eccsi, sakke, hpke, mlkem, mldsa, integer, rsa,
frodokem (+ a shared frodokem fault header).
SP-ARM emulator lanes (drive the C-level decisions in the cross-only asm SP
backends under qemu-user / m33mu): sp_arm64, sp_arm32, sp_armthumb, sp_cortexm.
The tests/unit-mcdc/*.c MC/DC white-box supplements this branch adds each
#include a wolfCrypt .c and carry their own main(); they are built standalone
by the per-module coverage campaign and are NOT compiled into libwolfssl or
unit.test (that would duplicate main()/symbols). They are test-only.
The source-completeness check (per-PR diff) requires every file a PR adds to
appear in an include.am. Since these cannot be tests_unit_test_SOURCES, list
them in EXTRA_DIST -- the same bucket tests/api/include.am uses for its
non-compiled files -- so they ship in the dist tarball without being built.
Scope: only the white-boxes this branch introduces.
Extend the swdev callback to handle ECC operations: keygen, ECDH, sign,
verify, get-size, get-sig-size.
Relax WOLF_CRYPTO_CB_ONLY_ECC guards in the test suite so that tests run
under swdev, and wire wc_SwDev_Init/Cleanup into testsuite, client, and
server. Two tests are intentionally kept excluded even with swdev because
they call raw ECC math primitives (wc_ecc_mulmod, on-curve validation in
wc_ecc_import_x963) that are stripped below the cryptocb dispatch layer:
- ecc_mulmod_test (wolfcrypt/test/test.c)
- test_wc_ecc_import_x963_off_curve (tests/api/test_ecc.c)
The memLock mutex and #include <pthread.h> in mem_track.h were
declared under #ifdef DO_MEM_LIST (Linux/macOS/Zephyr only), but
referenced under the broader guard
!defined(SINGLE_THREADED) && \
(defined(DO_MEM_LIST) || defined(DO_MEM_STATS))
Since DO_MEM_STATS is defined whenever WOLFSSL_TRACK_MEMORY +
USE_WOLFSSL_MEMORY are set without WOLFSSL_STATIC_MEMORY, any
non-Linux/Mac/Zephyr multi-threaded build failed to compile with
implicit pthread_mutex_lock declarations and undeclared memLock.
Replace the raw pthread mutex with wolfSSL's portable mutex API
(wc_InitMutex / wc_LockMutex / wc_UnLockMutex / wc_FreeMutex) so
locking works on every platform wolfSSL already ports to.
InitMemoryTracker now calls wc_InitMutex before
wolfSSL_SetAllocators installs TrackMalloc, guarded by a
memLockInit flag for idempotency. CleanupMemoryTracker calls
wc_FreeMutex after restoring the default allocators so no
in-flight allocation races a freed mutex. The four mutex guards
in TrackMalloc/TrackFree and the two in InitMemoryTracker/
ShowMemoryTracker are unified on the same condition as the
memLock declaration itself.
ZD #21763