Add SLH-DSA support for the TLS 1.3 and DTLS 1.3 handshake

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.
This commit is contained in:
Tobias Frauenschläger
2026-08-04 22:23:03 +02:00
parent e100f72548
commit ac75f181cd
49 changed files with 13728 additions and 290 deletions
+8
View File
@@ -68,6 +68,14 @@
"--enable-slhdsa=yes,small,verify-only",
"--enable-dilithium=yes,small,verify-only", "--disable-qt",
"CPPFLAGS=-pedantic -Wdeclaration-after-statement -Wnull-dereference -DWOLFCRYPT_TEST_LINT -DNO_WOLFSSL_CIPHER_SUITE_TEST -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE -DWOLFSSL_MLDSA_VERIFY_SMALL_MEM -DWOLFSSL_MLDSA_NO_LARGE_CODE"]},
{"name": "slhdsa-sha2-only", "minutes": 2,
"comment": "SHA2-only SLH-DSA build (SHAKE family disabled) across all 128/192/256 SHA2 parameter sets. A SHAKE-off build must still size the FORS/XMSS buffers for the 192/256-bit SHA2 parameters via the combined SLHDSA_ALL_NO_* maxima guards rather than collapsing to the 128-bit level; running the SHA2-256 sign/verify tests exercises that sizing.",
"configure": ["--enable-slhdsa=sha2", "--enable-testcert"]},
{"name": "pq-asynccrypt-dual-alg", "minutes": 2,
"comment": "Async crypto with dual-algorithm certs and PQC signatures. WOLFSSL_ASYNC_CRYPT turns off the streaming CertificateVerify path, so this is the only config that compiles the in-place fragmented send and the async args holder that Scv13Args must fit.",
"configure": ["--enable-asynccrypt-sw", "--enable-dual-alg-certs",
"--enable-slhdsa", "--enable-dilithium", "--enable-experimental",
"--enable-testcert"]},
{"name": "all-pq-mldsa-no-ctx", "minutes": 3,
"configure": ["--enable-intelasm", "--enable-sp-asm",
"--enable-all", "--enable-testcert", "--enable-acert",
+9
View File
@@ -262,6 +262,15 @@ PR stands for Pull Request, and PR <NUMBER> references a GitHub pull request num
## Enhancements
* **Behavioral change (`WC_MAX_CERT_VERIFY_SZ` with SLH-DSA)**: when SLH-DSA is
compiled in, `WC_MAX_CERT_VERIFY_SZ` is now sized from the largest enabled
SLH-DSA signature (`WC_SLHDSA_MAX_SIG_LEN + 1024`, up to roughly 50 KB) rather
than the previous 2048/6000. wolfSSL itself no longer uses the macro, but it
remains public: downstream code that declares a stack buffer with it, such as
`byte der[WC_MAX_CERT_VERIFY_SZ];`, grows that stack frame accordingly and
should allocate from the heap or size from the parameter set actually in use.
Builds without SLH-DSA are unaffected.
* **BREAKING (FIPS 205 SLH-DSA)**: `wc_SlhDsaKey_SignHash`, `wc_SlhDsaKey_SignHashDeterministic`, `wc_SlhDsaKey_SignHashWithRandom`, and `wc_SlhDsaKey_VerifyHash` now take the **caller-pre-hashed message digest** via `hash`/`hashSz` parameters (renamed from `msg`/`msgSz`), aligned with ML-DSA's `wc_dilithium_sign_ctx_hash` / `wc_dilithium_verify_ctx_hash` semantics, and NIST ACVP `signatureInterface=external` / `preHash=preHash` test vectors. `hashSz` must equal `wc_HashGetDigestSize(hashType)` (32 bytes for SHAKE128, 64 bytes for SHAKE256 per FIPS 205 Section 10.2.2); otherwise `BAD_LENGTH_E` is returned. Migration: hash the message yourself before the call (callers using positional arguments are source-compatible; only the parameter names changed). Caveat: callers who today pass a raw message whose length happens to equal the digest size for the chosen `hashType` (e.g., signing a 32-byte handle/IV/seed with `WC_HASH_TYPE_SHA256`) will not trip `BAD_LENGTH_E`; the resulting signature is syntactically valid but is over the wrong bytes. The pre-existing `wc_SlhDsaKey_SignMsgDeterministic` and `wc_SlhDsaKey_SignMsgWithRandom` retain their M'-supplied-directly contract (FIPS 205 internal interface, Algorithm 19); their input validation is hardened with the same NULL/length/`MISSING_KEY` checks as the `*Hash*` family. `wc_SlhDsaKey_VerifyMsg` is unchanged. All three gain doxygen coverage. (PR 10450, PR 10465)
* **Behavioral change (RSA-PSS trailerField enforcement)**: `DecodeRsaPssParams`
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MFICAQAwCwYJYIZIAWUDBAMVBEBjnLskgvRwsDH46s5f5Zd6W3FlAhW+xy1sfGck
hxcoVcIwxXOLlxrHqFE0n9/gAtpsMQCIZUs9O8FUCTLaUikI
-----END PRIVATE KEY-----
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MFICAQAwCwYJYIZIAWUDBAMUBEC9vheZPBjaR8CIx4Hw4qNb503pQeKv5AOVbmS7
S7otdQcBoV5stru1r86z4CgV82aKtfJIRZ6ev7qfTphHEqg9
-----END PRIVATE KEY-----
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MFICAQAwCwYJYIZIAWUDBAMbBECXSHJ0c9BqJlQbpOZD0vK429qeSsM7k8CRff2c
8P0srigPxVobEk82k2A6ptGfGkECMFGXvfkXb99vAaZ0/gqt
-----END PRIVATE KEY-----
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MFICAQAwCwYJYIZIAWUDBAMaBEBmQgMOIy2lyxOjyWQehHEH3uiw72tZvmnWBfs8
qNGZ46VII9GLPrz/jVN1LvJOAhJmcVLlA5GJ9Hnf9LOOCrbY
-----END PRIVATE KEY-----
File diff suppressed because it is too large Load Diff
+70 -3
View File
@@ -1,13 +1,15 @@
#!/usr/bin/env bash
#
# Regenerate SLH-DSA root certificates and ML-DSA-44 entity certificates
# used by tests/test-tls13-slhdsa-{shake,sha2}.conf.
# Regenerate the SLH-DSA root certificates, the ML-DSA-44 entity certificates
# used by tests/test-tls13-slhdsa-{shake,sha2}.conf, and the SLH-DSA entity
# (leaf) certificates used by tests/test-tls13-slhdsa-entity{,-128s}.conf.
#
# Requires: OpenSSL >= 3.5 (native SLH-DSA + ML-DSA support).
#
# The ML-DSA-44 entity keys are reused from ../mldsa/ (mldsa44_bare-priv.der
# for the server, mldsa44_seed-priv.der for the client) so this script does
# not generate or write new entity private keys.
# not generate or write new ML-DSA entity private keys. The SLH-DSA entity
# leaves get fresh keys and are signed by the family 128s root.
check_result(){
if [ "$1" -ne 0 ]; then
@@ -161,8 +163,73 @@ gen_variant() {
echo "Variant ${tag} complete."
}
# Generate one SLH-DSA leaf (server or client) signed by the matching 128s root
# of its hash family. Unlike the ML-DSA leaves above, the leaf key itself is
# SLH-DSA, so this cert exercises the SLH-DSA CertificateVerify handshake
# signature. The serials must stay distinct from the ML-DSA leaves (01/02) since
# they share the same issuer.
# $1 = role (server|client), $2 = hash family (shake|sha2),
# $3 = param tag (128f|128s), $4 = OpenSSL algorithm,
# $5 = cnf extension section, $6 = cert serial.
gen_slhdsa_leaf() {
local role=$1
local family=$2
local param=$3
local alg=$4
local ext=$5
local serial=$6
local root_base="root-slhdsa-${family}-128s"
local base="${role}-slhdsa-${family}-${param}"
echo "====================================================================="
echo " Generating ${base} (${alg}) signed by ${root_base}"
echo "====================================================================="
openssl genpkey -algorithm "$alg" -out "${base}-priv.pem"
check_result $? "Generate ${base} key"
openssl pkey -in "${base}-priv.pem" -outform DER -out "${base}-priv.der"
check_result $? "Convert ${base} key to DER"
echo -e "US\\nMontana\\nBozeman\\nwolfSSL_SLH-DSA\\nSLHDSA-${role}-${family}-${param}\\nwww.wolfssl.com\\nfacts@wolfssl.com\\n\\n\\n\\n" | \
openssl req -new -key "${base}-priv.pem" -config "$CNF" -nodes \
-out "${base}.csr"
check_result $? "Generate ${base} CSR"
openssl x509 -req -in "${base}.csr" -days 1000 \
-extfile "$CNF" -extensions "$ext" \
-CA "${root_base}.pem" -CAkey "${root_base}-priv.pem" \
-set_serial "$serial" \
-out "${base}-cert.pem"
check_result $? "Sign ${base} cert"
rm -f "${base}.csr"
openssl x509 -in "${base}-cert.pem" -outform DER > "${base}.der"
check_result $? "Convert ${base} cert to DER"
openssl x509 -in "${base}-cert.pem" -text > tmp.pem
mv tmp.pem "${base}-cert.pem"
# Served chain: leaf || root (ed25519/ML-DSA convention).
cat "${base}-cert.pem" "${root_base}.pem" > "${base}.pem"
rm -f "${base}-cert.pem"
}
gen_variant shake SLH-DSA-SHAKE-128s
gen_variant sha2 SLH-DSA-SHA2-128s
# SLH-DSA entity leaves for the TLS 1.3 handshake-signature tests, each signed
# by the 128s root of its own hash family above. 128f leaves drive a ~17KB
# CertificateVerify signature (fragmented send + reassembly); 128s leaves fit in
# a single record. Both the SHAKE and SHA2 families are covered so that builds
# that enable only one family still have working entity certs.
gen_slhdsa_leaf server shake 128f SLH-DSA-SHAKE-128f server_ecc 03
gen_slhdsa_leaf client shake 128f SLH-DSA-SHAKE-128f client_ecc 04
gen_slhdsa_leaf server shake 128s SLH-DSA-SHAKE-128s server_ecc 05
gen_slhdsa_leaf client shake 128s SLH-DSA-SHAKE-128s client_ecc 06
gen_slhdsa_leaf server sha2 128f SLH-DSA-SHA2-128f server_ecc 03
gen_slhdsa_leaf client sha2 128f SLH-DSA-SHA2-128f client_ecc 04
gen_slhdsa_leaf server sha2 128s SLH-DSA-SHA2-128s server_ecc 05
gen_slhdsa_leaf client sha2 128s SLH-DSA-SHA2-128s client_ecc 06
echo
echo "All SLH-DSA / ML-DSA-44 test certificates regenerated."
+16 -4
View File
@@ -23,14 +23,26 @@ EXTRA_DIST += \
certs/slhdsa/root-slhdsa-shake-128s.pem \
certs/slhdsa/root-slhdsa-shake-128s.der \
certs/slhdsa/server-mldsa44-shake.pem \
certs/slhdsa/server-mldsa44-shake.der \
certs/slhdsa/client-mldsa44-shake.pem \
certs/slhdsa/client-mldsa44-shake.der \
certs/slhdsa/root-slhdsa-sha2-128s-priv.pem \
certs/slhdsa/root-slhdsa-sha2-128s-priv.der \
certs/slhdsa/root-slhdsa-sha2-128s.pem \
certs/slhdsa/root-slhdsa-sha2-128s.der \
certs/slhdsa/server-mldsa44-sha2.pem \
certs/slhdsa/server-mldsa44-sha2.der \
certs/slhdsa/client-mldsa44-sha2.pem \
certs/slhdsa/client-mldsa44-sha2.der
certs/slhdsa/server-slhdsa-shake-128f-priv.pem \
certs/slhdsa/server-slhdsa-shake-128f.pem \
certs/slhdsa/client-slhdsa-shake-128f-priv.pem \
certs/slhdsa/client-slhdsa-shake-128f.pem \
certs/slhdsa/server-slhdsa-shake-128s-priv.pem \
certs/slhdsa/server-slhdsa-shake-128s.pem \
certs/slhdsa/client-slhdsa-shake-128s-priv.pem \
certs/slhdsa/client-slhdsa-shake-128s.pem \
certs/slhdsa/server-slhdsa-sha2-128f-priv.pem \
certs/slhdsa/server-slhdsa-sha2-128f.pem \
certs/slhdsa/client-slhdsa-sha2-128f-priv.pem \
certs/slhdsa/client-slhdsa-sha2-128f.pem \
certs/slhdsa/server-slhdsa-sha2-128s-priv.pem \
certs/slhdsa/server-slhdsa-sha2-128s.pem \
certs/slhdsa/client-slhdsa-sha2-128s-priv.pem \
certs/slhdsa/client-slhdsa-sha2-128s.pem
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MFICAQAwCwYJYIZIAWUDBAMVBEANOE7EfdCNHoAniR4INnlgx9wbwY5P6/GvS6qp
VAqa2QLot3vOVdiyLBiFeHdN/jQRrXt6L0xb387mkJarLKP1
-----END PRIVATE KEY-----
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MFICAQAwCwYJYIZIAWUDBAMUBEAV2jqhGIBxUrc/HxxuWxdf7al5aB70kOr958yA
6GOat/sOtEl6ndWSenNC8CUFkNNhhHyVSOLWp96Q3fS7gLM0
-----END PRIVATE KEY-----
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MFICAQAwCwYJYIZIAWUDBAMbBECfp1uIEgPHN76mdoiQ55gOFaSoTNBMXOpZ3DG4
pQTX1KUioB3F9tHZ+wyJFz2xSsXIOeIfhF/jKT+uEzp7zVQm
-----END PRIVATE KEY-----
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,4 @@
-----BEGIN PRIVATE KEY-----
MFICAQAwCwYJYIZIAWUDBAMaBEDftZ89P6hx2HdSGRWtnFqqX8c2Y9woT96b23DE
PHf8uuc0V5CRWfKtonjD05n/NekuYmzPKMUM2KtnLzWcQ8yP
-----END PRIVATE KEY-----
File diff suppressed because it is too large Load Diff
+5
View File
@@ -8223,6 +8223,11 @@ then
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_WC_SLHDSA"
# SLH-DSA is an X.509 certificate signature algorithm, so keep the
# certificate/ASN code enabled (mirrors ML-DSA) - otherwise a build with
# RSA, ECC and DH disabled turns ASN off and cannot authenticate a peer.
ENABLED_CERTS=yes
if test "$SLHDSA_PARAM_128S" = "yes"
then
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_SLHDSA_PARAM_128S"
+10
View File
@@ -1152,6 +1152,16 @@ static int Dtls13SendFragmented(WOLFSSL* ssl, byte* message, word16 length,
messageSize = length - rlHeaderLength - DTLS_HANDSHAKE_HEADER_SZ;
/* This copies the whole message body into a second full-size buffer,
* alongside the caller's output buffer that still holds the assembled
* message - a transient ~2x peak. For a large post-quantum
* CertificateVerify signature (SLH-DSA, ML-DSA) that body dominates RAM.
* A future optimization could have the caller assemble the body directly
* into this buffer (or sign into it) so the copy is avoided, cutting the
* peak toward ~1x. See the TLS 1.3 streamed CertificateVerify path
* (WOLFSSL_TLS13_STREAM_CERT_VERIFY) for the equivalent idea over TCP;
* note the retransmission copy (Dtls13RtxNewRecord) must remain regardless,
* so ~1x retained until ACK is the DTLS floor. */
ssl->dtls13FragmentsBuffer.buffer =
(byte*)XMALLOC(messageSize, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
+591 -11
View File
@@ -2355,6 +2355,11 @@ int InitSSL_Side(WOLFSSL* ssl, word16 side)
ssl->options.haveMlDsaSig = 1; /* always on client side */
}
#endif /* WOLFSSL_HAVE_MLDSA */
#ifdef WOLFSSL_HAVE_SLHDSA
if (ssl->options.side == WOLFSSL_CLIENT_END) {
ssl->options.haveSlhDsaSig = 1; /* always on client side */
}
#endif /* WOLFSSL_HAVE_SLHDSA */
#if defined(HAVE_EXTENDED_MASTER) && !defined(NO_WOLFSSL_CLIENT)
if (ssl->options.side == WOLFSSL_CLIENT_END) {
@@ -2799,6 +2804,11 @@ int InitSSL_Ctx(WOLFSSL_CTX* ctx, WOLFSSL_METHOD* method, void* heap)
ctx->haveMlDsaSig = 1; /* always on client side */
/* server can turn on by loading key */
#endif /* WOLFSSL_HAVE_MLDSA */
#ifdef WOLFSSL_HAVE_SLHDSA
if (method->side == WOLFSSL_CLIENT_END)
ctx->haveSlhDsaSig = 1; /* always on client side */
/* server can turn on by loading key */
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef HAVE_ECC
if (method->side == WOLFSSL_CLIENT_END) {
ctx->haveECDSAsig = 1; /* always on client side */
@@ -3504,6 +3514,92 @@ static WC_INLINE void AddSuiteHashSigAlgo(byte* hashSigAlgo, byte macAlgo,
}
else
#endif /* WOLFSSL_HAVE_MLDSA */
#ifdef WOLFSSL_HAVE_SLHDSA
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S)
if (sigAlgo == slhdsa_sha2_128s_sa_algo) {
ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
SLHDSA_SA_MAJOR, SLHDSA_SHA2_128S_SA_MINOR);
}
else
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128F)
if (sigAlgo == slhdsa_sha2_128f_sa_algo) {
ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
SLHDSA_SA_MAJOR, SLHDSA_SHA2_128F_SA_MINOR);
}
else
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_192S)
if (sigAlgo == slhdsa_sha2_192s_sa_algo) {
ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
SLHDSA_SA_MAJOR, SLHDSA_SHA2_192S_SA_MINOR);
}
else
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_192F)
if (sigAlgo == slhdsa_sha2_192f_sa_algo) {
ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
SLHDSA_SA_MAJOR, SLHDSA_SHA2_192F_SA_MINOR);
}
else
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_256S)
if (sigAlgo == slhdsa_sha2_256s_sa_algo) {
ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
SLHDSA_SA_MAJOR, SLHDSA_SHA2_256S_SA_MINOR);
}
else
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_256F)
if (sigAlgo == slhdsa_sha2_256f_sa_algo) {
ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
SLHDSA_SA_MAJOR, SLHDSA_SHA2_256F_SA_MINOR);
}
else
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_128S
if (sigAlgo == slhdsa_shake_128s_sa_algo) {
ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
SLHDSA_SA_MAJOR, SLHDSA_SHAKE_128S_SA_MINOR);
}
else
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_128F
if (sigAlgo == slhdsa_shake_128f_sa_algo) {
ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
SLHDSA_SA_MAJOR, SLHDSA_SHAKE_128F_SA_MINOR);
}
else
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192S
if (sigAlgo == slhdsa_shake_192s_sa_algo) {
ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
SLHDSA_SA_MAJOR, SLHDSA_SHAKE_192S_SA_MINOR);
}
else
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192F
if (sigAlgo == slhdsa_shake_192f_sa_algo) {
ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
SLHDSA_SA_MAJOR, SLHDSA_SHAKE_192F_SA_MINOR);
}
else
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256S
if (sigAlgo == slhdsa_shake_256s_sa_algo) {
ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
SLHDSA_SA_MAJOR, SLHDSA_SHAKE_256S_SA_MINOR);
}
else
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256F
if (sigAlgo == slhdsa_shake_256f_sa_algo) {
ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
SLHDSA_SA_MAJOR, SLHDSA_SHAKE_256F_SA_MINOR);
}
else
#endif
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef WC_RSA_PSS
if (sigAlgo == rsa_pss_sa_algo) {
/* RSA PSS is sig then mac */
@@ -3631,6 +3727,61 @@ void InitSuitesHashSigAlgo(byte* hashSigAlgo, int haveSig, int tls1_2,
keySz, &idx);
}
#endif /* WOLFSSL_HAVE_MLDSA */
#ifdef WOLFSSL_HAVE_SLHDSA
/* Only advertise the parameter sets that are actually compiled in, so we
* never offer a scheme we cannot sign or verify. SLH-DSA is defined for
* TLS 1.3 only, so it is never offered to a TLS 1.2 peer. */
if ((haveSig & SIG_SLHDSA) && tls1_3) {
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S)
AddSuiteHashSigAlgo(hashSigAlgo, no_mac, slhdsa_sha2_128s_sa_algo,
keySz, &idx);
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128F)
AddSuiteHashSigAlgo(hashSigAlgo, no_mac, slhdsa_sha2_128f_sa_algo,
keySz, &idx);
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_192S)
AddSuiteHashSigAlgo(hashSigAlgo, no_mac, slhdsa_sha2_192s_sa_algo,
keySz, &idx);
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_192F)
AddSuiteHashSigAlgo(hashSigAlgo, no_mac, slhdsa_sha2_192f_sa_algo,
keySz, &idx);
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_256S)
AddSuiteHashSigAlgo(hashSigAlgo, no_mac, slhdsa_sha2_256s_sa_algo,
keySz, &idx);
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_256F)
AddSuiteHashSigAlgo(hashSigAlgo, no_mac, slhdsa_sha2_256f_sa_algo,
keySz, &idx);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_128S
AddSuiteHashSigAlgo(hashSigAlgo, no_mac, slhdsa_shake_128s_sa_algo,
keySz, &idx);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_128F
AddSuiteHashSigAlgo(hashSigAlgo, no_mac, slhdsa_shake_128f_sa_algo,
keySz, &idx);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192S
AddSuiteHashSigAlgo(hashSigAlgo, no_mac, slhdsa_shake_192s_sa_algo,
keySz, &idx);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192F
AddSuiteHashSigAlgo(hashSigAlgo, no_mac, slhdsa_shake_192f_sa_algo,
keySz, &idx);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256S
AddSuiteHashSigAlgo(hashSigAlgo, no_mac, slhdsa_shake_256s_sa_algo,
keySz, &idx);
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256F
AddSuiteHashSigAlgo(hashSigAlgo, no_mac, slhdsa_shake_256f_sa_algo,
keySz, &idx);
#endif
}
#endif /* WOLFSSL_HAVE_SLHDSA */
if (haveSig & SIG_RSA) {
#ifdef WC_RSA_PSS
if (tls1_2) {
@@ -4804,6 +4955,150 @@ void InitSuites(Suites* suites, ProtocolVersion pv, int keySz, word16 haveRSA,
#if !defined(NO_WOLFSSL_SERVER) || !defined(NO_CERTS) || \
(!defined(NO_WOLFSSL_CLIENT) && (!defined(NO_DH) || defined(HAVE_ECC)))
#ifdef WOLFSSL_HAVE_SLHDSA
/* Map a TLS SLH-DSA signature-scheme minor byte (draft-reddy-tls-slhdsa) to
* the internal sa_algo. Returns invalid_sa_algo if unrecognized or the
* parameter family is not compiled in. */
byte SlhDsaSigMinorToType(byte minor)
{
switch (minor) {
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S)
case SLHDSA_SHA2_128S_SA_MINOR: return slhdsa_sha2_128s_sa_algo;
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128F)
case SLHDSA_SHA2_128F_SA_MINOR: return slhdsa_sha2_128f_sa_algo;
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_192S)
case SLHDSA_SHA2_192S_SA_MINOR: return slhdsa_sha2_192s_sa_algo;
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_192F)
case SLHDSA_SHA2_192F_SA_MINOR: return slhdsa_sha2_192f_sa_algo;
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_256S)
case SLHDSA_SHA2_256S_SA_MINOR: return slhdsa_sha2_256s_sa_algo;
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_256F)
case SLHDSA_SHA2_256F_SA_MINOR: return slhdsa_sha2_256f_sa_algo;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_128S
case SLHDSA_SHAKE_128S_SA_MINOR: return slhdsa_shake_128s_sa_algo;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_128F
case SLHDSA_SHAKE_128F_SA_MINOR: return slhdsa_shake_128f_sa_algo;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192S
case SLHDSA_SHAKE_192S_SA_MINOR: return slhdsa_shake_192s_sa_algo;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192F
case SLHDSA_SHAKE_192F_SA_MINOR: return slhdsa_shake_192f_sa_algo;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256S
case SLHDSA_SHAKE_256S_SA_MINOR: return slhdsa_shake_256s_sa_algo;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256F
case SLHDSA_SHAKE_256F_SA_MINOR: return slhdsa_shake_256f_sa_algo;
#endif
default: return (byte)invalid_sa_algo;
}
}
/* Map an internal SLH-DSA sa_algo to its enum SlhDsaParam value. Returns -1
* if hsType is not an SLH-DSA scheme. */
int SlhDsaTypeToParam(byte hsType)
{
switch (hsType) {
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S)
case slhdsa_sha2_128s_sa_algo: return SLHDSA_SHA2_128S;
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128F)
case slhdsa_sha2_128f_sa_algo: return SLHDSA_SHA2_128F;
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_192S)
case slhdsa_sha2_192s_sa_algo: return SLHDSA_SHA2_192S;
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_192F)
case slhdsa_sha2_192f_sa_algo: return SLHDSA_SHA2_192F;
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_256S)
case slhdsa_sha2_256s_sa_algo: return SLHDSA_SHA2_256S;
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_256F)
case slhdsa_sha2_256f_sa_algo: return SLHDSA_SHA2_256F;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_128S
case slhdsa_shake_128s_sa_algo: return SLHDSA_SHAKE128S;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_128F
case slhdsa_shake_128f_sa_algo: return SLHDSA_SHAKE128F;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192S
case slhdsa_shake_192s_sa_algo: return SLHDSA_SHAKE192S;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192F
case slhdsa_shake_192f_sa_algo: return SLHDSA_SHAKE192F;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256S
case slhdsa_shake_256s_sa_algo: return SLHDSA_SHAKE256S;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256F
case slhdsa_shake_256f_sa_algo: return SLHDSA_SHAKE256F;
#endif
default: return -1;
}
}
/* Is hsType any of the SLH-DSA signature schemes? */
int IsSlhDsaSigAlgo(byte hsType)
{
return SlhDsaTypeToParam(hsType) != -1;
}
/* Map an enum SlhDsaParam value to its internal SLH-DSA sa_algo. Returns
* invalid_sa_algo if the parameter set is not one of the TLS schemes. */
byte SlhDsaParamToType(int param)
{
switch (param) {
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S)
case SLHDSA_SHA2_128S: return slhdsa_sha2_128s_sa_algo;
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128F)
case SLHDSA_SHA2_128F: return slhdsa_sha2_128f_sa_algo;
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_192S)
case SLHDSA_SHA2_192S: return slhdsa_sha2_192s_sa_algo;
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_192F)
case SLHDSA_SHA2_192F: return slhdsa_sha2_192f_sa_algo;
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_256S)
case SLHDSA_SHA2_256S: return slhdsa_sha2_256s_sa_algo;
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_256F)
case SLHDSA_SHA2_256F: return slhdsa_sha2_256f_sa_algo;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_128S
case SLHDSA_SHAKE128S: return slhdsa_shake_128s_sa_algo;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_128F
case SLHDSA_SHAKE128F: return slhdsa_shake_128f_sa_algo;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192S
case SLHDSA_SHAKE192S: return slhdsa_shake_192s_sa_algo;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192F
case SLHDSA_SHAKE192F: return slhdsa_shake_192f_sa_algo;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256S
case SLHDSA_SHAKE256S: return slhdsa_shake_256s_sa_algo;
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256F
case SLHDSA_SHAKE256F: return slhdsa_shake_256f_sa_algo;
#endif
default: return (byte)invalid_sa_algo;
}
}
#endif /* WOLFSSL_HAVE_SLHDSA */
/* Decode the signature algorithm.
*
* input The encoded signature algorithm.
@@ -4812,6 +5107,10 @@ void InitSuites(Suites* suites, ProtocolVersion pv, int keySz, word16 haveRSA,
*/
void DecodeSigAlg(const byte* input, byte* hashAlgo, byte* hsType)
{
#ifdef WOLFSSL_HAVE_SLHDSA
byte slhType;
#endif
*hsType = invalid_sa_algo;
switch (input[0]) {
case NEW_SA_MAJOR:
@@ -4887,22 +5186,37 @@ void DecodeSigAlg(const byte* input, byte* hashAlgo, byte* hsType)
}
break;
#endif /* HAVE_FALCON */
#ifdef WOLFSSL_HAVE_MLDSA
#if defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_SLHDSA)
/* ML-DSA and SLH-DSA share the same major byte (0x09); their minor
* bytes are disjoint (ML-DSA 0x04-0x06, SLH-DSA 0x11-0x1C). */
case MLDSA_SA_MAJOR:
#ifdef WOLFSSL_HAVE_MLDSA
if (input[1] == MLDSA_44_SA_MINOR) {
*hsType = mldsa_44_sa_algo;
*hashAlgo = sha256_mac;
break;
}
else if (input[1] == MLDSA_65_SA_MINOR) {
*hsType = mldsa_65_sa_algo;
*hashAlgo = sha384_mac;
break;
}
else if (input[1] == MLDSA_87_SA_MINOR) {
*hsType = mldsa_87_sa_algo;
*hashAlgo = sha512_mac;
break;
}
#endif /* WOLFSSL_HAVE_MLDSA */
#ifdef WOLFSSL_HAVE_SLHDSA
slhType = SlhDsaSigMinorToType(input[1]);
if (slhType != (byte)invalid_sa_algo) {
*hsType = slhType;
/* Hash performed as part of sign/verify operation. */
*hashAlgo = sha512_mac;
}
#endif /* WOLFSSL_HAVE_SLHDSA */
break;
#endif /* WOLFSSL_HAVE_MLDSA */
#endif /* WOLFSSL_HAVE_MLDSA || WOLFSSL_HAVE_SLHDSA */
default:
*hashAlgo = input[0];
*hsType = input[1];
@@ -7366,7 +7680,8 @@ int SetSSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
ssl->ecdhCurveOID = ctx->ecdhCurveOID;
#endif
#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \
defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA)
defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \
defined(WOLFSSL_HAVE_SLHDSA)
ssl->pkCurveOID = ctx->pkCurveOID;
#endif
@@ -7412,7 +7727,8 @@ int SetSSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
ssl->options.haveECC = ctx->haveECC;
ssl->options.haveStaticECC = ctx->haveStaticECC;
ssl->options.haveFalconSig = ctx->haveFalconSig;
ssl->options.haveMlDsaSig = ctx->haveMlDsaSig;
ssl->options.haveMlDsaSig = ctx->haveMlDsaSig;
ssl->options.haveSlhDsaSig = ctx->haveSlhDsaSig;
#ifndef NO_PSK
ssl->options.havePSK = (word16)(ctx->havePSK);
@@ -8594,6 +8910,11 @@ void FreeKey(WOLFSSL* ssl, int type, void** pKey)
wc_MlDsaKey_Free((wc_MlDsaKey*)*pKey);
break;
#endif /* WOLFSSL_HAVE_MLDSA */
#if defined(WOLFSSL_HAVE_SLHDSA)
case DYNAMIC_TYPE_SLHDSA:
wc_SlhDsaKey_Free((SlhDsaKey*)*pKey);
break;
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifndef NO_DH
case DYNAMIC_TYPE_DH:
#if defined(WC_DH_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \
@@ -8702,6 +9023,11 @@ int AllocKey(WOLFSSL* ssl, int type, void** pKey)
sz = sizeof(wc_MlDsaKey);
break;
#endif /* WOLFSSL_HAVE_MLDSA */
#if defined(WOLFSSL_HAVE_SLHDSA)
case DYNAMIC_TYPE_SLHDSA:
sz = sizeof(SlhDsaKey);
break;
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifndef NO_DH
case DYNAMIC_TYPE_DH:
sz = sizeof(DhKey);
@@ -8820,6 +9146,17 @@ int AllocKey(WOLFSSL* ssl, int type, void** pKey)
ret = 0;
break;
#endif /* WOLFSSL_HAVE_MLDSA */
#if defined(WOLFSSL_HAVE_SLHDSA)
case DYNAMIC_TYPE_SLHDSA:
/* SLH-DSA requires the parameter set at init; use an always-present
* placeholder here and re-init with the real one once known.
* wc_SlhDsaKey_Init can fail before it zeroes the object, so clear
* it first, or the caller's FreeKey would run over raw memory. */
XMEMSET(*pKey, 0, sizeof(SlhDsaKey));
ret = wc_SlhDsaKey_Init((SlhDsaKey*)*pKey, WC_SLHDSA_DEFAULT_PARAM,
ssl->heap, ssl->devId);
break;
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef HAVE_CURVE448
case DYNAMIC_TYPE_CURVE448:
wc_curve448_init((curve448_key*)*pKey);
@@ -8866,7 +9203,8 @@ int AllocKey(WOLFSSL* ssl, int type, void** pKey)
#if (!defined(NO_CERTS) || !defined(WOLFSSL_NO_TLS12)) && \
(!defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \
defined(HAVE_CURVE25519) || defined(HAVE_ED448) || \
defined(HAVE_CURVE448) || defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA))
defined(HAVE_CURVE448) || defined(HAVE_FALCON) || \
defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_SLHDSA))
static int ReuseKey(WOLFSSL* ssl, int type, void* pKey)
{
int ret = 0;
@@ -8924,6 +9262,14 @@ static int ReuseKey(WOLFSSL* ssl, int type, void* pKey)
ret = wc_MlDsaKey_Init((wc_MlDsaKey*)pKey, NULL, INVALID_DEVID);
break;
#endif /* WOLFSSL_HAVE_MLDSA */
#if defined(WOLFSSL_HAVE_SLHDSA)
case DYNAMIC_TYPE_SLHDSA:
wc_SlhDsaKey_Free((SlhDsaKey*)pKey);
/* Re-init with placeholder param; caller re-inits with real one. */
ret = wc_SlhDsaKey_Init((SlhDsaKey*)pKey, WC_SLHDSA_DEFAULT_PARAM, NULL,
INVALID_DEVID);
break;
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifndef NO_DH
case DYNAMIC_TYPE_DH:
wc_FreeDhKey((DhKey*)pKey);
@@ -9353,6 +9699,14 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl)
XFREE(ssl->buffers.tls13CookieSecret.buffer, ssl->heap,
DYNAMIC_TYPE_COOKIE_PWD);
#endif
#ifdef WOLFSSL_TLS13_STREAM_CERT_VERIFY
/* Release any in-progress streamed CertificateVerify body (e.g. a
* connection torn down mid-send). */
XFREE(ssl->buffers.certVerifyMsg.buffer, ssl->heap,
DYNAMIC_TYPE_TMP_BUFFER);
ssl->buffers.certVerifyMsg.buffer = NULL;
ssl->buffers.certVerifyMsg.length = 0;
#endif
#ifdef WOLFSSL_DTLS
FreeSSL_DtlsResources(ssl);
#endif /* WOLFSSL_DTLS */
@@ -9378,6 +9732,10 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl)
FreeKey(ssl, DYNAMIC_TYPE_MLDSA, (void**)&ssl->peerMlDsaKey);
ssl->peerMlDsaKeyPresent = 0;
#endif
#if defined(WOLFSSL_HAVE_SLHDSA)
FreeKey(ssl, DYNAMIC_TYPE_SLHDSA, (void**)&ssl->peerSlhDsaKey);
ssl->peerSlhDsaKeyPresent = 0;
#endif
#if defined(HAVE_FALCON)
FreeKey(ssl, DYNAMIC_TYPE_FALCON, (void**)&ssl->peerFalconKey);
ssl->peerFalconKeyPresent = 0;
@@ -9611,6 +9969,10 @@ void FreeHandshakeResources(WOLFSSL* ssl)
FreeKey(ssl, DYNAMIC_TYPE_MLDSA, (void**)&ssl->peerMlDsaKey);
ssl->peerMlDsaKeyPresent = 0;
#endif /* WOLFSSL_HAVE_MLDSA */
#if defined(WOLFSSL_HAVE_SLHDSA)
FreeKey(ssl, DYNAMIC_TYPE_SLHDSA, (void**)&ssl->peerSlhDsaKey);
ssl->peerSlhDsaKeyPresent = 0;
#endif /* WOLFSSL_HAVE_SLHDSA */
}
#ifdef HAVE_ECC
@@ -17149,6 +17511,58 @@ static int ProcessPeerCertDecodeKey(WOLFSSL* ssl, ProcPeerCertArgs* args,
break;
}
#endif /* WOLFSSL_HAVE_MLDSA */
#if defined(WOLFSSL_HAVE_SLHDSA)
case SLH_DSA_SHA2_128Sk:
case SLH_DSA_SHA2_128Fk:
case SLH_DSA_SHA2_192Sk:
case SLH_DSA_SHA2_192Fk:
case SLH_DSA_SHA2_256Sk:
case SLH_DSA_SHA2_256Fk:
case SLH_DSA_SHAKE_128Sk:
case SLH_DSA_SHAKE_128Fk:
case SLH_DSA_SHAKE_192Sk:
case SLH_DSA_SHAKE_192Fk:
case SLH_DSA_SHAKE_256Sk:
case SLH_DSA_SHAKE_256Fk:
{
int keyRet = 0;
int slhParam = wc_SlhDsaOidToParam(args->dCert->keyOID);
if (slhParam < 0) {
ret = PEER_KEY_ERROR;
break;
}
if (ssl->peerSlhDsaKey == NULL) {
/* alloc/init on demand */
keyRet = AllocKey(ssl, DYNAMIC_TYPE_SLHDSA,
(void**)&ssl->peerSlhDsaKey);
} else if (ssl->peerSlhDsaKeyPresent) {
keyRet = ReuseKey(ssl, DYNAMIC_TYPE_SLHDSA,
ssl->peerSlhDsaKey);
ssl->peerSlhDsaKeyPresent = 0;
}
if (keyRet == 0) {
/* AllocKey/ReuseKey used a placeholder parameter
* set; re-init with the certificate's. */
wc_SlhDsaKey_Free(ssl->peerSlhDsaKey);
keyRet = wc_SlhDsaKey_Init(ssl->peerSlhDsaKey,
(enum SlhDsaParam)slhParam, ssl->heap, ssl->devId);
}
if (keyRet != 0 ||
wc_SlhDsaKey_ImportPublic(ssl->peerSlhDsaKey,
args->dCert->publicKey,
args->dCert->pubKeySize)
!= 0) {
ret = PEER_KEY_ERROR;
}
else {
ssl->peerSlhDsaKeyPresent = 1;
}
break;
}
#endif /* WOLFSSL_HAVE_SLHDSA */
default:
break;
}
@@ -19901,8 +20315,8 @@ static int DoHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
}
/* Cap the maximum size of a handshake message to something reasonable.
* By default is the maximum size of a certificate message assuming
* nine 2048-bit RSA certificates in the chain. */
* By default this is the maximum size of a certificate message,
* MAX_CERT_MSG_DEPTH certificates of MAX_CERT_WIRE_SZ each. */
if (size > MAX_HANDSHAKE_SZ) {
WOLFSSL_MSG("Handshake message too large");
WOLFSSL_ERROR_VERBOSE(HANDSHAKE_SIZE_ERROR);
@@ -30926,6 +31340,9 @@ static int ParseCipherList(Suites* suites,
#ifdef WOLFSSL_HAVE_MLDSA
haveSig |= SIG_MLDSA;
#endif /* WOLFSSL_HAVE_MLDSA */
#ifdef WOLFSSL_HAVE_SLHDSA
haveSig |= SIG_SLHDSA;
#endif /* WOLFSSL_HAVE_SLHDSA */
}
else
#ifdef BUILD_TLS_SM4_GCM_SM3
@@ -31123,7 +31540,8 @@ int SetCipherListFromBytes(WOLFSSL_CTX* ctx, Suites* suites, const byte* list,
int haveRSAsig = 0;
int haveECDSAsig = 0;
int haveFalconSig = 0;
int haveMlDsaSig = 0;
int haveMlDsaSig = 0;
int haveSlhDsaSig = 0;
int haveAnon = 0;
int tls1_3 = 0;
@@ -31198,6 +31616,9 @@ int SetCipherListFromBytes(WOLFSSL_CTX* ctx, Suites* suites, const byte* list,
#ifdef WOLFSSL_HAVE_MLDSA
haveMlDsaSig = 1;
#endif /* WOLFSSL_HAVE_MLDSA */
#ifdef WOLFSSL_HAVE_SLHDSA
haveSlhDsaSig = 1;
#endif /* WOLFSSL_HAVE_SLHDSA */
}
else
#endif /* WOLFSSL_TLS13 */
@@ -31236,6 +31657,7 @@ int SetCipherListFromBytes(WOLFSSL_CTX* ctx, Suites* suites, const byte* list,
haveSig |= haveRSAsig ? SIG_RSA : 0;
haveSig |= haveFalconSig ? SIG_FALCON : 0;
haveSig |= haveMlDsaSig ? SIG_MLDSA : 0;
haveSig |= haveSlhDsaSig ? SIG_SLHDSA : 0;
haveSig |= haveAnon ? SIG_ANON : 0;
InitSuitesHashSigAlgo(suites->hashSigAlgo, haveSig, 1, tls1_3,
keySz, &suites->hashSigAlgoSz);
@@ -31468,8 +31890,24 @@ static word32 MinRsaPssKeySz(int hashAlgo)
static int MatchSigAlgo(WOLFSSL* ssl, int hashAlgo, int sigAlgo)
{
#ifdef WOLFSSL_HAVE_SLHDSA
int slhParam;
#endif
(void)hashAlgo;
#ifdef WOLFSSL_HAVE_SLHDSA
slhParam = wc_SlhDsaOidToParam((int)ssl->pkCurveOID);
if (slhParam >= 0) {
/* Certificate has an SLH-DSA key, only match the scheme for that exact
* parameter set. SLH-DSA is TLS 1.3 only, so never match below it.
* Kept with the other certificate-type checks, above the RSA-PSS and
* Brainpool arms that key off ssl->options.sigAlgo. */
if (!IsAtLeastTLSv1_3(ssl->version))
return 0;
return sigAlgo == SlhDsaParamToType(slhParam);
}
#endif /* WOLFSSL_HAVE_SLHDSA */
#ifdef HAVE_ED25519
if (ssl->pkCurveOID == ECC_ED25519_OID) {
/* Certificate has Ed25519 key, only match with Ed25519 sig alg */
@@ -31742,6 +32180,15 @@ int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz,
break;
}
#endif /* WOLFSSL_HAVE_MLDSA */
#if defined(WOLFSSL_HAVE_SLHDSA)
if (wc_SlhDsaOidToParam((int)ssl->pkCurveOID) >= 0) {
/* Matched SLH-DSA - set chosen and finished. */
ssl->options.sigAlgo = sigAlgo;
ssl->options.hashAlgo = hashAlgo;
ret = 0;
break;
}
#endif /* WOLFSSL_HAVE_SLHDSA */
#if defined(HAVE_ECC_BRAINPOOL)
if (ssl->pkCurveOID == ECC_BRAINPOOLP256R1_OID ||
ssl->pkCurveOID == ECC_BRAINPOOLP384R1_OID ||
@@ -32103,13 +32550,16 @@ int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz,
* hsType Type of the key to create.
* heap Custom heap to use for mallocs/frees
* devId Id for device.
* slhParam SLH-DSA parameter set for DYNAMIC_TYPE_SLHDSA, whose set cannot
* be derived from an id or label. Pass -1 for other key types.
* return 0 on success.
* return ALGO_ID_E if slhParam is not a parameter set for an SLH-DSA key.
* return NOT_COMPILED_IN if algorithm type not supported.
* return MEMORY_E on memory allocation failure.
* return other internal error
*/
int CreateDevPrivateKey(void** pkey, byte* data, word32 length, int hsType,
int label, int id, void* heap, int devId)
int label, int id, void* heap, int devId, int slhParam)
{
int ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN);
@@ -32208,7 +32658,40 @@ int CreateDevPrivateKey(void** pkey, byte* data, word32 length, int hsType,
}
#endif
}
else if (hsType == DYNAMIC_TYPE_SLHDSA) {
#if defined(WOLFSSL_HAVE_SLHDSA)
SlhDsaKey* slhKey;
/* Unlike the other algorithms, the parameter set cannot be derived
* from a device-side identifier, so the caller supplies it. */
if (slhParam < 0) {
return ALGO_ID_E;
}
slhKey = (SlhDsaKey*)XMALLOC(sizeof(SlhDsaKey), heap,
DYNAMIC_TYPE_SLHDSA);
if (slhKey == NULL) {
return MEMORY_E;
}
if (label) {
ret = wc_SlhDsaKey_Init_label(slhKey, (enum SlhDsaParam)slhParam,
(char*)data, heap, devId);
}
else if (id) {
ret = wc_SlhDsaKey_Init_id(slhKey, (enum SlhDsaParam)slhParam,
data, (int)length, heap, devId);
}
if (ret == 0) {
*pkey = (void*)slhKey;
}
else {
XFREE(slhKey, heap, DYNAMIC_TYPE_SLHDSA);
}
#endif
}
(void)slhParam;
return ret;
}
#endif /* WOLF_PRIVATE_KEY_ID && !NO_CHECK_PRIVATE_KEY */
@@ -32236,6 +32719,9 @@ static int DecodePrivateKey_ex(WOLFSSL *ssl, byte keyType, const DerBuffer* key,
int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG);
int keySzDecoded;
word32 idx;
#if defined(WOLF_PRIVATE_KEY_ID) && !defined(NO_CHECK_PRIVATE_KEY)
int devSlhParam = -1;
#endif
/* make sure private key exists */
if (key == NULL || key->buffer == NULL) {
@@ -32271,12 +32757,24 @@ static int DecodePrivateKey_ex(WOLFSSL *ssl, byte keyType, const DerBuffer* key,
(keyType == mldsa_65_sa_algo) ||
(keyType == mldsa_87_sa_algo))
*hsType = DYNAMIC_TYPE_MLDSA;
#ifdef WOLFSSL_HAVE_SLHDSA
else if (IsSlhDsaSigAlgo(keyType)) {
*hsType = DYNAMIC_TYPE_SLHDSA;
/* The parameter set is not recoverable from a device-side
* identifier, so take it from the algorithm the key was loaded
* as. */
devSlhParam = SlhDsaTypeToParam(keyType);
if (devSlhParam < 0) {
ERROR_OUT(ALGO_ID_E, exit_dpk);
}
}
#endif
/* Create the private key */
ret = CreateDevPrivateKey(hsKey, key->buffer,
key->length, *hsType,
keyLabelSet, keyIdSet, ssl->heap,
keyDevId);
keyDevId, devSlhParam);
if (ret != 0) {
goto exit_dpk;
}
@@ -32356,6 +32854,20 @@ static int DecodePrivateKey_ex(WOLFSSL *ssl, byte keyType, const DerBuffer* key,
}
#else
ret = NOT_COMPILED_IN;
#endif
}
else if (*hsType == DYNAMIC_TYPE_SLHDSA) {
#if defined(WOLFSSL_HAVE_SLHDSA)
int slhSigSz = wc_SlhDsaKey_SigSize((SlhDsaKey*)*hsKey);
/* SLH-DSA has one fixed key size per parameter set, so there is no
* minimum-size policy to apply here. */
if (slhSigSz <= 0) {
ERROR_OUT(ALGO_ID_E, exit_dpk);
}
*sigLen = (word32)slhSigSz;
#else
ret = NOT_COMPILED_IN;
#endif
}
goto exit_dpk;
@@ -32698,6 +33210,57 @@ static int DecodePrivateKey_ex(WOLFSSL *ssl, byte keyType, const DerBuffer* key,
}
}
#endif /* WOLFSSL_HAVE_MLDSA */
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
#if !defined(NO_RSA) || defined(HAVE_ECC)
FreeKey(ssl, (int)*hsType, hsKey);
#endif
/* Unlike the ML-DSA block above, this matches only a concrete SLH-DSA
* algorithm, not keyType == 0. SLH-DSA key load always records the specific
* slhdsa_*_sa_algo in ssl->buffers.keyType (ProcessBufferTryDecodeSlhDsa),
* so keyType == 0 never denotes an SLH-DSA key here; the unknown-format
* (keyType == 0) probe is owned by the ML-DSA block. */
if (IsSlhDsaSigAlgo(keyType)) {
int slhParam = SlhDsaTypeToParam(keyType);
*hsType = DYNAMIC_TYPE_SLHDSA;
ret = AllocKey(ssl, (int)*hsType, hsKey);
if (ret != 0) {
goto exit_dpk;
}
/* AllocKey initialised the key with a placeholder parameter set;
* re-init with the parameter set matching the loaded key. */
wc_SlhDsaKey_Free((SlhDsaKey*)*hsKey);
ret = wc_SlhDsaKey_Init((SlhDsaKey*)*hsKey, (enum SlhDsaParam)slhParam,
ssl->heap, ssl->devId);
if (ret != 0) {
goto exit_dpk;
}
WOLFSSL_MSG("Trying SLH-DSA private key");
/* Set start of data to beginning of buffer. */
idx = 0;
PRIVATE_KEY_UNLOCK();
ret = wc_SlhDsaKey_PrivateKeyDecode(key->buffer, &idx,
(SlhDsaKey*)*hsKey, key->length);
PRIVATE_KEY_LOCK();
if (ret == 0) {
int slhSigSz;
WOLFSSL_MSG("Using SLH-DSA private key");
/* Return the maximum signature length. */
slhSigSz = wc_SlhDsaKey_SigSize((SlhDsaKey*)*hsKey);
if (slhSigSz <= 0) {
ERROR_OUT(ALGO_ID_E, exit_dpk);
}
*sigLen = (word32)slhSigSz;
goto exit_dpk;
}
}
#endif /* WOLFSSL_HAVE_SLHDSA */
(void)idx;
(void)keySzDecoded;
@@ -36510,7 +37073,12 @@ int SendCertificateVerify(WOLFSSL* ssl)
return 0; /* sent blank cert, can't verify */
}
args->sendSz = WC_MAX_CERT_VERIFY_SZ + MAX_MSG_EXTRA;
/* TLS 1.2 and earlier only ever produce a classic signature
* (RSA/ECC/EdDSA); PQC signatures are TLS 1.3 only and handled by
* SendTls13CertificateVerify. Size the record to the classic tier
* rather than WC_MAX_CERT_VERIFY_SZ, which balloons to ~50KB when
* SLH-DSA is enabled. Matches the ssl->buffers.sig sizing below. */
args->sendSz = MAX_ENCODED_CLASSIC_SIG_SZ + MAX_MSG_EXTRA;
if (IsEncryptionOn(ssl, 1)) {
args->sendSz += MAX_MSG_EXTRA;
}
@@ -36554,6 +37122,18 @@ int SendCertificateVerify(WOLFSSL* ssl)
ERROR_OUT(NO_PRIVATE_KEY, exit_scv);
}
/* No signature scheme below TLS 1.3 covers a post-quantum key, so
* such a key can never sign this message. Reject it here: the
* record is sized for a classic signature and the signing switches
* below have no PQC case, so continuing would send the reserved
* buffer's uninitialized tail. */
if (ssl->hsType == DYNAMIC_TYPE_FALCON ||
ssl->hsType == DYNAMIC_TYPE_MLDSA ||
ssl->hsType == DYNAMIC_TYPE_SLHDSA) {
WOLFSSL_MSG("PQC private key requires TLS 1.3");
ERROR_OUT(SIG_TYPE_E, exit_scv);
}
/* idx is used to track verify pointer offset to output */
args->idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
args->verify = &args->output[RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ];
+48 -1
View File
@@ -4408,6 +4408,42 @@ int wolfSSL_set_compression(WOLFSSL* ssl)
case mldsa_87_sa_algo:
*sigAlgo = ML_DSA_87k;
break;
case slhdsa_sha2_128s_sa_algo:
*sigAlgo = SLH_DSA_SHA2_128Sk;
break;
case slhdsa_sha2_128f_sa_algo:
*sigAlgo = SLH_DSA_SHA2_128Fk;
break;
case slhdsa_sha2_192s_sa_algo:
*sigAlgo = SLH_DSA_SHA2_192Sk;
break;
case slhdsa_sha2_192f_sa_algo:
*sigAlgo = SLH_DSA_SHA2_192Fk;
break;
case slhdsa_sha2_256s_sa_algo:
*sigAlgo = SLH_DSA_SHA2_256Sk;
break;
case slhdsa_sha2_256f_sa_algo:
*sigAlgo = SLH_DSA_SHA2_256Fk;
break;
case slhdsa_shake_128s_sa_algo:
*sigAlgo = SLH_DSA_SHAKE_128Sk;
break;
case slhdsa_shake_128f_sa_algo:
*sigAlgo = SLH_DSA_SHAKE_128Fk;
break;
case slhdsa_shake_192s_sa_algo:
*sigAlgo = SLH_DSA_SHAKE_192Sk;
break;
case slhdsa_shake_192f_sa_algo:
*sigAlgo = SLH_DSA_SHAKE_192Fk;
break;
case slhdsa_shake_256s_sa_algo:
*sigAlgo = SLH_DSA_SHAKE_256Sk;
break;
case slhdsa_shake_256f_sa_algo:
*sigAlgo = SLH_DSA_SHAKE_256Fk;
break;
case sm2_sa_algo:
*sigAlgo = SM2k;
break;
@@ -5624,6 +5660,16 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out,
ssl->options.certYieldPending = 0;
#endif
ssl->recordSzOverhead = 0;
#ifdef WOLFSSL_TLS13_STREAM_CERT_VERIFY
/* Drop any half-sent streamed CertificateVerify. Left in place, the
* resume guard in SendTls13CertificateVerify would fire on the next
* handshake and re-send the previous one's signature. */
XFREE(ssl->buffers.certVerifyMsg.buffer, ssl->heap,
DYNAMIC_TYPE_TMP_BUFFER);
ssl->buffers.certVerifyMsg.buffer = NULL;
ssl->buffers.certVerifyMsg.length = 0;
ssl->fragOffset = 0;
#endif
ssl->options.processReply = 0; /* doProcessInit */
ssl->options.havePeerVerify = 0;
ssl->options.havePeerCert = 0;
@@ -8752,7 +8798,8 @@ WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx)
ssl->options.haveECC = ctx->haveECC;
ssl->options.haveStaticECC = ctx->haveStaticECC;
ssl->options.haveFalconSig = ctx->haveFalconSig;
ssl->options.haveMlDsaSig = ctx->haveMlDsaSig;
ssl->options.haveMlDsaSig = ctx->haveMlDsaSig;
ssl->options.haveSlhDsaSig = ctx->haveSlhDsaSig;
#ifdef WOLFSSL_DUAL_ALG_CERTS
#ifndef WOLFSSL_BLIND_PRIVATE_KEY
ssl->buffers.altKey = ctx->altPrivateKey;
+94 -2
View File
@@ -52,6 +52,7 @@ static int check_cert_key_dev(word32 keyOID, byte* privKey, word32 privSz,
{
int ret = 0;
int type = 0;
int slhParam = -1;
void *pkey = NULL;
if (privKey == NULL) {
@@ -89,11 +90,33 @@ static int check_cert_key_dev(word32 keyOID, byte* privKey, word32 privSz,
case FALCON_LEVEL5k:
type = DYNAMIC_TYPE_FALCON;
break;
#endif
#if defined(WOLFSSL_HAVE_SLHDSA)
case SLH_DSA_SHA2_128Sk:
case SLH_DSA_SHA2_128Fk:
case SLH_DSA_SHA2_192Sk:
case SLH_DSA_SHA2_192Fk:
case SLH_DSA_SHA2_256Sk:
case SLH_DSA_SHA2_256Fk:
case SLH_DSA_SHAKE_128Sk:
case SLH_DSA_SHAKE_128Fk:
case SLH_DSA_SHAKE_192Sk:
case SLH_DSA_SHAKE_192Fk:
case SLH_DSA_SHAKE_256Sk:
case SLH_DSA_SHAKE_256Fk:
type = DYNAMIC_TYPE_SLHDSA;
slhParam = wc_SlhDsaOidToParam((int)keyOID);
if (slhParam < 0) {
ret = ALGO_ID_E;
}
break;
#endif
}
ret = CreateDevPrivateKey(&pkey, privKey, privSz, type, label, id, heap,
devId);
if (ret == 0) {
ret = CreateDevPrivateKey(&pkey, privKey, privSz, type, label, id,
heap, devId, slhParam);
}
}
#ifdef WOLF_CRYPTO_CB
if (ret == 0) {
@@ -131,6 +154,23 @@ static int check_cert_key_dev(word32 keyOID, byte* privKey, word32 privSz,
ret = wc_CryptoCb_PqcSignatureCheckPrivKey(pkey,
WC_PQC_SIG_TYPE_FALCON, pubKey, pubSz);
break;
#endif
#if defined(WOLFSSL_HAVE_SLHDSA)
case SLH_DSA_SHA2_128Sk:
case SLH_DSA_SHA2_128Fk:
case SLH_DSA_SHA2_192Sk:
case SLH_DSA_SHA2_192Fk:
case SLH_DSA_SHA2_256Sk:
case SLH_DSA_SHA2_256Fk:
case SLH_DSA_SHAKE_128Sk:
case SLH_DSA_SHAKE_128Fk:
case SLH_DSA_SHAKE_192Sk:
case SLH_DSA_SHAKE_192Fk:
case SLH_DSA_SHAKE_256Sk:
case SLH_DSA_SHAKE_256Fk:
ret = wc_CryptoCb_PqcSignatureCheckPrivKey(pkey,
WC_PQC_SIG_TYPE_SLHDSA, pubKey, pubSz);
break;
#endif
default:
ret = 0;
@@ -174,6 +214,22 @@ static int check_cert_key_dev(word32 keyOID, byte* privKey, word32 privSz,
case FALCON_LEVEL5k:
wc_falcon_free((falcon_key*)pkey);
break;
#endif
#if defined(WOLFSSL_HAVE_SLHDSA)
case SLH_DSA_SHA2_128Sk:
case SLH_DSA_SHA2_128Fk:
case SLH_DSA_SHA2_192Sk:
case SLH_DSA_SHA2_192Fk:
case SLH_DSA_SHA2_256Sk:
case SLH_DSA_SHA2_256Fk:
case SLH_DSA_SHAKE_128Sk:
case SLH_DSA_SHAKE_128Fk:
case SLH_DSA_SHAKE_192Sk:
case SLH_DSA_SHAKE_192Fk:
case SLH_DSA_SHAKE_256Sk:
case SLH_DSA_SHAKE_256Fk:
wc_SlhDsaKey_Free((SlhDsaKey*)pkey);
break;
#endif
default:
WC_DO_NOTHING;
@@ -2022,6 +2078,42 @@ static int SaToNid(byte sa, int* nid)
case mldsa_87_sa_algo:
*nid = CTC_ML_DSA_87;
break;
case slhdsa_sha2_128s_sa_algo:
*nid = CTC_SLH_DSA_SHA2_128S;
break;
case slhdsa_sha2_128f_sa_algo:
*nid = CTC_SLH_DSA_SHA2_128F;
break;
case slhdsa_sha2_192s_sa_algo:
*nid = CTC_SLH_DSA_SHA2_192S;
break;
case slhdsa_sha2_192f_sa_algo:
*nid = CTC_SLH_DSA_SHA2_192F;
break;
case slhdsa_sha2_256s_sa_algo:
*nid = CTC_SLH_DSA_SHA2_256S;
break;
case slhdsa_sha2_256f_sa_algo:
*nid = CTC_SLH_DSA_SHA2_256F;
break;
case slhdsa_shake_128s_sa_algo:
*nid = CTC_SLH_DSA_SHAKE_128S;
break;
case slhdsa_shake_128f_sa_algo:
*nid = CTC_SLH_DSA_SHAKE_128F;
break;
case slhdsa_shake_192s_sa_algo:
*nid = CTC_SLH_DSA_SHAKE_192S;
break;
case slhdsa_shake_192f_sa_algo:
*nid = CTC_SLH_DSA_SHAKE_192F;
break;
case slhdsa_shake_256s_sa_algo:
*nid = CTC_SLH_DSA_SHAKE_256S;
break;
case slhdsa_shake_256f_sa_algo:
*nid = CTC_SLH_DSA_SHAKE_256F;
break;
case sm2_sa_algo:
*nid = WC_NID_sm2;
break;
+241 -2
View File
@@ -1045,6 +1045,97 @@ static int ProcessBufferTryDecodeMlDsa(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
}
#endif /* WOLFSSL_HAVE_MLDSA */
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
/* Try to decode the DER encoding as an SLH-DSA private key.
*
* @param [in] ctx SSL context object.
* @param [in] ssl SSL object.
* @param [in] der DER encoding.
* @param [in, out] keyFormat On in, expected format. 0 means unknown.
* @param [in] heap Dynamic memory allocation hint.
* @param [out] keyType Type of key (an slhdsa_*_sa_algo).
* @param [out] keySize Size of the private key.
* @return 0 on success or when not an SLH-DSA key (with keyFormat 0).
*/
static int ProcessBufferTryDecodeSlhDsa(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
DerBuffer* der, int* keyFormat, void* heap, byte* keyType, int* keySize)
{
int ret;
word32 idx;
SlhDsaKey* key;
int keyTypeTemp = 0;
int keySizeTemp = 0;
(void)ctx;
(void)ssl;
/* Allocate an SLH-DSA key to parse into. */
key = (SlhDsaKey*)XMALLOC(sizeof(SlhDsaKey), heap, DYNAMIC_TYPE_SLHDSA);
if (key == NULL) {
return MEMORY_E;
}
/* wc_SlhDsaKey_Init returns NOT_COMPILED_IN before it zeroes the key, so
* clear it here to keep the unconditional Free below well defined. */
XMEMSET(key, 0, sizeof(SlhDsaKey));
/* Initialise with an always-present placeholder parameter set;
* wc_SlhDsaKey_PrivateKeyDecode selects the real one from the key's
* algorithm OID. */
ret = wc_SlhDsaKey_Init(key, WC_SLHDSA_DEFAULT_PARAM, heap, INVALID_DEVID);
if (ret == 0) {
idx = 0;
PRIVATE_KEY_UNLOCK();
ret = wc_SlhDsaKey_PrivateKeyDecode(der->buffer, &idx, key,
der->length);
PRIVATE_KEY_LOCK();
if (ret == 0) {
int param = (int)key->params->param;
keyTypeTemp = SlhDsaParamToType(param);
if (keyTypeTemp == (byte)invalid_sa_algo) {
ret = ALGO_ID_E;
}
else {
keySizeTemp = wc_SlhDsaKey_PrivateSize(key);
if (keySizeTemp <= 0) {
ret = ALGO_ID_E;
}
}
if (ret == 0) {
int oidSum = wc_SlhDsaParamToOid((enum SlhDsaParam)param);
/* Negative for an unknown or disabled parameter set; storing it
* would leave a nonsense key format behind a zero return. */
if (oidSum <= 0) {
ret = ALGO_ID_E;
}
else {
*keyFormat = oidSum;
*keyType = (byte)keyTypeTemp;
*keySize = keySizeTemp;
}
}
}
else if (*keyFormat == 0) {
WOLFSSL_MSG("Not an SLH-DSA key");
/* Unknown format wasn't SLH-DSA, so keep trying other formats. */
ret = 0;
}
}
/* Free any data allocated by wc_SlhDsaKey_Init(). This also runs when Init
* failed part way through, releasing any hash objects allocated before the
* failure; it is a no-op once the key's parameter set has been cleared. */
wc_SlhDsaKey_Free(key);
/* Dispose of allocated key. */
XFREE(key, heap, DYNAMIC_TYPE_SLHDSA);
return ret;
}
#endif /* WOLFSSL_HAVE_SLHDSA */
/* Try to decode DER data is a known private key.
*
* Checks size meets minimum for key type.
@@ -1188,6 +1279,27 @@ static int ProcessBufferTryDecode(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
matchAnyKey = 1;
}
#endif /* WOLFSSL_HAVE_MLDSA */
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
/* Try SLH-DSA if key format is an SLH-DSA key OID or yet unknown. */
if ((ret == 0) &&
((*keyFormat == 0) ||
(*keyFormat == SLH_DSA_SHA2_128Sk) ||
(*keyFormat == SLH_DSA_SHA2_128Fk) ||
(*keyFormat == SLH_DSA_SHA2_192Sk) ||
(*keyFormat == SLH_DSA_SHA2_192Fk) ||
(*keyFormat == SLH_DSA_SHA2_256Sk) ||
(*keyFormat == SLH_DSA_SHA2_256Fk) ||
(*keyFormat == SLH_DSA_SHAKE_128Sk) ||
(*keyFormat == SLH_DSA_SHAKE_128Fk) ||
(*keyFormat == SLH_DSA_SHAKE_192Sk) ||
(*keyFormat == SLH_DSA_SHAKE_192Fk) ||
(*keyFormat == SLH_DSA_SHAKE_256Sk) ||
(*keyFormat == SLH_DSA_SHAKE_256Fk))) {
ret = ProcessBufferTryDecodeSlhDsa(ctx, ssl, der, keyFormat, heap,
keyType, keySz);
matchAnyKey = 1;
}
#endif /* WOLFSSL_HAVE_SLHDSA */
/* Check we know the format. */
if ((ret == 0) &&
@@ -1513,6 +1625,27 @@ static void wolfssl_set_have_from_key_oid(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
}
break;
#endif /* WOLFSSL_HAVE_MLDSA */
#ifdef WOLFSSL_HAVE_SLHDSA
case SLH_DSA_SHA2_128Sk:
case SLH_DSA_SHA2_128Fk:
case SLH_DSA_SHA2_192Sk:
case SLH_DSA_SHA2_192Fk:
case SLH_DSA_SHA2_256Sk:
case SLH_DSA_SHA2_256Fk:
case SLH_DSA_SHAKE_128Sk:
case SLH_DSA_SHAKE_128Fk:
case SLH_DSA_SHAKE_192Sk:
case SLH_DSA_SHAKE_192Fk:
case SLH_DSA_SHAKE_256Sk:
case SLH_DSA_SHAKE_256Fk:
if (ssl != NULL) {
ssl->options.haveSlhDsaSig = 1;
}
else {
ctx->haveSlhDsaSig = 1;
}
break;
#endif /* WOLFSSL_HAVE_SLHDSA */
default:
WOLFSSL_MSG("Cert key not supported");
break;
@@ -1535,6 +1668,7 @@ static void ProcessBufferCertSetHave(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
ssl->options.haveECDSAsig = 0;
ssl->options.haveFalconSig = 0;
ssl->options.haveMlDsaSig = 0;
ssl->options.haveSlhDsaSig = 0;
}
/* Set which signature we have based on the type in the cert. */
@@ -1589,6 +1723,28 @@ static void ProcessBufferCertSetHave(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
ctx->haveMlDsaSig = 1;
}
break;
#endif
#ifdef WOLFSSL_HAVE_SLHDSA
case CTC_SLH_DSA_SHA2_128S:
case CTC_SLH_DSA_SHA2_128F:
case CTC_SLH_DSA_SHA2_192S:
case CTC_SLH_DSA_SHA2_192F:
case CTC_SLH_DSA_SHA2_256S:
case CTC_SLH_DSA_SHA2_256F:
case CTC_SLH_DSA_SHAKE_128S:
case CTC_SLH_DSA_SHAKE_128F:
case CTC_SLH_DSA_SHAKE_192S:
case CTC_SLH_DSA_SHAKE_192F:
case CTC_SLH_DSA_SHAKE_256S:
case CTC_SLH_DSA_SHAKE_256F:
WOLFSSL_MSG("SLH-DSA cert signature");
if (ssl) {
ssl->options.haveSlhDsaSig = 1;
}
else if (ctx) {
ctx->haveSlhDsaSig = 1;
}
break;
#endif
default:
WOLFSSL_MSG("Cert signature not supported");
@@ -1596,9 +1752,11 @@ static void ProcessBufferCertSetHave(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
}
#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \
defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || !defined(NO_RSA)
defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \
defined(WOLFSSL_HAVE_SLHDSA) || !defined(NO_RSA)
#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \
defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA)
defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \
defined(WOLFSSL_HAVE_SLHDSA)
/* Set the private key curve OID. */
if (ssl != NULL) {
ssl->pkCurveOID = cert->pkCurveOID;
@@ -1826,6 +1984,48 @@ static int ProcessBufferCertPublicKey(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
}
break;
#endif /* WOLFSSL_HAVE_MLDSA */
#if defined(WOLFSSL_HAVE_SLHDSA)
case SLH_DSA_SHA2_128Sk:
case SLH_DSA_SHA2_128Fk:
case SLH_DSA_SHA2_192Sk:
case SLH_DSA_SHA2_192Fk:
case SLH_DSA_SHA2_256Sk:
case SLH_DSA_SHA2_256Fk:
case SLH_DSA_SHAKE_128Sk:
case SLH_DSA_SHAKE_128Fk:
case SLH_DSA_SHAKE_192Sk:
case SLH_DSA_SHAKE_192Fk:
case SLH_DSA_SHAKE_256Sk:
case SLH_DSA_SHAKE_256Fk:
{
int slhParam = wc_SlhDsaOidToParam((int)cert->keyOID);
int keySzTemp;
if (slhParam < 0) {
ret = ALGO_ID_E;
break;
}
keyType = SlhDsaParamToType(slhParam);
if (keyType == (byte)invalid_sa_algo) {
ret = ALGO_ID_E;
break;
}
/* SLH-DSA has fixed, small public keys and no minimum-size
* option; store the public key size for reference. checkKeySz is
* unused for SLH-DSA (this is the only key type without a size
* check, so mark it used to avoid -Wunused-parameter in a build
* where SLH-DSA is the only certificate signature algorithm). */
keySzTemp = wc_SlhDsaKey_PublicSizeFromParam(
(enum SlhDsaParam)slhParam);
if (keySzTemp <= 0) {
ret = ALGO_ID_E;
break;
}
keySz = keySzTemp;
(void)checkKeySz;
break;
}
#endif /* WOLFSSL_HAVE_SLHDSA */
default:
WOLFSSL_MSG("No key size check done on public key in certificate");
@@ -2036,6 +2236,45 @@ static int ProcessBufferCertAltPublicKey(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
}
break;
#endif /* WOLFSSL_HAVE_MLDSA */
#if defined(WOLFSSL_HAVE_SLHDSA)
case SLH_DSA_SHA2_128Sk:
case SLH_DSA_SHA2_128Fk:
case SLH_DSA_SHA2_192Sk:
case SLH_DSA_SHA2_192Fk:
case SLH_DSA_SHA2_256Sk:
case SLH_DSA_SHA2_256Fk:
case SLH_DSA_SHAKE_128Sk:
case SLH_DSA_SHAKE_128Fk:
case SLH_DSA_SHAKE_192Sk:
case SLH_DSA_SHAKE_192Fk:
case SLH_DSA_SHAKE_256Sk:
case SLH_DSA_SHAKE_256Fk:
{
/* This switch is over the alternative key's OID, so the
* parameter set comes from sapkiOID; keyOID describes the native
* key and would name a different algorithm entirely. */
int slhParam = wc_SlhDsaOidToParam((int)cert->sapkiOID);
int keySzTemp;
if (slhParam < 0) {
ret = ALGO_ID_E;
break;
}
keyType = SlhDsaParamToType(slhParam);
if (keyType == (byte)invalid_sa_algo) {
ret = ALGO_ID_E;
break;
}
keySzTemp = wc_SlhDsaKey_PublicSizeFromParam(
(enum SlhDsaParam)slhParam);
if (keySzTemp <= 0) {
ret = ALGO_ID_E;
break;
}
keySz = keySzTemp;
break;
}
#endif /* WOLFSSL_HAVE_SLHDSA */
default:
/* In this case, there was an OID that we didn't recognize.
+766 -134
View File
File diff suppressed because it is too large Load Diff
+21 -2
View File
@@ -1695,7 +1695,10 @@ static int test_dual_alg_ecdsa_mldsa(void)
alt_pub_sz, 1);
ExpectIntGT(alt_pub_sz, 0);
alt_sig_alg_sz = SetAlgoID(CTC_SHA256wECDSA, alt_sig_alg, oidSigType, 0);
/* The alternative signature below is made with the ML-DSA key, so the
* altSignatureAlgorithm extension has to name ML-DSA too; otherwise the
* certificate declares one algorithm and carries another. */
alt_sig_alg_sz = SetAlgoID(CTC_ML_DSA_44, alt_sig_alg, oidSigType, 0);
ExpectIntGT(alt_sig_alg_sz, 0);
/**
@@ -1740,6 +1743,12 @@ static int test_dual_alg_ecdsa_mldsa(void)
ret = wc_ParseCert(&d_cert, CERT_TYPE, NO_VERIFY, NULL);
ExpectIntEQ(ret, 0);
/* An undersized output buffer must never look like success. The encoder
* surfaces this as WOLFSSL_FAILURE, which is 0, so callers have to treat
* every non-positive return as failure; ParseCertRelative depends on that
* to avoid skipping the alternative-signature check entirely. */
ExpectIntLE(wc_GeneratePreTBS(&d_cert, tbs_der, 1), 0);
tbs_der_sz = wc_GeneratePreTBS(&d_cert, tbs_der, tbs_der_sz);
ExpectIntGT(tbs_der_sz, 0);
@@ -1770,6 +1779,15 @@ static int test_dual_alg_ecdsa_mldsa(void)
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
}
/* Verify the certificate so ParseCertRelative runs its alternative
* signature check, which builds the PreTBS and confirms the alt signature
* against the SAPKI. Without this the alt-verify path is never executed. */
if (cm != NULL && final_der_sz > 0) {
ret = wolfSSL_CertManagerVerifyBuffer(cm, final_der, final_der_sz,
WOLFSSL_FILETYPE_ASN1);
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
}
if (cm != NULL) {
wolfSSL_CertManagerFree(cm);
cm = NULL;
@@ -19699,7 +19717,8 @@ static int test_wolfSSL_sigalg_info(void)
byte hashSigAlgo[WOLFSSL_MAX_SIGALGO];
word16 len = 0;
word16 idx = 0;
int allSigAlgs = SIG_ECDSA | SIG_RSA | SIG_SM2 | SIG_FALCON | SIG_MLDSA;
int allSigAlgs = SIG_ECDSA | SIG_RSA | SIG_SM2 | SIG_FALCON | SIG_MLDSA |
SIG_SLHDSA;
#if !defined(NO_SHA) && (!defined(NO_OLD_TLS) || defined(WOLFSSL_ALLOW_TLS_SHA1))
int sawSha1 = 0;
#endif
+581
View File
@@ -34,8 +34,12 @@
#include <wolfssl/wolfcrypt/types.h>
#include <wolfssl/wolfcrypt/asn.h>
#include <wolfssl/wolfcrypt/asn_public.h>
#include <wolfssl/ssl.h>
#include <wolfssl/internal.h>
#include <tests/api/api.h>
#include <tests/api/test_slhdsa.h>
#include <tests/utils.h>
#include <wolfssl/wolfcrypt/cryptocb.h>
#ifdef WOLFSSL_HAVE_SLHDSA
@@ -3290,3 +3294,580 @@ int test_wc_SlhdsaFeatureCoverage(void)
#undef TEST_SLHDSA_DEFAULT_PUB_LEN
#undef TEST_SLHDSA_DEFAULT_SEED_LEN
#endif
/* Gate the streamed-CertificateVerify WANT_WRITE resume test: needs an SLH-DSA
* 128f leaf (the ~17KB signature spans multiple records, taking the streaming
* send path) chained to the shared 128s root, TLS 1.3 with the streaming path
* compiled in, a signing (not verify-only) build, and the memio test harness.
* Works with whichever 128f/128s hash family is compiled in (SHAKE preferred,
* else SHA2) so a SHAKE-disabled (--enable-slhdsa=sha2) build still runs it. */
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && \
defined(WOLFSSL_TLS13) && defined(WOLFSSL_TLS13_STREAM_CERT_VERIFY) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
((defined(WOLFSSL_SLHDSA_PARAM_128F) && \
defined(WOLFSSL_SLHDSA_PARAM_128S)) || \
(defined(WOLFSSL_SLHDSA_PARAM_SHA2_128F) && \
defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S)))
#define TEST_SLHDSA_STREAM_CV_WANT_WRITE
#if defined(WOLFSSL_SLHDSA_PARAM_128F) && defined(WOLFSSL_SLHDSA_PARAM_128S)
#define SLHDSA_CV_FAM "shake"
#else
#define SLHDSA_CV_FAM "sha2"
#endif
#endif
#ifdef TEST_SLHDSA_STREAM_CV_WANT_WRITE
/* The server's SLH-DSA-128f flight puts two oversized handshake messages on the
* wire, the certificate and the CertificateVerify. The callback counts
* oversized records and returns WANT_WRITE on a chosen one, interrupting a
* streamed CertificateVerify mid-flight, which the blocking .conf handshakes
* never do. Note the scope: where the flight is flushed as a single write the
* retry happens below SendTls13CertificateVerify, so this does not by itself
* reach the fragOffset != 0 resume branch. */
struct slhdsa_wwrite_ctx {
int bigWritesUntilWantWrite; /* 1-based index of the record to interrupt */
int repeatStalls; /* WANT_WRITEs to fire on that record before it goes */
int bigSeen; /* oversized records written so far */
struct test_memio_ctx* memio;
};
static int slhdsa_want_write_send_cb(WOLFSSL* ssl, char* data, int sz,
void* ctx)
{
struct slhdsa_wwrite_ctx* ww = (struct slhdsa_wwrite_ctx*)ctx;
if (sz > 8000) {
ww->bigSeen++;
/* Only the record named by bigWritesUntilWantWrite is interrupted, and
* it is stalled repeatStalls times so the same record has to be
* re-sent more than once. */
if (ww->bigWritesUntilWantWrite == ww->bigSeen &&
ww->repeatStalls > 0) {
ww->repeatStalls--;
ww->bigSeen--; /* the record has not been written yet */
return WOLFSSL_CBIO_ERR_WANT_WRITE;
}
}
return test_memio_write_cb(ssl, data, sz, ww->memio);
}
/* Run one SLH-DSA-128f TLS 1.3 handshake over memio. targetBig names the
* oversized record to interrupt (1-based, 0 to interrupt none) and stalls is
* how many WANT_WRITEs to fire on it. Returns 0 when the handshake completed.
* *bigSeen receives the number of oversized records the server wrote and
* *stallsLeft the stalls that were never consumed.
*
* Two passes are needed because the server's record batching is build
* dependent: some configurations flush the whole flight as one oversized
* write, others emit the Certificate and the CertificateVerify separately. The
* CertificateVerify is always the LAST oversized record, so callers count them
* with targetBig 0 first and then interrupt that index. Interrupting the
* Certificate instead trips a known memio harness limitation. */
static int slhdsa_cv_stall_handshake(int targetBig, int stalls, int* bigSeen,
int* stallsLeft)
{
int ret = -1;
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
struct test_memio_ctx test_ctx;
struct slhdsa_wwrite_ctx ww_s;
const char* svrCert =
"./certs/slhdsa/server-slhdsa-" SLHDSA_CV_FAM "-128f.pem";
const char* svrKey =
"./certs/slhdsa/server-slhdsa-" SLHDSA_CV_FAM "-128f-priv.pem";
const char* caCert =
"./certs/slhdsa/root-slhdsa-" SLHDSA_CV_FAM "-128s.pem";
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
XMEMSET(&ww_s, 0, sizeof(ww_s));
ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method());
ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
if (ctx_s == NULL || ctx_c == NULL) {
goto cleanup;
}
if (wolfSSL_CTX_use_certificate_chain_file(ctx_s, svrCert)
!= WOLFSSL_SUCCESS) {
goto cleanup;
}
if (wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKey, WOLFSSL_FILETYPE_PEM)
!= WOLFSSL_SUCCESS) {
goto cleanup;
}
if (wolfSSL_CTX_load_verify_locations(ctx_c, caCert, NULL)
!= WOLFSSL_SUCCESS) {
goto cleanup;
}
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
ssl_s = wolfSSL_new(ctx_s);
ssl_c = wolfSSL_new(ctx_c);
if (ssl_s == NULL || ssl_c == NULL) {
goto cleanup;
}
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
ww_s.bigWritesUntilWantWrite = targetBig;
ww_s.repeatStalls = stalls;
ww_s.memio = &test_ctx;
wolfSSL_SetIOWriteCtx(ssl_s, &ww_s);
wolfSSL_SSLSetIOSend(ssl_s, slhdsa_want_write_send_cb);
ret = test_memio_do_handshake(ssl_c, ssl_s, 100, NULL);
if (ret == 0 && (!wolfSSL_is_init_finished(ssl_c) ||
!wolfSSL_is_init_finished(ssl_s))) {
ret = -1;
}
cleanup:
if (bigSeen != NULL)
*bigSeen = ww_s.bigSeen;
if (stallsLeft != NULL)
*stallsLeft = ww_s.repeatStalls;
wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
return ret;
}
#endif /* TEST_SLHDSA_STREAM_CV_WANT_WRITE */
int test_slhdsa_tls13_certverify_want_write(void)
{
EXPECT_DECLS;
#ifdef TEST_SLHDSA_STREAM_CV_WANT_WRITE
int bigSeen = 0;
int stallsLeft = 0;
/* Count the server's oversized records without interrupting anything. */
ExpectIntEQ(slhdsa_cv_stall_handshake(0, 0, &bigSeen, NULL), 0);
ExpectIntGT(bigSeen, 0);
/* Interrupt the CertificateVerify, which is the last oversized record,
* once. Completing anyway means the retried send re-emitted identical
* bytes and the transcript stayed consistent. */
if (EXPECT_SUCCESS()) {
ExpectIntEQ(slhdsa_cv_stall_handshake(bigSeen, 1, NULL, &stallsLeft),
0);
/* The stall has to have been consumed, otherwise the callback never
* interrupted anything and the test proved nothing. */
ExpectIntEQ(stallsLeft, 0);
}
#endif /* TEST_SLHDSA_STREAM_CV_WANT_WRITE */
return EXPECT_RESULT();
}
/* The single-stall test above leaves the repeated case open: a non-blocking
* peer can return WANT_WRITE many times for the same record. Stall one
* CertificateVerify record several times, which must keep emitting identical
* bytes. */
int test_slhdsa_tls13_certverify_multi_stall(void)
{
EXPECT_DECLS;
#ifdef TEST_SLHDSA_STREAM_CV_WANT_WRITE
int bigSeen = 0;
int stallsLeft = 0;
ExpectIntEQ(slhdsa_cv_stall_handshake(0, 0, &bigSeen, NULL), 0);
ExpectIntGT(bigSeen, 0);
/* Fire five WANT_WRITEs on the same CertificateVerify fragment. */
if (EXPECT_SUCCESS()) {
ExpectIntEQ(slhdsa_cv_stall_handshake(bigSeen, 5, NULL, &stallsLeft),
0);
ExpectIntEQ(stallsLeft, 0);
}
#endif /* TEST_SLHDSA_STREAM_CV_WANT_WRITE */
return EXPECT_RESULT();
}
#ifdef TEST_SLHDSA_STREAM_CV_WANT_WRITE
#undef TEST_SLHDSA_STREAM_CV_WANT_WRITE
#undef SLHDSA_CV_FAM
#endif
/* The streamed CertificateVerify path is enabled for ML-DSA and Falcon too,
* not just SLH-DSA: it triggers whenever the body exceeds one record, which a
* negotiated max_fragment_length makes reachable with a much smaller
* signature. Drive it with an ML-DSA leaf under a 512 byte fragment limit so
* the streaming send is covered for a non-SLH-DSA algorithm. */
#if defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_SIGN) && \
!defined(WOLFSSL_MLDSA_NO_VERIFY) && defined(WOLFSSL_TLS13) && \
defined(WOLFSSL_TLS13_STREAM_CERT_VERIFY) && \
defined(HAVE_MAX_FRAGMENT) && !defined(WOLFSSL_NO_ML_DSA_65) && \
!defined(WOLFSSL_NO_ML_DSA_87) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
#define TEST_MLDSA_STREAM_CV_MAXFRAG
#endif
int test_mldsa_tls13_certverify_maxfrag_stream(void)
{
EXPECT_DECLS;
#ifdef TEST_MLDSA_STREAM_CV_MAXFRAG
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
struct test_memio_ctx test_ctx;
const char msg[] = "maxfrag mldsa stream";
char readBuf[sizeof(msg)];
const char* svrCert = "./certs/mldsa/mldsa65-leaf87ca-cert.pem";
const char* svrKey = "./certs/mldsa/mldsa65-leaf87ca-key.pem";
const char* caCert = "./certs/mldsa/mldsa87-ca-cert.pem";
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx_s, svrCert),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKey,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, caCert, NULL),
WOLFSSL_SUCCESS);
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
/* 512 byte plaintext limit, well under the ML-DSA-65 signature, so the
* server's CertificateVerify must be streamed across records. */
ExpectIntEQ(wolfSSL_UseMaxFragment(ssl_c, WOLFSSL_MFL_2_9),
WOLFSSL_SUCCESS);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 100, NULL), 0);
ExpectTrue(wolfSSL_is_init_finished(ssl_c));
ExpectTrue(wolfSSL_is_init_finished(ssl_s));
XMEMSET(readBuf, 0, sizeof(readBuf));
ExpectIntEQ(wolfSSL_write(ssl_s, msg, (int)sizeof(msg)), (int)sizeof(msg));
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, (int)sizeof(readBuf)),
(int)sizeof(msg));
ExpectStrEQ(readBuf, msg);
wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif /* TEST_MLDSA_STREAM_CV_MAXFRAG */
return EXPECT_RESULT();
}
#ifdef TEST_MLDSA_STREAM_CV_MAXFRAG
#undef TEST_MLDSA_STREAM_CV_MAXFRAG
#endif
/* Not built with WOLFSSL_BLIND_PRIVATE_KEY: wolfSSL_CTX_check_private_key
* unblinds ctx->privateKey with ctx->privateKeyMask, and a key referenced by
* id or label never gets a mask because blinding only happens when real key
* material is loaded. The unblind then returns NULL and the check fails before
* check_cert_key runs. That is independent of the key algorithm, an RSA device
* key behaves the same way, so it is not something to work around here. */
#if defined(WOLFSSL_HAVE_SLHDSA) && defined(WOLF_PRIVATE_KEY_ID) && \
!defined(NO_CHECK_PRIVATE_KEY) && defined(WOLF_CRYPTO_CB) && \
!defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && \
!defined(WOLFSSL_BLIND_PRIVATE_KEY) && \
defined(WOLFSSL_SLHDSA_PARAM_128S) && !defined(NO_TLS)
#define TEST_SLHDSA_DEV_KEY
#define TEST_SLHDSA_DEV_DEVID 0x51484453
#endif
#ifdef TEST_SLHDSA_DEV_KEY
/* Stands in for a device holding the SLH-DSA private key. Only the
* private-against-public check is served; counting it proves the SLH-DSA arm
* of that check was taken rather than silently skipped. */
static int slhdsa_dev_key_cb(int devIdArg, wc_CryptoInfo* info, void* ctx)
{
int* checks = (int*)ctx;
(void)devIdArg;
if ((info != NULL) && (info->algo_type == WC_ALGO_TYPE_PK) &&
(info->pk.type == WC_PK_TYPE_PQC_SIG_CHECK_PRIV_KEY) &&
(info->pk.pqc_sig_check.type == WC_PQC_SIG_TYPE_SLHDSA)) {
if (checks != NULL) {
(*checks)++;
}
return 0;
}
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
}
#endif /* TEST_SLHDSA_DEV_KEY */
/* An SLH-DSA private key held in a device and referenced by id or label must
* be accepted against an SLH-DSA certificate. The parameter set cannot be
* recovered from an identifier, so it is carried from the certificate's key
* OID down to wc_SlhDsaKey_Init_id / wc_SlhDsaKey_Init_label. */
int test_slhdsa_dev_private_key(void)
{
EXPECT_DECLS;
#ifdef TEST_SLHDSA_DEV_KEY
static const unsigned char keyId[] = { 0x01, 0x02, 0x03, 0x04 };
static const char keyLabel[] = "slhdsa-device-key";
WOLFSSL_CTX* ctx = NULL;
const char* svrCert = "./certs/slhdsa/server-slhdsa-shake-128s.pem";
int checks = 0;
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_SLHDSA_DEV_DEVID,
slhdsa_dev_key_cb, &checks), 0);
/* The certificate supplies the SLH-DSA key OID the device key is matched
* against, so it has to be loaded first. */
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx, svrCert),
WOLFSSL_SUCCESS);
/* Checking the pair is what builds the device key for the certificate's
* algorithm. Before the device-key path knew about SLH-DSA no key could be
* constructed and the check failed. */
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_Id(ctx, keyId, (long)sizeof(keyId),
TEST_SLHDSA_DEV_DEVID), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_check_private_key(ctx), WOLFSSL_SUCCESS);
ExpectIntEQ(checks, 1);
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_Label(ctx, keyLabel,
TEST_SLHDSA_DEV_DEVID), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_check_private_key(ctx), WOLFSSL_SUCCESS);
/* One private-against-public check per call, each dispatched as SLH-DSA. */
ExpectIntEQ(checks, 2);
wolfSSL_CTX_free(ctx);
wc_CryptoCb_UnRegisterDevice(TEST_SLHDSA_DEV_DEVID);
#endif /* TEST_SLHDSA_DEV_KEY */
return EXPECT_RESULT();
}
#ifdef TEST_SLHDSA_DEV_KEY
#undef TEST_SLHDSA_DEV_KEY
#undef TEST_SLHDSA_DEV_DEVID
#endif
/* A post-quantum client certificate must not be usable for TLS 1.2 client
* authentication. No signature scheme below TLS 1.3 covers a PQC key, and the
* TLS 1.2 CertificateVerify record is sized for a classic signature, so the
* handshake has to fail rather than emit a message sized from the PQC
* signature length. */
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && \
defined(WOLFSSL_SLHDSA_PARAM_128S) && !defined(WOLFSSL_NO_TLS12) && \
!defined(NO_RSA) && !defined(NO_CERTS) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
#define TEST_SLHDSA_TLS12_CLIENT_AUTH
#endif
int test_slhdsa_tls12_client_cert_rejected(void)
{
EXPECT_DECLS;
#ifdef TEST_SLHDSA_TLS12_CLIENT_AUTH
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
struct test_memio_ctx test_ctx;
const char* cliCert = "./certs/slhdsa/client-slhdsa-shake-128s.pem";
const char* cliKey = "./certs/slhdsa/client-slhdsa-shake-128s-priv.pem";
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
/* Server keeps a classic certificate and asks for client authentication. */
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_2_server_method()));
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx_s,
"./certs/server-cert.pem"), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, "./certs/server-key.pem",
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
wolfSSL_CTX_set_verify(ctx_s, WOLFSSL_VERIFY_PEER, NULL);
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
/* Client holds an SLH-DSA certificate and key, which it cannot sign a
* TLS 1.2 CertificateVerify with. */
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_2_client_method()));
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx_c, cliCert),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_c, cliKey,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
wolfSSL_CTX_set_verify(ctx_c, WOLFSSL_VERIFY_NONE, NULL);
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
/* Must fail, and must fail while choosing a signature scheme rather than
* by building a CertificateVerify from an SLH-DSA key. PickHashSigAlgo
* finds no scheme the certificate can use below TLS 1.3, so
* DoCertificateRequest returns INVALID_PARAMETER. Pinning the code keeps
* the test honest: without the version gate the client would instead reach
* SendCertificateVerify and over-read its record buffer. */
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), INVALID_PARAMETER);
ExpectFalse(wolfSSL_is_init_finished(ssl_c));
wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif /* TEST_SLHDSA_TLS12_CLIENT_AUTH */
return EXPECT_RESULT();
}
#ifdef TEST_SLHDSA_TLS12_CLIENT_AUTH
#undef TEST_SLHDSA_TLS12_CLIENT_AUTH
#endif
/* A CertificateVerify that does not verify against the peer certificate's key
* must abort the handshake. The server presents its own certificate but signs
* with the client key: same parameter set, different key, so the signature is
* well formed and the failure is decided by wc_SlhDsaKey_Verify rather than by
* chain building or scheme matching. */
#if defined(WOLFSSL_HAVE_SLHDSA) && !defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && \
defined(WOLFSSL_SLHDSA_PARAM_128S) && defined(WOLFSSL_TLS13) && \
defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES)
#define TEST_SLHDSA_BAD_CV
#endif
int test_slhdsa_tls13_certverify_bad_signature(void)
{
EXPECT_DECLS;
#ifdef TEST_SLHDSA_BAD_CV
WOLFSSL_CTX* ctx_c = NULL;
WOLFSSL_CTX* ctx_s = NULL;
WOLFSSL* ssl_c = NULL;
WOLFSSL* ssl_s = NULL;
struct test_memio_ctx test_ctx;
const char* svrCert = "./certs/slhdsa/server-slhdsa-shake-128s.pem";
const char* wrongKey =
"./certs/slhdsa/client-slhdsa-shake-128s-priv.pem";
const char* caCert = "./certs/slhdsa/root-slhdsa-shake-128s.pem";
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectNotNull(ctx_s = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
ExpectIntEQ(wolfSSL_CTX_use_certificate_chain_file(ctx_s, svrCert),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, wrongKey,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
wolfSSL_SetIORecv(ctx_s, test_memio_read_cb);
wolfSSL_SetIOSend(ctx_s, test_memio_write_cb);
ExpectNotNull(ctx_c = wolfSSL_CTX_new(wolfTLSv1_3_client_method()));
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, caCert, NULL),
WOLFSSL_SUCCESS);
wolfSSL_SetIORecv(ctx_c, test_memio_read_cb);
wolfSSL_SetIOSend(ctx_c, test_memio_write_cb);
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 20, NULL), 0);
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), SIG_VERIFY_E);
ExpectFalse(wolfSSL_is_init_finished(ssl_c));
wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif /* TEST_SLHDSA_BAD_CV */
return EXPECT_RESULT();
}
#ifdef TEST_SLHDSA_BAD_CV
#undef TEST_SLHDSA_BAD_CV
#endif
/* Map every compiled-in SLH-DSA signature scheme from its wire code point to
* the public key OID sum, covering both DecodeSigAlg's SLH-DSA arm and the
* wolfSSL_get_sigalg_info cases. */
int test_slhdsa_get_sigalg_info(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_HAVE_SLHDSA) && defined(OPENSSL_EXTRA)
static const struct {
byte minor;
int keyOidSum;
} schemes[] = {
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S)
{ SLHDSA_SHA2_128S_SA_MINOR, SLH_DSA_SHA2_128Sk },
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_128F)
{ SLHDSA_SHA2_128F_SA_MINOR, SLH_DSA_SHA2_128Fk },
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_192S)
{ SLHDSA_SHA2_192S_SA_MINOR, SLH_DSA_SHA2_192Sk },
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_192F)
{ SLHDSA_SHA2_192F_SA_MINOR, SLH_DSA_SHA2_192Fk },
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_256S)
{ SLHDSA_SHA2_256S_SA_MINOR, SLH_DSA_SHA2_256Sk },
#endif
#if defined(WOLFSSL_SLHDSA_SHA2) && defined(WOLFSSL_SLHDSA_PARAM_SHA2_256F)
{ SLHDSA_SHA2_256F_SA_MINOR, SLH_DSA_SHA2_256Fk },
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_128S
{ SLHDSA_SHAKE_128S_SA_MINOR, SLH_DSA_SHAKE_128Sk },
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_128F
{ SLHDSA_SHAKE_128F_SA_MINOR, SLH_DSA_SHAKE_128Fk },
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192S
{ SLHDSA_SHAKE_192S_SA_MINOR, SLH_DSA_SHAKE_192Sk },
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_192F
{ SLHDSA_SHAKE_192F_SA_MINOR, SLH_DSA_SHAKE_192Fk },
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256S
{ SLHDSA_SHAKE_256S_SA_MINOR, SLH_DSA_SHAKE_256Sk },
#endif
#ifdef WOLFSSL_SLHDSA_PARAM_256F
{ SLHDSA_SHAKE_256F_SA_MINOR, SLH_DSA_SHAKE_256Fk },
#endif
{ 0, 0 } /* terminator, keeps the array non-empty */
};
size_t i;
int hashOid = 0;
int keyOid = 0;
for (i = 0; i < (sizeof(schemes) / sizeof(schemes[0])) - 1; i++) {
hashOid = 0;
keyOid = 0;
ExpectIntEQ(wolfSSL_get_sigalg_info(SLHDSA_SA_MAJOR,
schemes[i].minor, &hashOid, &keyOid), 0);
ExpectIntEQ(keyOid, schemes[i].keyOidSum);
}
/* An SLH-DSA major with a minor that is not a scheme stays unmapped. */
ExpectIntEQ(wolfSSL_get_sigalg_info(SLHDSA_SA_MAJOR, 0x7F, &hashOid,
&keyOid), BAD_FUNC_ARG);
/* NULL outputs are rejected. */
ExpectIntEQ(wolfSSL_get_sigalg_info(SLHDSA_SA_MAJOR,
SLHDSA_SHAKE_128S_SA_MINOR, NULL, &keyOid), BAD_FUNC_ARG);
ExpectIntEQ(wolfSSL_get_sigalg_info(SLHDSA_SA_MAJOR,
SLHDSA_SHAKE_128S_SA_MINOR, &hashOid, NULL), BAD_FUNC_ARG);
#endif /* WOLFSSL_HAVE_SLHDSA && OPENSSL_EXTRA */
return EXPECT_RESULT();
}
+15 -1
View File
@@ -42,6 +42,13 @@ int test_wc_slhdsa_param_disabled(void);
int test_wc_slhdsa_decoder_disabled_oid(void);
int test_wc_SlhdsaDecisionCoverage(void);
int test_wc_SlhdsaFeatureCoverage(void);
int test_slhdsa_tls13_certverify_want_write(void);
int test_slhdsa_get_sigalg_info(void);
int test_slhdsa_tls12_client_cert_rejected(void);
int test_slhdsa_tls13_certverify_multi_stall(void);
int test_mldsa_tls13_certverify_maxfrag_stream(void);
int test_slhdsa_dev_private_key(void);
int test_slhdsa_tls13_certverify_bad_signature(void);
#define TEST_SLHDSA_DECLS \
TEST_DECL_GROUP("slhdsa", test_wc_slhdsa), \
@@ -61,6 +68,13 @@ int test_wc_SlhdsaFeatureCoverage(void);
TEST_DECL_GROUP("slhdsa", test_wc_slhdsa_param_disabled), \
TEST_DECL_GROUP("slhdsa", test_wc_slhdsa_decoder_disabled_oid), \
TEST_DECL_GROUP("slhdsa", test_wc_SlhdsaDecisionCoverage), \
TEST_DECL_GROUP("slhdsa", test_wc_SlhdsaFeatureCoverage)
TEST_DECL_GROUP("slhdsa", test_wc_SlhdsaFeatureCoverage), \
TEST_DECL_GROUP("slhdsa", test_slhdsa_tls13_certverify_want_write), \
TEST_DECL_GROUP("slhdsa", test_slhdsa_get_sigalg_info), \
TEST_DECL_GROUP("slhdsa", test_slhdsa_tls12_client_cert_rejected), \
TEST_DECL_GROUP("slhdsa", test_slhdsa_tls13_certverify_multi_stall), \
TEST_DECL_GROUP("slhdsa", test_mldsa_tls13_certverify_maxfrag_stream), \
TEST_DECL_GROUP("slhdsa", test_slhdsa_dev_private_key), \
TEST_DECL_GROUP("slhdsa", test_slhdsa_tls13_certverify_bad_signature)
#endif /* WOLFCRYPT_TEST_SLHDSA_H */
+8 -1
View File
@@ -420,7 +420,14 @@ int test_wolfSSL_get_signature_type_nid(void)
anonymous_sa_algo, rsa_sa_algo, dsa_sa_algo, ecc_dsa_sa_algo,
ecc_brainpool_sa_algo, rsa_pss_sa_algo, rsa_pss_pss_algo,
falcon_level1_sa_algo, falcon_level5_sa_algo, mldsa_44_sa_algo,
mldsa_65_sa_algo, mldsa_87_sa_algo, sm2_sa_algo
mldsa_65_sa_algo, mldsa_87_sa_algo,
slhdsa_sha2_128s_sa_algo, slhdsa_sha2_128f_sa_algo,
slhdsa_sha2_192s_sa_algo, slhdsa_sha2_192f_sa_algo,
slhdsa_sha2_256s_sa_algo, slhdsa_sha2_256f_sa_algo,
slhdsa_shake_128s_sa_algo, slhdsa_shake_128f_sa_algo,
slhdsa_shake_192s_sa_algo, slhdsa_shake_192f_sa_algo,
slhdsa_shake_256s_sa_algo, slhdsa_shake_256f_sa_algo,
sm2_sa_algo
};
static const byte failAlgos[] = { invalid_sa_algo, any_sa_algo };
size_t i;
+6
View File
@@ -44,6 +44,12 @@ EXTRA_DIST += tests/unit.h \
tests/test-tls13-slhdsa-shake.conf \
tests/test-tls13-slhdsa-sha2.conf \
tests/test-tls13-slhdsa-fail.conf \
tests/test-tls13-slhdsa-entity.conf \
tests/test-tls13-slhdsa-entity-128s.conf \
tests/test-tls13-slhdsa-entity-sha2.conf \
tests/test-tls13-slhdsa-entity-sha2-128s.conf \
tests/test-dtls13-slhdsa-entity.conf \
tests/test-dtls13-slhdsa-entity-128s.conf \
tests/test-dtls13-pq-standalone.conf \
tests/test-dtls13-pq-standalone-frag.conf \
tests/test-dtls13-pq-hybrid-frag.conf \
+107 -2
View File
@@ -1326,7 +1326,8 @@ int SuiteTest(int argc, char** argv)
goto exit;
}
#endif
#if defined(WOLFSSL_HAVE_SLHDSA) && defined(WOLFSSL_HAVE_MLDSA) && \
#if defined(WOLFSSL_HAVE_SLHDSA) && \
!defined(WOLFSSL_MLDSA_VERIFY_ONLY) && defined(WOLFSSL_HAVE_MLDSA) && \
defined(WOLFSSL_SLHDSA_PARAM_128S) && \
defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_ML_DSA_44)
/* SLH-DSA-SHAKE-128s root + ML-DSA-44 entity cert tests (TLS 1.3) */
@@ -1356,7 +1357,8 @@ int SuiteTest(int argc, char** argv)
XSTRLCPY(argv0[2], "", sizeof(argv0[2]));
args.argc = 2;
#endif
#if defined(WOLFSSL_HAVE_SLHDSA) && defined(WOLFSSL_SLHDSA_SHA2) && \
#if defined(WOLFSSL_HAVE_SLHDSA) && \
!defined(WOLFSSL_MLDSA_VERIFY_ONLY) && defined(WOLFSSL_SLHDSA_SHA2) && \
defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S) && defined(WOLFSSL_HAVE_MLDSA) && \
defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_ML_DSA_44)
/* SLH-DSA-SHA2-128s root + ML-DSA-44 entity cert tests (TLS 1.3) */
@@ -1370,6 +1372,109 @@ int SuiteTest(int argc, char** argv)
goto exit;
}
#endif
#if defined(WOLFSSL_HAVE_SLHDSA) && \
!defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_SLHDSA_PARAM_128F) && \
defined(WOLFSSL_SLHDSA_PARAM_128S) && defined(WOLFSSL_TLS13)
/* SLH-DSA-SHAKE-128f entity (leaf) certificate used for the handshake
* signature in CertificateVerify. The leaf's ~17KB signature also exercises
* fragmented CertificateVerify send + reassembly. The leaf is signed by the
* SLH-DSA-SHAKE-128s root, so 128s must be enabled too for chain verify. */
XSTRLCPY(argv0[1], "tests/test-tls13-slhdsa-entity.conf",
sizeof(argv0[1]));
printf("starting TLSv13 SLH-DSA entity-cert CertificateVerify tests\n");
test_harness(&args);
if (args.return_code != 0) {
printf("error from script %d\n", args.return_code);
args.return_code = EXIT_FAILURE;
goto exit;
}
#endif
#if defined(WOLFSSL_HAVE_SLHDSA) && \
!defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_SLHDSA_PARAM_128S) && \
defined(WOLFSSL_TLS13)
/* SLH-DSA-SHAKE-128s entity (leaf) certificate used for the handshake
* signature in CertificateVerify, signed by the SLH-DSA-SHAKE-128s root.
* The leaf's ~7.8KB signature fits in a single record, exercising the
* single-record CertificateVerify path. */
XSTRLCPY(argv0[1], "tests/test-tls13-slhdsa-entity-128s.conf",
sizeof(argv0[1]));
printf("starting TLSv13 SLH-DSA entity-cert (128s single-record) tests\n");
test_harness(&args);
if (args.return_code != 0) {
printf("error from script %d\n", args.return_code);
args.return_code = EXIT_FAILURE;
goto exit;
}
#endif
#if defined(WOLFSSL_HAVE_SLHDSA) && \
!defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_SLHDSA_SHA2) && \
defined(WOLFSSL_SLHDSA_PARAM_SHA2_128F) && \
defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S) && defined(WOLFSSL_TLS13)
/* SLH-DSA-SHA2-128f entity (leaf) certificate used for the handshake
* signature in CertificateVerify. The leaf's ~17KB signature also exercises
* fragmented CertificateVerify send + reassembly for the SHA2 family. The
* leaf is signed by the SLH-DSA-SHA2-128s root, so 128s must be enabled too
* for chain verify. */
XSTRLCPY(argv0[1], "tests/test-tls13-slhdsa-entity-sha2.conf",
sizeof(argv0[1]));
printf("starting TLSv13 SLH-DSA entity-cert (SHA2) CertificateVerify "
"tests\n");
test_harness(&args);
if (args.return_code != 0) {
printf("error from script %d\n", args.return_code);
args.return_code = EXIT_FAILURE;
goto exit;
}
#endif
#if defined(WOLFSSL_HAVE_SLHDSA) && \
!defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_SLHDSA_SHA2) && \
defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S) && defined(WOLFSSL_TLS13)
/* SLH-DSA-SHA2-128s entity (leaf) certificate used for the handshake
* signature in CertificateVerify, signed by the SLH-DSA-SHA2-128s root.
* The leaf's ~7.8KB signature fits in a single record, exercising the
* single-record CertificateVerify path for the SHA2 family. */
XSTRLCPY(argv0[1], "tests/test-tls13-slhdsa-entity-sha2-128s.conf",
sizeof(argv0[1]));
printf("starting TLSv13 SLH-DSA entity-cert (SHA2 128s single-record) "
"tests\n");
test_harness(&args);
if (args.return_code != 0) {
printf("error from script %d\n", args.return_code);
args.return_code = EXIT_FAILURE;
goto exit;
}
#endif
#if defined(WOLFSSL_HAVE_SLHDSA) && \
!defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_SLHDSA_PARAM_128F) && \
defined(WOLFSSL_SLHDSA_PARAM_128S) && defined(WOLFSSL_DTLS13)
/* DTLS 1.3 SLH-DSA-SHAKE-128f entity cert: exercises the DTLS
* CertificateVerify send path (Dtls13HandshakeSend) with a fragmented
* ~17KB SLH-DSA signature. */
XSTRLCPY(argv0[1], "tests/test-dtls13-slhdsa-entity.conf",
sizeof(argv0[1]));
printf("starting DTLSv13 SLH-DSA entity-cert CertificateVerify tests\n");
test_harness(&args);
if (args.return_code != 0) {
printf("error from script %d\n", args.return_code);
args.return_code = EXIT_FAILURE;
goto exit;
}
#endif
#if defined(WOLFSSL_HAVE_SLHDSA) && \
!defined(WOLFSSL_SLHDSA_VERIFY_ONLY) && defined(WOLFSSL_SLHDSA_PARAM_128S) && \
defined(WOLFSSL_DTLS13)
/* DTLS 1.3 SLH-DSA-SHAKE-128s entity cert: single-record DTLS
* CertificateVerify path. */
XSTRLCPY(argv0[1], "tests/test-dtls13-slhdsa-entity-128s.conf",
sizeof(argv0[1]));
printf("starting DTLSv13 SLH-DSA entity-cert (128s single-record) tests\n");
test_harness(&args);
if (args.return_code != 0) {
printf("error from script %d\n", args.return_code);
args.return_code = EXIT_FAILURE;
goto exit;
}
#endif
#if defined(HAVE_ECC) && defined(WOLFSSL_SHA512) && \
(defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES))
/* add P-521 certificate cipher suite tests */
+42
View File
@@ -0,0 +1,42 @@
# DTLS 1.3 variant of the SLH-DSA entity-certificate CertificateVerify test.
# Exercises the DTLS 1.3 CertificateVerify send path (Dtls13HandshakeSend) with
# an SLH-DSA leaf whose ~7.8KB SLH-DSA-SHAKE-128s signature fits in a single
# record. Companion to tests/test-tls13-slhdsa-entity-128s.conf.
# Server-auth scenario.
# server DTLSv1.3 TLS13-AES128-GCM-SHA256
-u
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/server-slhdsa-shake-128s.pem
-k ./certs/slhdsa/server-slhdsa-shake-128s-priv.pem
-d
# client DTLSv1.3 TLS13-AES128-GCM-SHA256
-u
-v 4
-l TLS13-AES128-GCM-SHA256
-A ./certs/slhdsa/root-slhdsa-shake-128s.pem
-C
# Mutual-auth scenario (SLH-DSA CertificateVerify in both directions).
# server DTLSv1.3 TLS13-AES128-GCM-SHA256
-u
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/server-slhdsa-shake-128s.pem
-k ./certs/slhdsa/server-slhdsa-shake-128s-priv.pem
-A ./certs/slhdsa/root-slhdsa-shake-128s.pem
-V
# Remove -V when CRL for SLH-DSA certificates available.
# client DTLSv1.3 TLS13-AES128-GCM-SHA256
-u
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/client-slhdsa-shake-128s.pem
-k ./certs/slhdsa/client-slhdsa-shake-128s-priv.pem
-A ./certs/slhdsa/root-slhdsa-shake-128s.pem
-C
+42
View File
@@ -0,0 +1,42 @@
# DTLS 1.3 variant of the SLH-DSA entity-certificate CertificateVerify test.
# Exercises the DTLS 1.3 CertificateVerify send path (Dtls13HandshakeSend) with
# an SLH-DSA leaf, which fragments the ~17KB SLH-DSA-SHAKE-128f signature across
# DTLS records. Companion to tests/test-tls13-slhdsa-entity.conf.
# Server-auth scenario.
# server DTLSv1.3 TLS13-AES128-GCM-SHA256
-u
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/server-slhdsa-shake-128f.pem
-k ./certs/slhdsa/server-slhdsa-shake-128f-priv.pem
-d
# client DTLSv1.3 TLS13-AES128-GCM-SHA256
-u
-v 4
-l TLS13-AES128-GCM-SHA256
-A ./certs/slhdsa/root-slhdsa-shake-128s.pem
-C
# Mutual-auth scenario (SLH-DSA CertificateVerify in both directions).
# server DTLSv1.3 TLS13-AES128-GCM-SHA256
-u
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/server-slhdsa-shake-128f.pem
-k ./certs/slhdsa/server-slhdsa-shake-128f-priv.pem
-A ./certs/slhdsa/root-slhdsa-shake-128s.pem
-V
# Remove -V when CRL for SLH-DSA certificates available.
# client DTLSv1.3 TLS13-AES128-GCM-SHA256
-u
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/client-slhdsa-shake-128f.pem
-k ./certs/slhdsa/client-slhdsa-shake-128f-priv.pem
-A ./certs/slhdsa/root-slhdsa-shake-128s.pem
-C
+41
View File
@@ -0,0 +1,41 @@
# SLH-DSA entity (leaf) certificates used for the TLS 1.3 handshake signature
# in the CertificateVerify message (draft-reddy-tls-slhdsa). TLS 1.3 only.
#
# SLH-DSA-SHAKE-128s leaves signed by the shared SLH-DSA-SHAKE-128s root. The
# leaf key's ~7.8KB signature fits in a single TLS record, so this exercises the
# single-record CertificateVerify send path with an SLH-DSA key (the companion
# 128f config exercises the fragmented path).
# Server-auth scenario.
# server TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/server-slhdsa-shake-128s.pem
-k ./certs/slhdsa/server-slhdsa-shake-128s-priv.pem
-d
# client TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-A ./certs/slhdsa/root-slhdsa-shake-128s.pem
-C
# Mutual-auth scenario (SLH-DSA CertificateVerify in both directions).
# server TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/server-slhdsa-shake-128s.pem
-k ./certs/slhdsa/server-slhdsa-shake-128s-priv.pem
-A ./certs/slhdsa/root-slhdsa-shake-128s.pem
-V
# Remove -V when CRL for SLH-DSA certificates available.
# client TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/client-slhdsa-shake-128s.pem
-k ./certs/slhdsa/client-slhdsa-shake-128s-priv.pem
-A ./certs/slhdsa/root-slhdsa-shake-128s.pem
-C
@@ -0,0 +1,41 @@
# SLH-DSA entity (leaf) certificates used for the TLS 1.3 handshake signature
# in the CertificateVerify message (draft-reddy-tls-slhdsa). TLS 1.3 only.
#
# SLH-DSA-SHA2-128s leaves signed by the shared SLH-DSA-SHA2-128s root. The leaf
# key's ~7.8KB signature fits in a single TLS record, so this exercises the
# single-record CertificateVerify send path for the SHA2 parameter family (the
# companion sha2 128f config exercises the fragmented path).
# Server-auth scenario.
# server TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/server-slhdsa-sha2-128s.pem
-k ./certs/slhdsa/server-slhdsa-sha2-128s-priv.pem
-d
# client TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-A ./certs/slhdsa/root-slhdsa-sha2-128s.pem
-C
# Mutual-auth scenario (SLH-DSA CertificateVerify in both directions).
# server TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/server-slhdsa-sha2-128s.pem
-k ./certs/slhdsa/server-slhdsa-sha2-128s-priv.pem
-A ./certs/slhdsa/root-slhdsa-sha2-128s.pem
-V
# Remove -V when CRL for SLH-DSA certificates available.
# client TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/client-slhdsa-sha2-128s.pem
-k ./certs/slhdsa/client-slhdsa-sha2-128s-priv.pem
-A ./certs/slhdsa/root-slhdsa-sha2-128s.pem
-C
+41
View File
@@ -0,0 +1,41 @@
# SLH-DSA entity (leaf) certificates used for the TLS 1.3 handshake signature
# in the CertificateVerify message (draft-reddy-tls-slhdsa). TLS 1.3 only.
#
# SLH-DSA-SHA2-128f leaves signed by the shared SLH-DSA-SHA2-128s root. The leaf
# key's ~17KB signature makes the CertificateVerify handshake message exceed a
# single TLS record, so this also exercises fragmented CertificateVerify send +
# reassembly for the SHA2 parameter family.
# Server-auth scenario.
# server TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/server-slhdsa-sha2-128f.pem
-k ./certs/slhdsa/server-slhdsa-sha2-128f-priv.pem
-d
# client TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-A ./certs/slhdsa/root-slhdsa-sha2-128s.pem
-C
# Mutual-auth scenario (SLH-DSA CertificateVerify in both directions).
# server TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/server-slhdsa-sha2-128f.pem
-k ./certs/slhdsa/server-slhdsa-sha2-128f-priv.pem
-A ./certs/slhdsa/root-slhdsa-sha2-128s.pem
-V
# Remove -V when CRL for SLH-DSA certificates available.
# client TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/client-slhdsa-sha2-128f.pem
-k ./certs/slhdsa/client-slhdsa-sha2-128f-priv.pem
-A ./certs/slhdsa/root-slhdsa-sha2-128s.pem
-C
+41
View File
@@ -0,0 +1,41 @@
# SLH-DSA entity (leaf) certificates used for the TLS 1.3 handshake signature
# in the CertificateVerify message (draft-reddy-tls-slhdsa). TLS 1.3 only.
#
# SLH-DSA-SHAKE-128f leaves signed by the shared SLH-DSA-SHAKE-128s root. The
# leaf key's ~17KB signature makes the CertificateVerify handshake message
# exceed a single TLS record, so this also exercises fragmented
# CertificateVerify send + reassembly.
# Server-auth scenario.
# server TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/server-slhdsa-shake-128f.pem
-k ./certs/slhdsa/server-slhdsa-shake-128f-priv.pem
-d
# client TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-A ./certs/slhdsa/root-slhdsa-shake-128s.pem
-C
# Mutual-auth scenario (SLH-DSA CertificateVerify in both directions).
# server TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/server-slhdsa-shake-128f.pem
-k ./certs/slhdsa/server-slhdsa-shake-128f-priv.pem
-A ./certs/slhdsa/root-slhdsa-shake-128s.pem
-V
# Remove -V when CRL for SLH-DSA certificates available.
# client TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/client-slhdsa-shake-128f.pem
-k ./certs/slhdsa/client-slhdsa-shake-128f-priv.pem
-A ./certs/slhdsa/root-slhdsa-shake-128s.pem
-C
+100 -54
View File
@@ -24003,6 +24003,68 @@ static Signer* FindSignerByAkidOrName(void* cm, Signer* extraCAList,
}
#endif /* !IGNORE_NAME_CONSTRAINTS */
#ifdef WOLFSSL_DUAL_ALG_CERTS
/* Build the PreTBS used to verify an alternative signature, allocating the
* buffer that holds it.
*
* The PreTBS omits the signature, the alternative signature and the
* alternative-signature extension, so the certificate size minus both
* signature values is a close estimate and is tried first: this runs on
* constrained targets where the allocation size matters. It is only an
* estimate, because wc_GeneratePreTBS re-encodes the TBSCertificate rather
* than copying it, and a canonical re-encode may add algorithm parameters or
* re-frame fields. If the estimate is short, retry once at a size the
* re-encode cannot exceed, since the PreTBS is a re-encode of a strict subset
* of the certificate.
*
* On success sets *derOut and returns the PreTBS length, which is > 0.
* Returns a negative error code on failure; *derOut is NULL in that case. */
static int GeneratePreTBSBuffer(DecodedCert* cert, byte** derOut)
{
int ret;
byte* der;
word32 derSz;
*derOut = NULL;
/* Check each length on its own so the guard cannot wrap and the
* subtraction cannot underflow on a malformed certificate. */
if (cert->sigLength >= cert->maxIdx ||
(word32)cert->altSigValLen >= cert->maxIdx - cert->sigLength) {
return ASN_PARSE_E;
}
derSz = cert->maxIdx - cert->sigLength - (word32)cert->altSigValLen;
der = (byte*)XMALLOC(derSz, cert->heap, DYNAMIC_TYPE_DCERT);
if (der == NULL) {
return MEMORY_E;
}
ret = wc_GeneratePreTBS(cert, der, (int)derSz);
if (ret <= 0) {
XFREE(der, cert->heap, DYNAMIC_TYPE_DCERT);
derSz = cert->maxIdx + MAX_ALGO_SZ + MAX_SEQ_SZ + MAX_LENGTH_SZ;
der = (byte*)XMALLOC(derSz, cert->heap, DYNAMIC_TYPE_DCERT);
if (der == NULL) {
return MEMORY_E;
}
ret = wc_GeneratePreTBS(cert, der, (int)derSz);
}
if (ret <= 0) {
/* wc_GeneratePreTBS reports an encoder failure as WOLFSSL_FAILURE,
* which is 0. Return an error so a PreTBS that was not produced can
* never skip the signature check and read as a verified signature. */
XFREE(der, cert->heap, DYNAMIC_TYPE_DCERT);
return (ret == 0) ? ASN_PARSE_E : ret;
}
*derOut = der;
return ret;
}
#endif /* WOLFSSL_DUAL_ALG_CERTS */
int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
Signer *extraCAList)
{
@@ -24625,35 +24687,27 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
#ifdef WOLFSSL_DUAL_ALG_CERTS
if ((ret == 0) && cert->extAltSigAlgSet &&
cert->extAltSigValSet) {
#ifndef WOLFSSL_SMALL_STACK
byte der[WC_MAX_CERT_VERIFY_SZ];
#else
byte *der = (byte*)XMALLOC(WC_MAX_CERT_VERIFY_SZ, cert->heap,
DYNAMIC_TYPE_DCERT);
if (der == NULL) {
ret = MEMORY_E;
} else
#endif /* ! WOLFSSL_SMALL_STACK */
{
ret = wc_GeneratePreTBS(cert, der, WC_MAX_CERT_VERIFY_SZ);
word32 derSz;
byte* der;
if (ret > 0) {
ret = ConfirmSignature(&cert->sigCtx, der, ret,
cert->ca->sapkiDer, cert->ca->sapkiLen,
cert->ca->sapkiOID, cert->altSigValDer,
cert->altSigValLen, cert->altSigAlgOID,
NULL, 0, NULL);
}
WC_FREE_VAR_EX(der, cert->heap, DYNAMIC_TYPE_DCERT);
ret = GeneratePreTBSBuffer(cert, &der);
if (ret > 0) {
derSz = (word32)ret;
ret = ConfirmSignature(&cert->sigCtx, der, derSz,
cert->ca->sapkiDer, cert->ca->sapkiLen,
cert->ca->sapkiOID, cert->altSigValDer,
cert->altSigValLen, cert->altSigAlgOID,
NULL, 0, NULL);
XFREE(der, cert->heap, DYNAMIC_TYPE_DCERT);
}
if (ret != 0) {
WOLFSSL_MSG("Confirm alternative signature failed");
WOLFSSL_ERROR_VERBOSE(ret);
return ret;
}
else {
WOLFSSL_MSG("Alt signature has been verified!");
}
if (ret != 0) {
WOLFSSL_MSG("Confirm alternative signature failed");
WOLFSSL_ERROR_VERBOSE(ret);
return ret;
}
else {
WOLFSSL_MSG("Alt signature has been verified!");
}
}
#endif /* WOLFSSL_DUAL_ALG_CERTS */
@@ -24684,35 +24738,27 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
#ifdef WOLFSSL_DUAL_ALG_CERTS
if ((ret == 0) && cert->extAltSigAlgSet &&
cert->extAltSigValSet) {
#ifndef WOLFSSL_SMALL_STACK
byte der[WC_MAX_CERT_VERIFY_SZ];
#else
byte *der = (byte*)XMALLOC(WC_MAX_CERT_VERIFY_SZ, cert->heap,
DYNAMIC_TYPE_DCERT);
if (der == NULL) {
ret = MEMORY_E;
} else
#endif /* ! WOLFSSL_SMALL_STACK */
{
ret = wc_GeneratePreTBS(cert, der, WC_MAX_CERT_VERIFY_SZ);
word32 derSz;
byte* der;
if (ret > 0) {
ret = ConfirmSignature(&cert->sigCtx, der, ret,
cert->sapkiDer, cert->sapkiLen,
cert->sapkiOID, cert->altSigValDer,
cert->altSigValLen, cert->altSigAlgOID,
NULL, 0, NULL);
}
WC_FREE_VAR_EX(der, cert->heap, DYNAMIC_TYPE_DCERT);
ret = GeneratePreTBSBuffer(cert, &der);
if (ret > 0) {
derSz = (word32)ret;
ret = ConfirmSignature(&cert->sigCtx, der, derSz,
cert->sapkiDer, cert->sapkiLen,
cert->sapkiOID, cert->altSigValDer,
cert->altSigValLen, cert->altSigAlgOID,
NULL, 0, NULL);
XFREE(der, cert->heap, DYNAMIC_TYPE_DCERT);
}
if (ret != 0) {
WOLFSSL_MSG("Confirm alternative signature failed");
WOLFSSL_ERROR_VERBOSE(ret);
return ret;
}
else {
WOLFSSL_MSG("Alt signature has been verified!");
}
if (ret != 0) {
WOLFSSL_MSG("Confirm alternative signature failed");
WOLFSSL_ERROR_VERBOSE(ret);
return ret;
}
else {
WOLFSSL_MSG("Alt signature has been verified!");
}
}
#endif /* WOLFSSL_DUAL_ALG_CERTS */
@@ -1709,7 +1709,11 @@ int tsip_Tls13SendCertVerify(WOLFSSL* ssl)
}
if (ret == 0) {
recordSz = WC_MAX_CERT_VERIFY_SZ + MAX_MSG_EXTRA * 2;
/* TSIP only signs classic RSA/ECC CertificateVerify messages in
* hardware, never PQC. Size the record to the classic signature tier
* rather than WC_MAX_CERT_VERIFY_SZ, which balloons to ~50KB when
* SLH-DSA is enabled elsewhere in the build. */
recordSz = MAX_ENCODED_CLASSIC_SIG_SZ + MAX_MSG_EXTRA * 2;
/* check for available size */
ret = CheckAvailableSize(ssl, recordSz);
recordSz = 0;
+72 -16
View File
@@ -71,20 +71,28 @@ static cpuid_flags_t cpuid_flags = WC_CPUID_INITIALIZER;
#define SLHDSA_WM1 (SLHDSA_W - 1)
#ifndef WOLFSSL_SLHDSA_PARAM_NO_256
/* The buffer-sizing maxima below (SLHDSA_MAX_N/A/H_M/INDICES_SZ/MD) size
* stack/heap buffers that are later written using the true n/a/h_m taken from
* key->params at runtime, so they must stay large enough for every compiled-in
* parameter set across BOTH hash families. They key off the combined
* WC_SLHDSA_ALL_NO_* guards (derived in wc_slhdsa.h), NOT the per-family SHAKE
* 'NO' guards, otherwise a SHA2-only build (SHAKE disabled) would collapse the
* maxima below the SHA2 parameters' true needs and overflow the FORS/XMSS
* 'nodes' buffers. */
#ifndef WC_SLHDSA_ALL_NO_256
/* Maximum size of hash output. */
#define SLHDSA_MAX_N 32
#ifndef WOLFSSL_SLHDSA_PARAM_NO_FAST
#ifndef WC_SLHDSA_ALL_NO_FAST
/* Maximum number of indices for FORS signatures. */
#define SLHDSA_MAX_INDICES_SZ 35
#else
/* Maximum number of indices for FORS signatures. */
#define SLHDSA_MAX_INDICES_SZ 22
#endif
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_192)
#elif !defined(WC_SLHDSA_ALL_NO_192)
/* Maximum size of hash output. */
#define SLHDSA_MAX_N 24
#ifndef WOLFSSL_SLHDSA_PARAM_NO_FAST
#ifndef WC_SLHDSA_ALL_NO_FAST
/* Maximum number of indices for FORS signatures. */
#define SLHDSA_MAX_INDICES_SZ 33
#else
@@ -94,7 +102,7 @@ static cpuid_flags_t cpuid_flags = WC_CPUID_INITIALIZER;
#else
/* Maximum size of hash output. */
#define SLHDSA_MAX_N 16
#ifndef WOLFSSL_SLHDSA_PARAM_NO_FAST
#ifndef WC_SLHDSA_ALL_NO_FAST
/* Maximum number of indices for FORS signatures. */
#define SLHDSA_MAX_INDICES_SZ 33
#else
@@ -103,11 +111,11 @@ static cpuid_flags_t cpuid_flags = WC_CPUID_INITIALIZER;
#endif
#endif
#ifndef WOLFSSL_SLHDSA_PARAM_NO_SMALL
#if !defined(WOLFSSL_SLHDSA_PARAM_NO_256)
#ifndef WC_SLHDSA_ALL_NO_SMALL
#if !defined(WC_SLHDSA_ALL_NO_256)
/* Maximum number of trees for FORS. */
#define SLHDSA_MAX_A 14
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_192)
#elif !defined(WC_SLHDSA_ALL_NO_192)
/* Maximum number of trees for FORS. */
#define SLHDSA_MAX_A 14
#else
@@ -115,10 +123,10 @@ static cpuid_flags_t cpuid_flags = WC_CPUID_INITIALIZER;
#define SLHDSA_MAX_A 12
#endif
#else
#if !defined(WOLFSSL_SLHDSA_PARAM_NO_256)
#if !defined(WC_SLHDSA_ALL_NO_256)
/* Maximum number of trees for FORS. */
#define SLHDSA_MAX_A 9
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_192)
#elif !defined(WC_SLHDSA_ALL_NO_192)
/* Maximum number of trees for FORS. */
#define SLHDSA_MAX_A 8
#else
@@ -127,7 +135,7 @@ static cpuid_flags_t cpuid_flags = WC_CPUID_INITIALIZER;
#endif
#endif
#ifndef WOLFSSL_SLHDSA_PARAM_NO_SMALL
#ifndef WC_SLHDSA_ALL_NO_SMALL
/* Maximum height of Merkle tree. */
#define SLHDSA_MAX_H_M 9
#else
@@ -148,19 +156,19 @@ wc_static_assert(SLHDSA_MAX_MSG_SZ <= 255);
* declarations and the ForceZero() sizes so they cannot drift. */
#define SLHDSA_SHAKE_X4_STATE_W (25 * 4)
#ifndef WOLFSSL_SLHDSA_PARAM_NO_256F
#ifndef WC_SLHDSA_ALL_NO_256F
/* Maximum number of bytes to produce from digest of message. */
#define SLHDSA_MAX_MD 49
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_256S)
#elif !defined(WC_SLHDSA_ALL_NO_256S)
/* Maximum number of bytes to produce from digest of message. */
#define SLHDSA_MAX_MD 47
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_192F)
#elif !defined(WC_SLHDSA_ALL_NO_192F)
/* Maximum number of bytes to produce from digest of message. */
#define SLHDSA_MAX_MD 42
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_192S)
#elif !defined(WC_SLHDSA_ALL_NO_192S)
/* Maximum number of bytes to produce from digest of message. */
#define SLHDSA_MAX_MD 39
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_128F)
#elif !defined(WC_SLHDSA_ALL_NO_128F)
/* Maximum number of bytes to produce from digest of message. */
#define SLHDSA_MAX_MD 34
#else
@@ -9193,6 +9201,31 @@ int wc_SlhDsaKey_PrivateKeyDecode(const byte* input, word32* inOutIdx,
return ASN_PARSE_E;
}
#ifdef WOLFSSL_SLHDSA_SHA2
/* The key may have been initialised with a placeholder parameter set from a
* different hash family (callers pass WC_SLHDSA_DEFAULT_PARAM, a SHAKE set
* whenever any SHAKE parameter is built in). If the detected set uses a
* different hash family, re-initialise the hash objects for it so the
* placeholder's SHAKE objects are released via wc_SlhDsaKey_Free() instead
* of being orphaned when key->params switches to a SHA2 set below. */
if ((key->params != NULL) &&
(SLHDSA_IS_SHA2(key->params->param) != SLHDSA_IS_SHA2(params->param))) {
void* keyHeap = key->heap;
#ifdef WOLF_CRYPTO_CB
int keyDevId = key->devId;
#else
int keyDevId = INVALID_DEVID;
#endif
wc_SlhDsaKey_Free(key);
ret = wc_SlhDsaKey_Init(key, (enum SlhDsaParam)paramId, keyHeap,
keyDevId);
if (ret != 0) {
*inOutIdx = savedIdx;
return ret;
}
}
#endif /* WOLFSSL_SLHDSA_SHA2 */
/* RFC 9909: privateKey is a single OCTET STRING containing the raw key
* (4*n bytes). Unlike Ed25519/Ed448, there is no nested inner OCTET
* STRING wrapping. */
@@ -9408,6 +9441,29 @@ int wc_SlhDsaKey_PublicKeyDecode(const byte* input, word32* inOutIdx,
return ASN_PARSE_E;
}
#ifdef WOLFSSL_SLHDSA_SHA2
/* The hash objects live in a union selected by family. Importing across
* families writes the new family's state over the old one's, orphaning it,
* so re-initialise for the detected set first. Mirrors the guard in
* wc_SlhDsaKey_PrivateKeyDecode. */
if ((key->params != NULL) &&
(SLHDSA_IS_SHA2(key->params->param) != SLHDSA_IS_SHA2(params->param))) {
void* keyHeap = key->heap;
#ifdef WOLF_CRYPTO_CB
int keyDevId = key->devId;
#else
int keyDevId = INVALID_DEVID;
#endif
wc_SlhDsaKey_Free(key);
ret = wc_SlhDsaKey_Init(key, (enum SlhDsaParam)paramId, keyHeap,
keyDevId);
if (ret != 0) {
*inOutIdx = savedIdx;
return ret;
}
}
#endif /* WOLFSSL_SLHDSA_SHA2 */
oldFlags = key->flags;
oldParams = key->params;
key->params = params;
+139 -10
View File
@@ -129,6 +129,9 @@
#ifdef WOLFSSL_HAVE_MLDSA
#include <wolfssl/wolfcrypt/wc_mldsa.h>
#endif
#ifdef WOLFSSL_HAVE_SLHDSA
#include <wolfssl/wolfcrypt/wc_slhdsa.h>
#endif
#ifdef HAVE_HKDF
#include <wolfssl/wolfcrypt/kdf.h>
#endif
@@ -1801,6 +1804,23 @@ enum Misc {
MLDSA_87_SA_MAJOR = 0x09,
MLDSA_87_SA_MINOR = 0x06,
/* These values for SLH-DSA correspond to the code points assigned in
* draft-reddy-tls-slhdsa (0x0911-0x091C) and match what oqs-provider uses.
* The major byte (0x09) is shared with ML-DSA. */
SLHDSA_SA_MAJOR = 0x09,
SLHDSA_SHA2_128S_SA_MINOR = 0x11,
SLHDSA_SHA2_128F_SA_MINOR = 0x12,
SLHDSA_SHA2_192S_SA_MINOR = 0x13,
SLHDSA_SHA2_192F_SA_MINOR = 0x14,
SLHDSA_SHA2_256S_SA_MINOR = 0x15,
SLHDSA_SHA2_256F_SA_MINOR = 0x16,
SLHDSA_SHAKE_128S_SA_MINOR = 0x17,
SLHDSA_SHAKE_128F_SA_MINOR = 0x18,
SLHDSA_SHAKE_192S_SA_MINOR = 0x19,
SLHDSA_SHAKE_192F_SA_MINOR = 0x1A,
SLHDSA_SHAKE_256S_SA_MINOR = 0x1B,
SLHDSA_SHAKE_256F_SA_MINOR = 0x1C,
MIN_RSA_SHA512_PSS_BITS = 512 * 2 + 8 * 8, /* Min key size */
MIN_RSA_SHA384_PSS_BITS = 384 * 2 + 8 * 8, /* Min key size */
@@ -1901,7 +1921,8 @@ WOLFSSL_LOCAL int NamedGroupIsPqcHybrid(int group);
/* number of items in the signature algo list */
#ifndef WOLFSSL_MAX_SIGALGO
#if (defined(WOLFSSL_LEANPSK) || defined(WOLFSSL_LEANTLS)) && \
!defined(HAVE_FALCON) && !defined(WOLFSSL_HAVE_MLDSA)
!defined(HAVE_FALCON) && !defined(WOLFSSL_HAVE_MLDSA) && \
!defined(WOLFSSL_HAVE_SLHDSA)
/* Lean builds keep the list small to minimize the memory footprint, unless
* they are post-quantum builds: those want to inter-op with OQS's OpenSSL
* that sends a lot more sigalgs, so they fall through to the larger default.
@@ -1980,9 +2001,21 @@ WOLFSSL_LOCAL int NamedGroupIsPqcHybrid(int group);
#define SESSIDX_IDX_MASK 0x0F
#endif
/* Size of the static per-certificate slot in a cached session's chain. This is
* embedded by value MAX_CHAIN_DEPTH times in every WOLFSSL_SESSION, so it is
* deliberately not sized from a post-quantum signature: a certificate too
* large for a slot is simply not recorded in the chain. Use
* MAX_CERT_WIRE_SZ for anything bounding a certificate on the wire. */
#ifndef MAX_X509_SIZE
#if defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA)
#define MAX_X509_SIZE (8*1024) /* max static x509 buffer size; ML-DSA is big */
/* 9 KB holds the largest ML-DSA certificate (ML-DSA-87: 4627 byte signature
* plus 2592 byte public key, ~7.6 KB in practice) and an ML-DSA-44 dual
* algorithm certificate, which carries a second key and signature. Not
* derived from the enabled parameter set: the slot holds any certificate in
* a peer's chain, so tying it to the local ML-DSA level would make a
* level-restricted build silently drop certificates a full build kept. */
#if defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \
defined(WOLFSSL_HAVE_SLHDSA)
#define MAX_X509_SIZE (9*1024) /* max static x509 buffer size; ML-DSA is big */
#elif defined(WOLFSSL_HAPROXY)
#define MAX_X509_SIZE 3072 /* max static x509 buffer size */
#else
@@ -1990,6 +2023,24 @@ WOLFSSL_LOCAL int NamedGroupIsPqcHybrid(int group);
#endif
#endif
/* Largest single certificate that may appear in a handshake message. A
* post-quantum certificate's DER size is dominated by the issuer signature
* embedded in it, whose length depends on the parameter sets compiled in, plus
* headroom for the subject public key (largest is ML-DSA-87 at 2592 bytes) and
* the rest of the TBSCertificate. A leaf may be signed by a root of a larger
* parameter set, so the signature maximum is taken family-wide. */
#ifndef MAX_CERT_WIRE_SZ
#if defined(WOLFSSL_HAVE_SLHDSA) && \
((WC_SLHDSA_MAX_SIG_LEN + 4096) > MAX_X509_SIZE)
#define MAX_CERT_WIRE_SZ (WC_SLHDSA_MAX_SIG_LEN + 4096)
#elif defined(WOLFSSL_HAVE_MLDSA) && \
((MLDSA_MAX_SIG_SIZE + 4096) > MAX_X509_SIZE)
#define MAX_CERT_WIRE_SZ (MLDSA_MAX_SIG_SIZE + 4096)
#else
#define MAX_CERT_WIRE_SZ MAX_X509_SIZE
#endif
#endif
/* max cert chain peer depth */
#ifndef MAX_CHAIN_DEPTH
#define MAX_CHAIN_DEPTH 9
@@ -2011,12 +2062,34 @@ WOLFSSL_LOCAL int NamedGroupIsPqcHybrid(int group);
#define MAX_CERT_EXTENSIONS 1
#endif
/* Chain depth assumed when sizing the certificate message. Deliberately not
* MAX_CHAIN_DEPTH: that bounds how deep a chain may be verified, while this
* sizes a buffer an unauthenticated peer can make us allocate. Only reduced
* when a post-quantum certificate has inflated the per-certificate size, where
* the full verification depth would reserve hundreds of kilobytes and chains
* that deep are not realistic. Classic builds keep the historical depth, since
* the resulting buffer is small either way. Raise it for a deployment that
* presents deeper chains of post-quantum certificates. */
#ifndef MAX_CERT_MSG_DEPTH
/* Trim only once a single certificate is large enough that the full
* verification depth would reserve an unreasonable amount for an
* unauthenticated peer. The threshold sits above any classic or ML-DSA
* certificate, so those builds keep the historical depth, and the test is
* on the size itself rather than on which macro produced it, so raising
* MAX_X509_SIZE cannot disengage the trim. */
#if (MAX_CERT_WIRE_SZ > (16*1024)) && (MAX_CHAIN_DEPTH > 5)
#define MAX_CERT_MSG_DEPTH 5
#else
#define MAX_CERT_MSG_DEPTH MAX_CHAIN_DEPTH
#endif
#endif
/* max size of a certificate message payload */
/* assumes MAX_CHAIN_DEPTH number of certificates at 2kb per certificate */
/* assumes MAX_CERT_MSG_DEPTH certificates of MAX_CERT_WIRE_SZ each */
#ifndef MAX_CERTIFICATE_SZ
#define MAX_CERTIFICATE_SZ \
(CERT_HEADER_SZ + \
(MAX_X509_SIZE + CERT_HEADER_SZ) * MAX_CHAIN_DEPTH)
(MAX_CERT_WIRE_SZ + CERT_HEADER_SZ) * MAX_CERT_MSG_DEPTH)
#endif
/* max size of a handshake message, currently set to the certificate */
@@ -2203,9 +2276,11 @@ WOLFSSL_LOCAL int CheckVersion(WOLFSSL *ssl, ProtocolVersion pv);
WOLFSSL_LOCAL int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo,
word32 hashSigAlgoSz, int matchSuites);
#if defined(WOLF_PRIVATE_KEY_ID) && !defined(NO_CHECK_PRIVATE_KEY)
/* slhParam is the enum SlhDsaParam for DYNAMIC_TYPE_SLHDSA, whose parameter
* set cannot be derived from a device-side identifier. Pass -1 otherwise. */
WOLFSSL_LOCAL int CreateDevPrivateKey(void** pkey, byte* data, word32 length,
int hsType, int label, int id,
void* heap, int devId);
void* heap, int devId, int slhParam);
#endif
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
WOLFSSL_LOCAL int wolfssl_priv_der_blind(WC_RNG* rng, DerBuffer* key,
@@ -4114,6 +4189,7 @@ struct WOLFSSL_CTX {
byte haveECDSAsig:1; /* server cert signed w/ ECDSA */
byte haveFalconSig:1; /* server cert signed w/ Falcon */
byte haveMlDsaSig:1; /* server cert signed w/ ML-DSA */
byte haveSlhDsaSig:1; /* server cert signed w/ SLH-DSA */
byte haveStaticECC:1; /* static server ECC private key */
byte partialWrite:1; /* only one msg per write call */
byte autoRetry:1; /* retry read/write on a WANT_{READ|WRITE} */
@@ -4268,7 +4344,8 @@ struct WOLFSSL_CTX {
word16 eccTempKeySz; /* in octets 20 - 66 */
#endif
#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \
defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA)
defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \
defined(WOLFSSL_HAVE_SLHDSA)
word32 pkCurveOID; /* curve Ecc_Sum */
#endif
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
@@ -4595,9 +4672,10 @@ enum KeyExchangeAlgorithm {
#define SIG_FALCON 0x08
#define SIG_MLDSA 0x10
#define SIG_ANON 0x20
#define SIG_SLHDSA 0x40
/* SIG_ANON is omitted by default */
#define SIG_ALL (SIG_ECDSA | SIG_RSA | SIG_SM2 | SIG_FALCON | \
SIG_MLDSA)
SIG_MLDSA | SIG_SLHDSA)
/* Supported Authentication Schemes */
enum SignatureAlgorithm {
@@ -4617,6 +4695,18 @@ enum SignatureAlgorithm {
sm2_sa_algo = 17,
any_sa_algo = 18,
ecc_brainpool_sa_algo = 19,
slhdsa_sha2_128s_sa_algo = 20,
slhdsa_sha2_128f_sa_algo = 21,
slhdsa_sha2_192s_sa_algo = 22,
slhdsa_sha2_192f_sa_algo = 23,
slhdsa_sha2_256s_sa_algo = 24,
slhdsa_sha2_256f_sa_algo = 25,
slhdsa_shake_128s_sa_algo = 26,
slhdsa_shake_128f_sa_algo = 27,
slhdsa_shake_192s_sa_algo = 28,
slhdsa_shake_192f_sa_algo = 29,
slhdsa_shake_256s_sa_algo = 30,
slhdsa_shake_256f_sa_algo = 31,
invalid_sa_algo = 255
};
@@ -5109,6 +5199,25 @@ typedef struct ThreadCrypt {
#endif
/* Streamed TLS 1.3 CertificateVerify send. When the CertificateVerify body
* (the signature) does not fit in a single record - a post-quantum signature
* such as SLH-DSA or ML-DSA, or any signature under a small
* max_fragment_length - it is generated once into a connection-level buffer and
* emitted one record at a time, so the output buffer never has to hold the
* whole signature. The assembled body must be held at the connection level
* (not on the stack) because these signatures are randomized: a non-blocking
* WANT_WRITE can return control mid-send, and the records already sent are
* bound into the transcript, so the resumed send must continue emitting the
* exact same signature - it cannot be regenerated. This is algorithm-neutral;
* it applies to any signature scheme whose CertificateVerify can exceed a
* record. It is not used with WOLFSSL_ASYNC_CRYPT, whose record-AEAD pends are
* handled by the existing in-place fragmented path. */
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_ASYNC_CRYPT) && \
(defined(WOLFSSL_HAVE_SLHDSA) || defined(WOLFSSL_HAVE_MLDSA) || \
defined(HAVE_FALCON))
#define WOLFSSL_TLS13_STREAM_CERT_VERIFY
#endif
/* buffers for struct WOLFSSL */
typedef struct Buffers {
bufferStatic inputBuffer;
@@ -5191,6 +5300,13 @@ typedef struct Buffers {
buffer peerRsaKey; /* we own for Rsa Verify Callbacks */
#endif /* NO_RSA */
#endif /* HAVE_PK_CALLBACKS */
#ifdef WOLFSSL_TLS13_STREAM_CERT_VERIFY
/* Assembled TLS 1.3 CertificateVerify body (sig-alg | length | signature)
* held across records while it is streamed, so a non-blocking WANT_WRITE
* can resume the send without recomputing the signature. NULL when idle;
* freed by wolfSSL_ResourceFree. See WOLFSSL_TLS13_STREAM_CERT_VERIFY. */
buffer certVerifyMsg;
#endif
} Buffers;
/* sub-states for send/do key share (key exchange) */
@@ -5287,6 +5403,7 @@ struct Options {
word16 haveStaticECC:1; /* static server ECC private key */
word16 haveFalconSig:1; /* server Falcon signed cert */
word16 haveMlDsaSig:1; /* server ML-DSA signed cert */
word16 haveSlhDsaSig:1; /* server SLH-DSA signed cert */
word16 havePeerCert:1; /* do we have peer's cert */
word16 havePeerVerify:1; /* and peer's cert verify */
word16 usingPSK_cipher:1; /* are using psk as cipher */
@@ -5695,7 +5812,8 @@ struct WOLFSSL_X509 {
int pubKeyOID;
DNS_entry* altNamesNext; /* hint for retrieval */
#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \
defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA)
defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \
defined(WOLFSSL_HAVE_SLHDSA)
word32 pkCurveOID;
#endif
#ifndef NO_CERTS
@@ -6425,7 +6543,8 @@ struct WOLFSSL {
#endif
#if defined(HAVE_ECC) || defined(HAVE_ED25519) || \
defined(HAVE_CURVE448) || defined(HAVE_ED448) || \
defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA)
defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \
defined(WOLFSSL_HAVE_SLHDSA)
word32 pkCurveOID; /* curve Ecc_Sum */
#endif
#ifdef HAVE_ED25519
@@ -6452,6 +6571,10 @@ struct WOLFSSL {
wc_MlDsaKey* peerMlDsaKey;
byte peerMlDsaKeyPresent;
#endif
#ifdef WOLFSSL_HAVE_SLHDSA
SlhDsaKey* peerSlhDsaKey;
byte peerSlhDsaKeyPresent;
#endif
#ifdef HAVE_LIBZ
z_stream c_stream; /* compression stream */
z_stream d_stream; /* decompression stream */
@@ -7352,6 +7475,12 @@ WOLFSSL_LOCAL int FindSuite(const Suites* suites, byte first, byte second);
WOLFSSL_LOCAL void DecodeSigAlg(const byte* input, byte* hashAlgo,
byte* hsType);
#ifdef WOLFSSL_HAVE_SLHDSA
WOLFSSL_LOCAL byte SlhDsaSigMinorToType(byte minor);
WOLFSSL_LOCAL int SlhDsaTypeToParam(byte hsType);
WOLFSSL_LOCAL int IsSlhDsaSigAlgo(byte hsType);
WOLFSSL_LOCAL byte SlhDsaParamToType(int param);
#endif
WOLFSSL_LOCAL enum wc_HashType HashAlgoToType(int hashAlgo);
#ifndef NO_CERTS
+10 -1
View File
@@ -936,7 +936,16 @@ extern const WOLFSSL_ObjectInfo wolfssl_object_info[];
#endif
#endif
#if defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA)
/* Maximum size of a CertificateVerify signature buffer. Retained as public API
* for backward compatibility; wolfSSL no longer uses it internally. The TLS and
* certificate-generation paths now size their buffers from the actual signature
* length instead of this worst case, which balloons to ~50KB when SLH-DSA is
* enabled. Downstream code that sizes a stack buffer with this should account
* for that when SLH-DSA is compiled in. */
#if defined(WOLFSSL_HAVE_SLHDSA)
/* SLH-DSA signatures are large (up to ~50KB for the 'f' parameter sets). */
#define WC_MAX_CERT_VERIFY_SZ (WC_SLHDSA_MAX_SIG_LEN + 1024)
#elif defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA)
#define WC_MAX_CERT_VERIFY_SZ 6000 /* For ML-DSA */
#elif defined(WOLFSSL_CERT_EXT)
#define WC_MAX_CERT_VERIFY_SZ 2048 /* For larger extensions */
+198 -45
View File
@@ -89,6 +89,41 @@
#endif /* !WOLFSSL_SLHDSA_NO_SHAKE */
/* Push a group-level exclusion down onto each set in that group. The parameter
* table and the OID lookups gate on the per-set 'NO' macros while the TLS
* mappings gate on the positive macros; without this, a build excluding only a
* group would decode a certificate the mappings cannot name. */
#if defined(WOLFSSL_SLHDSA_PARAM_NO_128) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SMALL)
#undef WOLFSSL_SLHDSA_PARAM_NO_128S
#define WOLFSSL_SLHDSA_PARAM_NO_128S
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_128) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_FAST)
#undef WOLFSSL_SLHDSA_PARAM_NO_128F
#define WOLFSSL_SLHDSA_PARAM_NO_128F
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_192) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SMALL)
#undef WOLFSSL_SLHDSA_PARAM_NO_192S
#define WOLFSSL_SLHDSA_PARAM_NO_192S
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_192) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_FAST)
#undef WOLFSSL_SLHDSA_PARAM_NO_192F
#define WOLFSSL_SLHDSA_PARAM_NO_192F
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_256) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SMALL)
#undef WOLFSSL_SLHDSA_PARAM_NO_256S
#define WOLFSSL_SLHDSA_PARAM_NO_256S
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_256) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_FAST)
#undef WOLFSSL_SLHDSA_PARAM_NO_256F
#define WOLFSSL_SLHDSA_PARAM_NO_256F
#endif
/* When 'NO' defines are on then define no parameter set. */
#if defined(WOLFSSL_SLHDSA_PARAM_NO_128S) && \
defined(WOLFSSL_SLHDSA_PARAM_NO_128F)
@@ -225,6 +260,39 @@
#undef WOLFSSL_SLHDSA_PARAM_NO_SHA2_FAST
#endif
/* Push a group-level SHA2 exclusion down onto each set in that group, for the
* same reason as the SHAKE family above. */
#if defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_128) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_SMALL)
#undef WOLFSSL_SLHDSA_PARAM_NO_SHA2_128S
#define WOLFSSL_SLHDSA_PARAM_NO_SHA2_128S
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_128) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_FAST)
#undef WOLFSSL_SLHDSA_PARAM_NO_SHA2_128F
#define WOLFSSL_SLHDSA_PARAM_NO_SHA2_128F
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_192) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_SMALL)
#undef WOLFSSL_SLHDSA_PARAM_NO_SHA2_192S
#define WOLFSSL_SLHDSA_PARAM_NO_SHA2_192S
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_192) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_FAST)
#undef WOLFSSL_SLHDSA_PARAM_NO_SHA2_192F
#define WOLFSSL_SLHDSA_PARAM_NO_SHA2_192F
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_256) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_SMALL)
#undef WOLFSSL_SLHDSA_PARAM_NO_SHA2_256S
#define WOLFSSL_SLHDSA_PARAM_NO_SHA2_256S
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_256) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_FAST)
#undef WOLFSSL_SLHDSA_PARAM_NO_SHA2_256F
#define WOLFSSL_SLHDSA_PARAM_NO_SHA2_256F
#endif
/* Derive aggregate 'NO' defines for SHA2. */
#if defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_128S) && \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_128F)
@@ -468,18 +536,85 @@
#endif /* WOLFSSL_SLHDSA_SHA2 */
/* ======== Combined family-absence guards ======== */
/* A size/speed class is only truly absent when neither its SHAKE variant nor
* its SHA2 variant is compiled in. The per-family 'NO' guards above describe
* one family each; the SHA2 'NO' macros exist only when WOLFSSL_SLHDSA_SHA2 is
* defined, otherwise WOLFSSL_SLHDSA_NO_SHA2 stands for "all SHA2 absent".
* These combined guards let the maximum-size defines below (and the internal
* buffer maxima in wc_slhdsa.c) stay large enough for every compiled-in
* parameter set across both hash families. Without them a SHA2-only build
* (SHAKE disabled) would size buffers for the 128-bit level only and overflow
* on the 192/256-bit SHA2 parameter sets. */
#if defined(WOLFSSL_SLHDSA_PARAM_NO_256) && \
(defined(WOLFSSL_SLHDSA_NO_SHA2) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_256))
#define WC_SLHDSA_ALL_NO_256
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_192) && \
(defined(WOLFSSL_SLHDSA_NO_SHA2) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_192))
#define WC_SLHDSA_ALL_NO_192
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_SMALL) && \
(defined(WOLFSSL_SLHDSA_NO_SHA2) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_SMALL))
#define WC_SLHDSA_ALL_NO_SMALL
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_FAST) && \
(defined(WOLFSSL_SLHDSA_NO_SHA2) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_FAST))
#define WC_SLHDSA_ALL_NO_FAST
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_128) && \
(defined(WOLFSSL_SLHDSA_NO_SHA2) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_128))
#define WC_SLHDSA_ALL_NO_128
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_256F) && \
(defined(WOLFSSL_SLHDSA_NO_SHA2) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_256F))
#define WC_SLHDSA_ALL_NO_256F
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_256S) && \
(defined(WOLFSSL_SLHDSA_NO_SHA2) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_256S))
#define WC_SLHDSA_ALL_NO_256S
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_192F) && \
(defined(WOLFSSL_SLHDSA_NO_SHA2) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_192F))
#define WC_SLHDSA_ALL_NO_192F
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_192S) && \
(defined(WOLFSSL_SLHDSA_NO_SHA2) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_192S))
#define WC_SLHDSA_ALL_NO_192S
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_128F) && \
(defined(WOLFSSL_SLHDSA_NO_SHA2) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_128F))
#define WC_SLHDSA_ALL_NO_128F
#endif
#if defined(WOLFSSL_SLHDSA_PARAM_NO_128S) && \
(defined(WOLFSSL_SLHDSA_NO_SHA2) || \
defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_128S))
#define WC_SLHDSA_ALL_NO_128S
#endif
/* ======== Maximum size defines ======== */
/* Determine maximum private and public key lengths based on maximum 256-bit
* output length. SHA2 variants have identical sizes to SHAKE counterparts. */
#ifndef WOLFSSL_SLHDSA_PARAM_NO_256
#ifndef WC_SLHDSA_ALL_NO_256
/* Maximum private key length. */
#define WC_SLHDSA_MAX_PRIV_LEN WC_SLHDSA_SHAKE256F_PRIV_LEN
/* Maximum public key length. */
#define WC_SLHDSA_MAX_PUB_LEN WC_SLHDSA_SHAKE256F_PUB_LEN
/* Maximum seed length. */
#define WC_SLHDSA_MAX_SEED WC_SLHDSA_SHAKE256_SEED_LEN
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_192)
#elif !defined(WC_SLHDSA_ALL_NO_192)
/* Maximum private key length. */
#define WC_SLHDSA_MAX_PRIV_LEN WC_SLHDSA_SHAKE192F_PRIV_LEN
/* Maximum public key length. */
@@ -496,55 +631,31 @@
#endif
/* Determine maximum signature length depending on the parameters compiled in.
*/
#if !defined(WOLFSSL_SLHDSA_PARAM_NO_256) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_FAST)
/* Maximum signature length. */
*
* The signature size depends only on the parameter set, not the hash family:
* each SHA2 variant is byte-for-byte the same size as its SHAKE counterpart.
* So this keys off the per-variant combined WC_SLHDSA_ALL_NO_*F/S guards (present
* when that exact variant is absent from BOTH families) and references the
* SHAKE size constant. Using the raw per-family WOLFSSL_SLHDSA_PARAM_NO[_SHA2]_*
* guards here mis-fires on the SHA2 arms in a SHAKE-limited, SHA2-absent build,
* because the WOLFSSL_SLHDSA_PARAM_NO_SHA2_* macros are undefined when the SHA2
* family is off (only WOLFSSL_SLHDSA_NO_SHA2 is), so the arm is wrongly taken
* and expands to an undefined WC_SLHDSA_SHA2_*_SIG_LEN. Selecting on the exact
* variant (rather than class-plus-speed) also keeps the bound tight in
* mixed-family builds. The arms are ordered by descending signature size
* (256f > 192f > 256s > 128f > 192s > 128s). */
#if !defined(WC_SLHDSA_ALL_NO_256F)
#define WC_SLHDSA_MAX_SIG_LEN WC_SLHDSA_SHAKE256F_SIG_LEN
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_256) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_FAST)
/* Maximum signature length. */
#define WC_SLHDSA_MAX_SIG_LEN WC_SLHDSA_SHA2_256F_SIG_LEN
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_192) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_FAST)
/* Maximum signature length. */
#elif !defined(WC_SLHDSA_ALL_NO_192F)
#define WC_SLHDSA_MAX_SIG_LEN WC_SLHDSA_SHAKE192F_SIG_LEN
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_192) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_FAST)
/* Maximum signature length. */
#define WC_SLHDSA_MAX_SIG_LEN WC_SLHDSA_SHA2_192F_SIG_LEN
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_256) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_SMALL)
/* Maximum signature length. */
#elif !defined(WC_SLHDSA_ALL_NO_256S)
#define WC_SLHDSA_MAX_SIG_LEN WC_SLHDSA_SHAKE256S_SIG_LEN
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_256) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_SMALL)
/* Maximum signature length. */
#define WC_SLHDSA_MAX_SIG_LEN WC_SLHDSA_SHA2_256S_SIG_LEN
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_128) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_FAST)
/* Maximum signature length. */
#elif !defined(WC_SLHDSA_ALL_NO_128F)
#define WC_SLHDSA_MAX_SIG_LEN WC_SLHDSA_SHAKE128F_SIG_LEN
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_128) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_FAST)
/* Maximum signature length. */
#define WC_SLHDSA_MAX_SIG_LEN WC_SLHDSA_SHA2_128F_SIG_LEN
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_192) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_SMALL)
/* Maximum signature length. */
#elif !defined(WC_SLHDSA_ALL_NO_192S)
#define WC_SLHDSA_MAX_SIG_LEN WC_SLHDSA_SHAKE192S_SIG_LEN
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_192) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_SMALL)
/* Maximum signature length. */
#define WC_SLHDSA_MAX_SIG_LEN WC_SLHDSA_SHA2_192S_SIG_LEN
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_128) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_SMALL)
/* Maximum signature length. */
#elif !defined(WC_SLHDSA_ALL_NO_128S)
#define WC_SLHDSA_MAX_SIG_LEN WC_SLHDSA_SHAKE128S_SIG_LEN
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_128) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_SMALL)
/* Maximum signature length. */
#define WC_SLHDSA_MAX_SIG_LEN WC_SLHDSA_SHA2_128S_SIG_LEN
#else
#error "No parameters defined"
#endif
@@ -567,6 +678,48 @@ enum SlhDsaParam {
#endif
};
/* A parameter set that is guaranteed to be compiled in. Use as a placeholder
* for wc_SlhDsaKey_Init when the real parameter set is only known later (e.g.
* determined from a decoded key OID or a negotiated TLS signature scheme).
* The SHAKE family is disabled when only the SHA2 family is enabled, so the
* placeholder cannot be a fixed SHAKE value. */
/* Selection must use the same WOLFSSL_SLHDSA_PARAM_NO_* macros that gate the
* SlhDsaParams[] rows, or the placeholder can name a set with no table row and
* every wc_SlhDsaKey_Init using it fails with NOT_COMPILED_IN. */
#if !defined(WOLFSSL_SLHDSA_PARAM_NO_128S)
#define WC_SLHDSA_DEFAULT_PARAM SLHDSA_SHAKE128S
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_128F)
#define WC_SLHDSA_DEFAULT_PARAM SLHDSA_SHAKE128F
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_192S)
#define WC_SLHDSA_DEFAULT_PARAM SLHDSA_SHAKE192S
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_192F)
#define WC_SLHDSA_DEFAULT_PARAM SLHDSA_SHAKE192F
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_256S)
#define WC_SLHDSA_DEFAULT_PARAM SLHDSA_SHAKE256S
#elif !defined(WOLFSSL_SLHDSA_PARAM_NO_256F)
#define WC_SLHDSA_DEFAULT_PARAM SLHDSA_SHAKE256F
#elif defined(WOLFSSL_SLHDSA_SHA2) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_128S)
#define WC_SLHDSA_DEFAULT_PARAM SLHDSA_SHA2_128S
#elif defined(WOLFSSL_SLHDSA_SHA2) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_128F)
#define WC_SLHDSA_DEFAULT_PARAM SLHDSA_SHA2_128F
#elif defined(WOLFSSL_SLHDSA_SHA2) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_192S)
#define WC_SLHDSA_DEFAULT_PARAM SLHDSA_SHA2_192S
#elif defined(WOLFSSL_SLHDSA_SHA2) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_192F)
#define WC_SLHDSA_DEFAULT_PARAM SLHDSA_SHA2_192F
#elif defined(WOLFSSL_SLHDSA_SHA2) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_256S)
#define WC_SLHDSA_DEFAULT_PARAM SLHDSA_SHA2_256S
#elif defined(WOLFSSL_SLHDSA_SHA2) && \
!defined(WOLFSSL_SLHDSA_PARAM_NO_SHA2_256F)
#define WC_SLHDSA_DEFAULT_PARAM SLHDSA_SHA2_256F
#else
#error "WOLFSSL_HAVE_SLHDSA requires at least one parameter set"
#endif
/* Helper macro to detect SHA2 parameter sets. */
#ifdef WOLFSSL_SLHDSA_SHA2
#define SLHDSA_IS_SHA2(p) ((p) >= SLHDSA_SHA2_128S)