Add a new option to require that an external Pre-Shared Key is negotiated
for a handshake to succeed, configured via the new APIs
wolfSSL_CTX_require_psk()/wolfSSL_require_psk(). When set, a handshake
that completes without negotiating an external PSK is aborted with
PSK_MISSING_ERROR instead of falling back to a certificate handshake, so
the PSK acts as an additional security factor.
This is a TLS 1.3 / DTLS 1.3 feature. In (D)TLS 1.2 the use of a PSK is
determined by the negotiated cipher suite, so a mandatory PSK is instead
configured there by restricting the cipher suite list to PSK suites; the
new APIs therefore reject non-TLS-1.3 contexts with BAD_FUNC_ARG.
To keep the requirement fail-closed, the APIs also disable version
downgrade on the object so a downgrade-capable context (e.g. one created
from a v23 method) cannot silently fall back to (D)TLS 1.2 and complete
without a PSK; a peer that does not support (D)TLS 1.3 fails to connect.
The requirement applies to external PSKs only (not session tickets):
session-ticket resumption is exempt. To preserve forward secrecy a
mandatory external PSK must also use an (EC)DHE key exchange; a pure
psk_ke handshake is rejected with PSK_KEY_ERROR. When used with
WOLFSSL_CERT_WITH_EXTERN_PSK, it also ensures that peers are properly
authenticated with both the PSK and via certificates.
The new APIs live alongside the existing wolfSSL_[CTX_]no_dhe_psk()/
only_dhe_psk() PSK options and do not depend on certificate support, so
the feature is usable in NO_CERTS (PSK-only) builds.
Added unit tests for the new APIs and enforcement.
Expose the internal SAN-list helpers as public wc_ APIs so callers can build a
DNS_entry list, encode it into a DER GeneralNames SEQUENCE, and populate a Cert
directly:
- wc_SetDNSEntry() - append a typed alt-name entry to a list
- wc_FlattenAltNames() - encode a list into a buffer (thin wrapper)
- wc_SetAltNamesFromList() - encode a list straight into cert->altNames/Sz
Declarations live in asn.h (they use the DNS_entry type) and are gated by the
existing WOLFSSL_ASN_API export macro; doxygen notes the WOLFSSL_PUBLIC_ASN/
OPENSSL_EXTRA export requirement. Adds a wolfCrypt test covering the success,
NULL-list, NULL-output, BUFFER_E, and Cert paths.
Add a crypto-callback operation for validating an ECC key.
Under WOLF_CRYPTO_CB_ONLY_ECC validation now fails closed with
NO_VALID_DEVID when no device handles the operation; previously such
keys were accepted unvalidated. This is a deliberate compatibility
break, documented at the dispatch site.
Under WOLF_CRYPTO_CB_ONLY_ECC, HAVE_ECC_MAKE_PUB is now enabled and
backed by the dispatch alone, failing closed with NO_VALID_DEVID when
no device handles the operation (previously NOT_COMPILED_IN).
Return PUBLIC_KEY_E for wc_ed25519_export_key if public key is not
present.
Return PUBLIC_KEY_E for wc_ed448_export_key if public key is not
present.
Rename several inLen parameters to outLen for consistency.
Fix F-4427
Enable all-zero shared secret check for Curve448/25519 by default. Ensure post_handshake_auth extension was sent before accepting post-handshake CertificateRequest message.
Follow up to PR #10450 with some minor fixes:
* FIPS 205 numbering: slh_sign is §10.2.1 Alg 22; slh_verify is Alg 24;
hash_slh_verify is Alg 25 (impl comments and doxygen).
* Widen wc_SlhDsaKey_SignHashWithRandom's addRnd to const byte* to
match wc_SlhDsaKey_SignWithRandom.
* Make the SLHDSA_PHMSG_MAX_LEN invariant explicit with a named
SLHDSA_LARGEST_APPROVED_PHM_LEN constant and a wc_static_assert.
* SHAKE128/SHAKE256 round-trip and length-rejection coverage for both
SignHash and VerifyHash.
* Doxygen: briefs for the five DER encode/decode APIs; accurate
decoder failure-rollback wording; tighter return-code lists for
Verify and VerifyMsg.
* ChangeLog: silent-failure caveat for raw messages whose length
happens to equal the digest size of the chosen hashType.
wc_SlhDsaKey_{Sign,Verify}Hash* previously accepted the raw message and
performed the pre-hash internally. They now require the caller to hash the
message first and pass the resulting digest -- the functions no longer call
wc_*Hash() themselves and feed the supplied digest directly into the M'
construction. Parameters are renamed from msg/msgSz to hash/hashSz to reflect
this, and hashSz is validated against wc_HashGetDigestSize(hashType) per
FIPS 205 Section 10.2.2 (32 for SHAKE128, 64 for SHAKE256), returning
BAD_LENGTH_E on mismatch.
This matches ML-DSA's wc_dilithium_{sign,verify}_ctx_hash, NIST ACVP
signatureInterface=external / preHash=preHash vectors, and other libraries
(OpenSSL HASH-ML-DSA, leancrypto, mldsa-native). It also enables distributed
signers and HSM-style flows where the digest is computed separately from the
signing operation.
Migration: callers must now hash the message before invoking these APIs;
passing the raw message will either fail length validation or produce
signatures over the wrong input. The M'-supplied wc_SlhDsaKey_SignMsg* /
VerifyMsg family (FIPS 205 internal interface, Algorithms 19/20) is
unchanged but gains stricter input validation and doxygen coverage.
Replace the liboqs-based pre-standardization SPHINCS+ implementation
with the native FIPS 205 SLH-DSA implementation across the
certificate / ASN.1 / X.509 layers, and add SLH-DSA-rooted test
certificates plus TLS 1.3 .conf scenarios that exercise the new
verification path. All liboqs SPHINCS+ code is removed.
This enables SLH-DSA for certificate chain authentication: CA
certificates signed with SLH-DSA, certificate signature verification
against an SLH-DSA root. TLS 1.3 entity authentication via
CertificateVerify with SLH-DSA will be added in a follow-up PR.
Follows RFC 9909 (X.509 Algorithm Identifiers for SLH-DSA) and
NIST FIPS 205. Supports both SHAKE and SHA-2 parameter families
across all twelve standardized variants.
DER codec:
- New PrivateKeyDecode, PublicKeyDecode, KeyToDer, PrivateKeyToDer,
PublicKeyToDer with RFC 9909 encoding (bare OCTET STRING containing
4*n raw bytes = SK.seed || SK.prf || PK.seed || PK.root, no nested
wrapper). OID auto-detection across all twelve SHAKE / SHA-2 variants.
- PublicKeyDecode raw-bytes fast path mirrors wc_Falcon_PublicKeyDecode
and wc_Dilithium_PublicKeyDecode so callers (notably
wolfssl_x509_make_der and ConfirmSignature, which pass the raw
BIT STRING contents stashed by StoreKey) decode correctly. Honours
the caller's *inOutIdx start offset.
- Error paths in Private/PublicKeyDecode preserve params/flags/
inOutIdx and only ForceZero the buffer half each helper actually
writes; skip the wipe entirely on BAD_LENGTH_E (no bytes touched).
- ImportPublic uses |= on flags so a Private-then-Public import
sequence retains FLAG_PRIVATE.
OID dispatch:
- 12 standardized NIST OIDs (6 SHAKE + 6 SHA-2) per RFC 9909. The
pre-standardization OID-collision mechanism is removed since NIST
OIDs do not collide.
- wc_SlhDsaOidToParam / wc_SlhDsaOidToCertType return NOT_COMPILED_IN
(rather than -1) for recognised SLH-DSA OIDs whose parameter set
isn't built; wc_IsSlhDsaOid recognises both. The x509 dispatch
surfaces this as a precise diagnostic instead of the generic
"No public key found".
- wc_GetKeyOID picks a placeholder parameter from whatever variant is
compiled in and #errors at compile time if none is.
- asn_orig.c EncodeCert / EncodeCertReq accept SHA-2 SLH-DSA keyTypes
alongside SHAKE.
Tests and fixtures:
- Test cert chain in certs/slhdsa/: SLH-DSA-SHAKE-128s and
SLH-DSA-SHA2-128s self-signed roots that sign reused ML-DSA-44
entity keys (server + client), plus the gen script
(gen-slhdsa-mldsa-certs.sh, OpenSSL >= 3.5).
- New TLS 1.3 .conf scenarios under tests/suites.c dispatch:
test-tls13-slhdsa-shake.conf, test-tls13-slhdsa-sha2.conf, and a
wrong-CA negative test test-tls13-slhdsa-fail.conf.
- DER round-trip and on-disk decode tests; bench_slhdsa_*_key.der
fixtures regenerated with wolfSSL's own encoder so the codec is
pinned to RFC 9909.
- New unit test test_wc_slhdsa_x509_i2d_roundtrip exercises the raw
PublicKeyDecode entry point that wolfssl_x509_make_der relies on.
- test_wc_slhdsa_check_key now tests both Public-then-Private and
Private-then-Public import orderings.
Build / ABI:
- DYNAMIC_TYPE_SPHINCS = 98 kept as RESERVED with a tombstone comment
for ABI stability; new code should use DYNAMIC_TYPE_SLHDSA (107).
- All build system / IDE project files updated; SPHINCS+ sources,
headers, and test data removed.
- Dead bench_slhdsa_*_key arrays removed from gencertbuf.pl and
certs_test.h; the .der files on disk drive the decode tests.
RFC 8446 section 8 requires any server instance to accept 0-RTT for a
given ClientHello at most once. Prior to this change wolfSSL's behaviour
diverged from that requirement in several ways:
* ctx->maxEarlyDataSz defaulted to MAX_EARLY_DATA_SZ whenever the
library was built with WOLFSSL_EARLY_DATA, so servers auto-
advertised 0-RTT in NewSessionTicket without the application
asking. RFC 8446 E.5 says 0-RTT MUST NOT be enabled unless
specifically requested.
* The post-accept eviction is compiled out under NO_SESSION_CACHE,
so builds without the cache accepted 0-RTT with no replay defence.
* Stateless self-encrypted tickets do not carry a session ID on the
stateless DoClientTicket decrypt path, so wolfSSL_SSL_CTX_remove_
session could not locate them to evict.
* wolfSSL_SSL_CTX_remove_session always returned 0 on success
regardless of whether the session was actually in the cache,
diverging from OpenSSL's SSL_CTX_remove_session (1 on success,
0 on not-found).
Changes:
* src/internal.c: ctx->maxEarlyDataSz defaults to 0; applications
must opt in with wolfSSL_CTX_set_max_early_data.
* src/tls13.c: #error when WOLFSSL_EARLY_DATA is built with
HAVE_SESSION_TICKET and NO_SESSION_CACHE. Escape hatch
WOLFSSL_EARLY_DATA_NO_ANTI_REPLAY for deployments that take
application-layer responsibility.
* wolfssl/internal.h: imply WOLFSSL_TICKET_HAVE_ID from
WOLFSSL_EARLY_DATA so stateless-ticket issuance populates the
cache under an ID that eviction can find.
* src/ssl_sess.c: wolfSSL_SSL_CTX_remove_session returns 1 when the
session was found (internal-cache hit, or ctx->rem_sess_cb fired
for an external cache), 0 otherwise. Matches OpenSSL semantics.
* src/tls13.c: the 0-RTT acceptance condition in CheckPreSharedKeys
now calls wolfSSL_SSL_CTX_remove_session and checks its return:
the eviction is the check. If the session was in the cache, 0-RTT
is accepted and the single-use requirement is satisfied. If not,
the early_data extension is rejected through the normal path so
the record layer correctly skips in-flight 0-RTT records.
WOLFSSL_MSG at each rejection site.
* doc/dox_comments/header_files/ssl.h: document runtime opt-in.
* tests: four new tests —
test_tls13_0rtt_default_off (fails without default-to-0 fix),
test_tls13_0rtt_stateless_replay (fails without TICKET_HAVE_ID
implication and remove_session gate),
test_tls13_remove_session_return (fails without return-value fix),
test_tls13_0rtt_ext_cache_eviction (fails without ext-cache
counts-as-found fix).
test_tls13_early_data explicitly opts in via
wolfSSL_CTX_set_max_early_data.
tests/api.c: two SSL_CTX_remove_session == 0 assertions updated
to == 1.
Add SRAM PUF (Physically Unclonable Function) support to wolfCrypt. Derives device-unique cryptographic keys from the power-on state of SRAM memory using a BCH(127,64,t=10) fuzzy extractor with HKDF key derivation.
- **wolfCrypt PUF API** (`wolfcrypt/src/puf.c`, `wolfssl/wolfcrypt/puf.h`)
- `wc_PufInit`, `wc_PufReadSram`, `wc_PufEnroll`, `wc_PufReconstruct`
- `wc_PufDeriveKey` (HKDF-SHA256), `wc_PufGetIdentity` (SHA-256 device fingerprint)
- `wc_PufZeroize` (secure context cleanup)
- `wc_PufSetTestData` (synthetic SRAM for testing without hardware)
- **BCH(127,64,t=10) error-correcting codec** - corrects up to 10 bit flips per 127-bit codeword across 16 codewords
- **`WC_PUF_SHA3` build option** - select SHA3-256 instead of SHA-256 for identity hash and HKDF (default: SHA-256)
- **Precomputed GF(2^7) tables** - `const` arrays in `.rodata` (no runtime init, thread-safe, flash-resident on embedded)
- `./configure --enable-puf` (auto-enables HKDF dependency)
- CMake: `WOLFSSL_PUF=yes`
- `WOLFSSL_USER_SETTINGS`: define `WOLFSSL_PUF` and `WOLFSSL_PUF_SRAM`
- See wolfssl-examples/puf for example implementation on STM32 NUCLEO-H563ZI (Cortex-M33, STM32H563ZI)
- Supports test mode (synthetic SRAM)
- Builds to ~13KB `.elf`
- Tested on NUCLEO-H563ZI: enrollment, noisy reconstruction, key derivation all pass
- `.github/workflows/puf.yml`: host build + test workflow for PUF feature
- Doxygen API docs for all 8 public functions
- PUF group added to `doxygen_groups.h`
Implement RFC8773bis (draft-ietf-tls-8773bis-13)
cert_with_extern_psk for TLS 1.3, including protocol checks
and API support.
Includes unit tests for API and handshake behavior as well
as tests in the testsuite using extended examples.