Skip to content

Add SLH-DSA TLS 1.3 entity authentication (CertificateVerify)#6

Closed
Frauschi wants to merge 2 commits intoclaude/sphincs-to-slhdsa-transition-VhszIfrom
claude/slhdsa-tls-handshake
Closed

Add SLH-DSA TLS 1.3 entity authentication (CertificateVerify)#6
Frauschi wants to merge 2 commits intoclaude/sphincs-to-slhdsa-transition-VhszIfrom
claude/slhdsa-tls-handshake

Conversation

@Frauschi
Copy link
Copy Markdown
Owner

Summary

Adds SLH-DSA as a TLS 1.3 signature algorithm for entity authentication per draft-reddy-tls-slhdsa-02. This builds on the certificate-layer SLH-DSA support in PR #5.

Depends on: PR #5 (must be merged first)

Changes

TLS 1.3 SignatureScheme negotiation

  • 6 SHAKE code points (0x0917-0x091C) with PQC_SA_MAJOR = 0x09
  • EncodeSigAlg / DecodeTls13SigAlg / DecodeSigAlg for SLH-DSA
  • PickHashSigAlgo with SLH-DSA pkCurveOID matching
  • MatchSigAlgo for all 6 SHAKE variants
  • SIG_SLHDSA flag in SIG_ALL

CertificateVerify signing and verification

  • SendTls13CertificateVerify: SLH-DSA signing with empty context per spec
  • DoTls13CertificateVerify: SLH-DSA verification
  • Record fragmentation for signatures > 16384 bytes (MAX_RECORD_SIZE):
    • Saves tail plaintext before in-place encryption to avoid corruption
    • Loops through N fragments for arbitrarily large signatures
    • Tested with SHAKE-256f (49856-byte signature, 4 records)

Peer key management

  • peerSlhDsaKey / peerSlhDsaKeyPresent in WOLFSSL struct
  • AllocKey / FreeKey / ReuseKey for DYNAMIC_TYPE_SLHDSA
  • ProcessPeerCerts extracts SLH-DSA peer keys with param auto-detect

Key loading

  • ProcessBufferTryDecodeSlhDsa for SLH-DSA private key loading
  • ProcessBufferCertSetHave / ProcessBufferKeySet for haveSlhDsaSig

Infrastructure

  • MAX_X509_SIZE = 56*1024 for SLH-DSA (SHAKE-256f cert ~50KB)
  • WC_MAX_CERT_VERIFY_SZ = 49920 for SLH-DSA-SHAKE-256f
  • MIN_SLHDSAKEY_SZ = 32 (smallest SLH-DSA public key)
  • OpenSSL 3.5-generated SLH-DSA-SHAKE-128s cert chain

Interop test results

Tested with OQS provider (OpenSSL 3.4 + liboqs) in both directions:

Variant Sig Size Records wolfssl-wolfssl wolfssl-OQS OQS-wolfssl
SHAKE-128s 7,856 1 PASS PASS PASS
SHAKE-128f 17,088 2 PASS PASS PASS
SHAKE-256f 49,856 4 PASS PASS PASS

Test plan

  • wolfcrypt tests pass (SLH-DSA test passed!)
  • wolfssl-to-wolfssl TLS 1.3 handshake with SLH-DSA certs (all 3 variants)
  • OQS interop in both directions (all 3 variants)
  • CertificateVerify record fragmentation works for signatures up to 50KB

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV

claude added 2 commits April 14, 2026 08:25
Replace the liboqs-based pre-standardization SPHINCS+ implementation
with the native FIPS 205 SLH-DSA implementation across the
certificate/ASN.1/X.509 layers. All liboqs SPHINCS+ code is removed.

This PR enables SLH-DSA for certificate chain authentication (CA
certificates signed with SLH-DSA, certificate verification). TLS 1.3
entity authentication via CertificateVerify with SLH-DSA will be
added in a follow-up PR.

Follows RFC 9909 (X.509 Algorithm Identifiers for SLH-DSA).

Changes:
- New DER codec for SLH-DSA (PrivateKeyDecode, PublicKeyDecode,
  KeyToDer, PrivateKeyToDer, PublicKeyToDer) with RFC 9909 compliant
  encoding (bare OCTET STRING, no nested wrapper) and OID auto-
  detection across all 6 SHAKE parameter sets
