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.
When a subjectAltName otherName has an empty value, SetDNSEntry() called
XMEMCPY(dst, NULL, 0). Passing NULL to memcpy is undefined behavior and
aborts under UBSan. Skip the copy unless there is real data to copy.
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.
TlsSessionCacheGetAndRdLock() and TlsSessionCacheUnlockRow() are defined
only when the internal session cache is compiled in, but three call sites
used them without that guard. Building with NO_SESSION_CACHE while session
tickets and TLS 1.3 are enabled compiled cleanly and then failed to link.
Both call sites handle state that cannot exist without the cache.
GetSesionFromCacheOrExt() and FreeSessionFromCacheOrExt() serve the
stateful TLS 1.3 ticket path, where the ticket is only a session ID that
has to be looked up. TlsSessionIdIsValid() serves DTLS session ID
resumption, which already bails out at run time on sessionCacheOff. With
no internal cache and no external cache, the lookup now returns NULL and
the peer falls back to a full handshake.
FreeSessionFromCacheOrExt() returns early from the external cache branch
instead of using an else, so that the unlock can be guarded on its own.
This has been broken since the stateful ticket support was added in
commit 1106e5ff0, first released in v5.6.2-stable.
The bulk struct copy always carries over the input's ex_data pointers,
so the caller now says whether the output takes ownership of them.
wolfSSL_GetSessionAtIndex hands the copy to the caller, so it must not
take over the ex_data the cache owns. wolfSSL_DupSession keeps the
current behaviour by passing true.
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.
wolfSSL_X509_STORE_CTX_set_verify_cb stored the application callback in
ctx->verify_cb, but every verification site read ctx->store->verify_cb
instead, so the field was never consulted. An application installing a
restrictive callback on the store context, which is the OpenSSL
documented way to enforce extra policy during verification, had it
silently ignored, and wolfSSL_X509_verify_cert could report success on a
chain the callback would have rejected.
Add X509StoreGetVerifyCb, which prefers the context callback and falls
back to the store one, and use it at all four call sites in
X509StoreVerifyCert, X509StoreCheckPathLen and wolfSSL_X509_verify_cert.
The store fallback keeps its OPENSSL_ALL or WOLFSSL_QT guard because the
store field only exists there, while the call sites now follow the
OPENSSL_EXTRA guard of the setter. Clear ctx->verify_cb in
wolfSSL_X509_STORE_CTX_init along with the other per-verification state
so a reused context does not carry a stale callback.
A rejection also has to be reportable. When the certificate manager
accepts a chain, ctx->error is X509_V_OK, so a callback that rejects it
without recording an error of its own left wolfSSL_X509_verify_cert
returning failure while X509_STORE_CTX_get_error still said the chain
was fine. Record WOLFSSL_X509_V_ERR_UNSPECIFIED in that case, matching
what OpenSSL reports, and only when the callback set no error itself.
Add that value to the X509 error enum, where the openssl compatibility
header already had the define.
Feeding the rejection marker to SetupStoreCtxError is not an option
there, since GetX509Error has no X509_V_ error for it and would pass the
negative value through as the reported error. The OPENSSL_ALL date
recheck did exactly that after a rejection, so skip that block once the
callback has rejected, which also stops it from consulting the callback
a second time.
Add a regression test that verifies a good chain twice, once bare and
once with a rejecting context callback, requires the second attempt to
fail, and checks the reported error both when the callback records one
and when it does not.
Fixes F-7341.
The function freed the extension's dynamically allocated ASN.1 string
buffer but left value.data and value.isDynamic pointing at it. The
subsequent wolfSSL_ASN1_STRING_copy() call snapshots those fields before
copying and frees the old buffer once the copy is complete, so the stale
pointer was freed a second time. Any second call to
wolfSSL_X509_EXTENSION_set_data() on an extension holding a value of at
least CTC_NAME_SIZE bytes hit this, and passing the extension its own
value made the copy read freed memory as well.
wolfSSL_ASN1_STRING_set() already performs an alias safe replacement and
disposes of the previous buffer itself, so drop the manual free. Add a
regression test that replaces a dynamically allocated extension value and
then sets the value from itself.
Fixes F-7340.
DoClientHello has four exits that fail with VERSION_ERROR when runtime
version restrictions leave nothing acceptable at or below the version the
client offered. Three of them sent no alert at all, and the fourth sent
one only when WOLFSSL_EXTRA_ALERTS was defined, so a default build simply
dropped the connection. The client could not tell a version mismatch from
a network failure.
The generic fallback did not help. SendFatalAlertOnly is a no-op unless
WOLFSSL_EXTRA_ALERTS is defined, and where it is defined it grouped
VERSION_ERROR with MATCH_SUITE_ERROR and sent handshake_failure. That also
disagreed with the TLS 1.3 mapping, which already resolves VERSION_ERROR
to protocol_version.
Send a fatal protocol_version alert from all four branches regardless of
WOLFSSL_EXTRA_ALERTS, and give VERSION_ERROR its own case in
SendFatalAlertOnly so the generic path agrees.
Note that this is only observable on the TLS 1.2 message path. A TLS 1.3
capable server routes the ClientHello through DoTls13HandShakeMsgType,
which already translates the error into the right alert.
Fixes F-7568.
Six white-box builds were being skipped, each contributing nothing to the
union while the runs still reported success.
test_frodokem_fault_common.h: a comment contained "wc_Shake*" followed by
"/wc_AesEcbEncrypt", and the "*/" closed the block comment, so the rest of
the sentence parsed as code. One character in a shared header took out two
white-boxes across two variants.
test_wc_xmss_impl_whitebox.c: wc_xmss_rand_hash_lr(), wc_xmss_chain_sha256_32()
and the BdsState helpers exist only in the non-small signing path, so the
file never compiled under WOLFSSL_WC_XMSS_SMALL. Each section now carries the
guard its target carries in wc_xmss_impl.c, and the existing stub block
covers the excluded builds.
test_mldsa_fault_whitebox.c: the DER encode/decode entry points need ASN.1
support plus the export and private-key options. All ten call sites now sit
behind one WB_MLDSA_ASN1 gate; without it the sweeps cover the raw
import/export paths instead.
test_memory_whitebox.c: this file compiles memory.c with
WOLFSSL_STATIC_MEMORY, where the wc_MemStats_Ptr definition is guarded out,
while the rest of the library still references it through mem_track.h. With
memory.o trimmed from the archive that reference dangled. Supply the
definition; void* because the type is not visible here and only the storage
matters.
test_rsa_fault_whitebox.c: setvbuf(stdout) so output survives a run that
dies mid-buffer.
Measured after each fix: frodokem, xmss, mldsa and infra now build every
white-box in every variant. wc_xmss_impl.c returns to 57/74 -- an earlier
attempt at the xmss fix removed a section that was still live and cost a
condition.
mlkem_poly: same ladder defect the mldsa white-box had -- USE_INTEL_AVX512()
is IS_INTEL_AVX512() && IS_INTEL_AVX512_BW(), so clearing the F bit alone
never gave the middle operand a false side. 122/219 -> 145/219.
cryptocb: wc_CryptoCb_Curve25519MakePub/Generic take no devId, resolving a
device through FindDevice/FindDeviceByIndex, so their argument guards and
`dev && dev->cb` are driven across registered / callback-less / nothing-
registered states. 128/144 -> 136/144.
curve25519: wc_curve25519_generic_blind()'s size and pointer OR-guards, one
operand true per call. 56/64 -> 62/64, back to its pre-rebase level.
Upstream added an AVX512 arm above the AVX2 one, and the ladder only ever
cleared AVX2/BMI2 -- so the AVX512 arm won every row and the arms below it
stopped being reached, costing 64 conditions.
Three fixes: USE_INTEL_AVX512() is IS_INTEL_AVX512() && IS_INTEL_AVX512_BW(),
so the F and BW bits are now cleared separately to give that decision's three
operands their own rows; a row clears AVX512_VBMI for the new VBMI arm; and
CPUID_INTEL is forced on, because SHA3_USE_AVX2() is gated on IS_CPU_INTEL()
and its true side is otherwise unreachable on an AMD host. Rows can now set
bits as well as clear them.
wc_mldsa.c 292/579 -> 463/579.
check-source-text rule I flags an error code used as a comparison operand
without WC_NO_ERR_TRACE(). Jenkins reported five; the same idiom appears
17 times in the file, and the check caps its output, so all of them are
wrapped here rather than only the reported lines.
The object is not unsafe to reuse because it crashes -- it does not crash.
PKCS7_EncodeSigned() frees the object's cert list once it has written the
certificates, so a second encode succeeds with an empty certificate set.
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.
check-source-text flags a comparison against a bare error code, since an
unwrapped operand defeats the error-trace build. 85 of them across five
white-boxes, all introduced by this branch; wrap each in WC_NO_ERR_TRACE().
test_compress.c was the only file of the 105 in tests/api/ that included a
libc header before <tests/unit.h>. That header establishes wolfSSL's
feature-test macros, and pulling limits.h in ahead of it fixes glibc's
exposure before they are seen, which under -std=c89 left POSIX types the
rest of the suite needs undeclared -- the unknown 'stack_t' and 'intptr_t'
the c89 clang-tidy scenario reported. Include it after, where it still
provides INT_MAX.