- 12 standardized NIST OIDs (6 SHA2 + 6 SHAKE) per RFC 9909
- Complete ASN.1 layer replacement (~500 lines in asn.c)
- X.509 public key handling in x509.c
- OID collision mechanism cleaned up (NIST OIDs don't collide)
- DER round-trip test for all compiled-in parameter sets
- SLH-DSA test cert chain generated with OpenSSL 3.5
- All build system/IDE project files updated
- SPHINCS+ source files, headers, and test data removed

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
Add SLH-DSA as a TLS 1.3 signature algorithm for entity authentication
in the handshake, per draft-reddy-tls-slhdsa-02.

This builds on the certificate-layer SLH-DSA support (PR #5) and adds:

TLS 1.3 SignatureScheme negotiation:
- 6 SHAKE code points (0x0917-0x091C) with PQC_SA_MAJOR = 0x09
- EncodeSigAlg / DecodeTls13SigAlg / DecodeSigAlg
- PickHashSigAlgo with SLH-DSA pkCurveOID matching
- MatchSigAlgo for all 6 SHAKE variants
- SIG_SLHDSA flag in SIG_ALL

CertificateVerify signing and verification:
- SendTls13CertificateVerify with SLH-DSA signing (empty context)
- DoTls13CertificateVerify with SLH-DSA verification
- Record fragmentation for signatures > MAX_RECORD_SIZE (16384 bytes)
  - Saves tail plaintext before in-place encryption
  - Loops through N fragments for arbitrarily large signatures
  - Tested with SHAKE-256f (49856-byte sig, 4 records)

Peer key management:
- peerSlhDsaKey / peerSlhDsaKeyPresent in WOLFSSL struct
- AllocKey / FreeKey / ReuseKey for DYNAMIC_TYPE_SLHDSA
- ProcessPeerCerts extracts SLH-DSA peer keys with param auto-detect

Key loading:
- ProcessBufferTryDecodeSlhDsa for SLH-DSA private key loading
- ProcessBufferCertSetHave / ProcessBufferKeySet for haveSlhDsaSig

Infrastructure:
- haveSlhDsaSig, minSlhDsaKeySz in Options and CTX
- MAX_X509_SIZE = 56*1024 for SLH-DSA (SHAKE-256f cert ~50KB)
- WC_MAX_CERT_VERIFY_SZ = 49920 for SLH-DSA-SHAKE-256f
- OpenSSL 3.5-generated SLH-DSA-SHAKE-128s cert chain for testing

Interop tested with OQS provider (OpenSSL 3.4 + liboqs):
- SHAKE-128s (7856-byte sig, 1 record) - bidirectional
- SHAKE-128f (17088-byte sig, 2 records) - bidirectional
- SHAKE-256f (49856-byte sig, 4 records) - bidirectional

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
@Frauschi Frauschi force-pushed the claude/sphincs-to-slhdsa-transition-VhszI branch 3 times, most recently from 5f875ba to 1d23ad3 Compare April 16, 2026 12:43
@Frauschi Frauschi deleted the branch claude/sphincs-to-slhdsa-transition-VhszI April 23, 2026 17:34
@Frauschi Frauschi closed this Apr 23, 2026
Frauschi pushed a commit that referenced this pull request May 3, 2026
- Fix small-variant signing regression (review finding bonus): reverting
  word16 sum -> unsigned int in wc_lmots_q_expand broke the small-variant
  checksum loop, which relies on arithmetic wrapping at 2^16. Restore
  word16 sum with explicit (word16) casts at every assignment so the
  -Wconversion warning is silenced without changing semantics. Caught by
  the new runtime test job (also added).
- Fix 16-bit shift UB (review #1): the matrix doesn't cover 16-bit, but
  on word32 = unsigned long targets `1U << params->height` is undefined
  for height >= 16. Restore (word32)1U on every shift whose exponent can
  reach height (LMS_Q_AT_LEVEL macro and four open-coded sites).
- Add msgSz <= 0 check to wc_LmsKey_Verify (review #2): wc_LmsKey_Sign
  already rejects non-positive msgSz; the public verify entry point did
  not. Without it, the (word32)msgSz cast forwarded a huge value to
  wc_hss_verify and caused buffer over-read.
- Add a test_lms_runtime CI job (review #11): the existing matrix only
  builds; it cannot catch a semantic regression like the q_expand one
  above. The new job builds --enable-lms and --enable-lms=yes,small and
  runs testwolfcrypt for both.
- Make wc_hss_make_key loop index word32 (review #10) so the (int)cast
  on HSS_MAX_LEVELS in the second-loop bound is no longer needed.
- Add LMOTS_Y_LEN as the canonical name for the LM-OTS y[] length and
  alias LMS_PRIV_Y_TREE_LEN to it (review #6); switch the non-cache
  call sites in wc_hss_sign / wc_lms_sig_copy / wc_hss_sign_build_sig
  to LMOTS_Y_LEN to match intent.

Verified: 15 matrix rows still build clean under the conversion flags;
testwolfcrypt LMS Vfy / LMS pass for --enable-lms, --enable-lms=yes,small,
and --enable-lms=yes,small,sha256-192,shake256; bench_lms shows no
regression.

https://claude.ai/code/session_01EJmy1bKDgHseTwZ5Qqpu1g
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants