Skip to content

Transition from liboqs SPHINCS+ to native SLH-DSA (FIPS 205)#5

Open
Frauschi wants to merge 15 commits intomasterfrom
claude/sphincs-to-slhdsa-transition-VhszI
Open

Transition from liboqs SPHINCS+ to native SLH-DSA (FIPS 205)#5
Frauschi wants to merge 15 commits intomasterfrom
claude/sphincs-to-slhdsa-transition-VhszI

Conversation

@Frauschi
Copy link
Copy Markdown
Owner

Summary

This PR replaces the liboqs-based pre-standardization SPHINCS+ implementation with the native FIPS 205 SLH-DSA implementation across all higher layers (certificate/ASN.1/X.509 and TLS 1.3 handshake). All liboqs SPHINCS+ code is removed.

The transition follows two specifications:

  • RFC 9909: X.509 Algorithm Identifiers for SLH-DSA
  • draft-reddy-tls-slhdsa-02: Use of SLH-DSA in TLS 1.3

Motivation

wolfSSL has a native FIPS 205 SLH-DSA implementation in wolfcrypt/src/wc_slhdsa.c, but it previously lacked higher-layer integration. The liboqs-based SPHINCS+ wrapper used pre-standardization OIDs (1.3.9999.6.x) and depended on the external liboqs library. This PR switches everything to the standardized native implementation and removes the liboqs SPHINCS+ dependency entirely.

A future standalone effort will remove liboqs support for ML-KEM and ML-DSA as well.

Changes

New DER codec for SLH-DSA

  • wc_SlhDsaKey_PrivateKeyDecode, wc_SlhDsaKey_PublicKeyDecode
  • wc_SlhDsaKey_KeyToDer, wc_SlhDsaKey_PrivateKeyToDer, wc_SlhDsaKey_PublicKeyToDer
  • Uses existing DecodeAsymKey/SetAsymKeyDer helpers following the Dilithium pattern

OIDs (RFC 9909)

Added 12 standardized NIST OIDs under 2.16.840.1.101.3.4.3:

  • SHA2 variants: sigAlgs 20-25 (definitions only, no implementation)
  • SHAKE variants: sigAlgs 26-31 (fully functional, matches native SLH-DSA support)

AlgorithmIdentifier parameters are absent (per RFC 9909). Context string is empty for X.509 signing and TLS CertificateVerify.

Certificate/ASN.1 layer

  • Replaced ~487 SPHINCS lines in wolfcrypt/src/asn.c
  • Replaced ~68 SPHINCS lines in wolfcrypt/src/asn_orig.c
  • All SPHINCS PEM headers/footers replaced with SLH-DSA equivalents
  • Certificate signing uses wc_SlhDsaKey_Sign(key, NULL, 0, ...)
  • Certificate verification uses wc_SlhDsaKey_Verify(key, NULL, 0, ...) (returns 0 on success)
  • Fixed two pre-existing bugs in SPHINCS cert verify code during the port

X.509 layer (src/x509.c)

  • Replaced sphincs_key handling with SlhDsaKey
  • Uses DYNAMIC_TYPE_SLHDSA

TLS 1.3 handshake (src/tls13.c, src/internal.c, src/ssl.c, src/ssl_load.c)

Per draft-reddy-tls-slhdsa-02:

  • Added 6 SHAKE SignatureScheme code points (0x0917-0x091C)
  • EncodeSigAlg/DecodeTls13SigAlg for SLH-DSA (shares SA_MAJOR=0x09 with Dilithium, disambiguated by minor byte)
  • SendTls13CertificateVerify / DoTls13CertificateVerify with empty context
  • Peer key management (peerSlhDsaKey, peerSlhDsaKeyPresent)
  • Key lifecycle in AllocKey/FreeKey/ReuseKey
  • ProcessPeerCerts extracts SLH-DSA peer keys
  • ProcessBufferTryDecodeSlhDsa for loading SLH-DSA private keys
  • haveSlhDsaSig, minSlhDsaKeySz added to Options and CTX
  • SIG_SLHDSA flag in SIG_ALL

Build system

  • HAVE_SPHINCS removed from HAVE_LIBOQS block in settings.h
  • sphincs.c removed from BUILD_LIBOQS sources
  • Updated: CMakeLists.txt, all IDE project files (VS, VS2022, Xcode, INTIME-RTOS, MPLABX16, Espressif, Zephyr, Renesas, CSharp wrapper), configure.ac, cmake/functions.cmake, rpm/spec.in

Types and settings

  • MAX_ENCODED_SIG_SZ conditional updated to WOLFSSL_HAVE_SLHDSA
  • MAX_X509_HEADER_SZ conditional updated
  • WC_ENABLE_ASYM_KEY_EXPORT/IMPORT conditionals updated
  • DYNAMIC_TYPE_SPHINCS removed (replaced by existing DYNAMIC_TYPE_SLHDSA)

Test infrastructure

  • Generated 6 SLH-DSA test key DER files in certs/slhdsa/ (all SHAKE parameter sets)
  • Updated certs_test.h with SLH-DSA test keys
  • Updated gencertbuf.pl to reference SLH-DSA DER files
  • SLH-DSA benchmarks already exist in wolfcrypt/benchmark/benchmark.c; SPHINCS+ benchmark code removed
  • scripts/asn1_oid_sum.pl updated with the 12 new SLH-DSA OIDs

Removed

  • wolfcrypt/src/sphincs.c
  • wolfssl/wolfcrypt/sphincs.h
  • certs/sphincs/ directory
  • SPHINCS references in doc/dox_comments/, INSTALL, .wolfssl_known_macro_extras

Test plan

  • Build succeeds: ./configure --enable-slhdsa && make
  • Unit tests pass: ./wolfcrypt/test/testwolfcrypt reports SLH-DSA test passed! along with all other algorithms
  • No remaining SPHINCS references in source code (only historical mentions in ChangeLog.md and INSTALL where SLH-DSA is described as the standardized name of SPHINCS+)
  • Build works without liboqs (SLH-DSA is native)
  • Build works with liboqs (remaining Falcon/Dilithium/ML-KEM liboqs support intact) — should be verified in CI
  • End-to-end TLS 1.3 handshake with SLH-DSA certificate — should be verified with example client/server

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV

claude added 15 commits April 9, 2026 21:29
Foundation for transitioning from liboqs SPHINCS+ to native SLH-DSA
(FIPS 205) per RFC 9909.

- Add 12 SLH-DSA OIDs (6 SHAKE + 6 SHA2) to oid_sum.h, replacing
  SPHINCS+ pre-standardization OIDs with NIST standardized OIDs
  (2.16.840.1.101.3.4.3.{20-31})
- Add DER codec functions to wc_slhdsa.c: PrivateKeyDecode,
  PublicKeyDecode, KeyToDer, PrivateKeyToDer, PublicKeyToDer
- Add SLH-DSA key type enums in asn.h (replacing SPHINCS key types)
- Add SLH-DSA cert type enums in asn_public.h
- Update SignatureCtx key union to use SlhDsaKey instead of sphincs_key

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
- Add SLH-DSA SignatureScheme code points to internal.h per
  draft-reddy-tls-slhdsa-02 (0x0917-0x091C for SHAKE variants)
- Add SLH-DSA signature algorithm enum values, SIG_SLHDSA flag,
  peer key storage, and min key size fields
- Update types.h: MAX_ENCODED_SIG_SZ for SLH-DSA (49856 bytes)
- Update settings.h: remove HAVE_SPHINCS from liboqs block,
  replace with WOLFSSL_HAVE_SLHDSA in asym key export/import
- Update ssl.c: replace sphincs.h include with wc_slhdsa.h
- Update build system: remove sphincs.c/sphincs.h from include.am

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
Update src/x509.c to use native SLH-DSA (FIPS 205) instead of
liboqs SPHINCS+ for X.509 public key handling. Replace all
sphincs_key usage with SlhDsaKey, update OID checks to use
SLH-DSA constants, and use the new SLH-DSA API.

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
- Remove all SPHINCS+ benchmark code (bench_sphincsKeySign, flags,
  CLI options) from benchmark.c/h. SLH-DSA benchmarks already exist.
- Update gencertbuf.pl to reference SLH-DSA test key files instead
  of SPHINCS+ files.

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
Remove all SPHINCS+ test key arrays from certs_test.h. SLH-DSA
test keys will be generated and added in a subsequent commit.
Update gencertbuf.pl to reference SLH-DSA file paths.

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
Major update to wolfcrypt/src/asn.c and asn_orig.c:
- Replace all SPHINCS+ OID arrays with SLH-DSA NIST OIDs per RFC 9909
  (SHA2: sigAlgs 20-25, SHAKE: sigAlgs 26-31)
- Replace sphincs_key with SlhDsaKey throughout
- Update certificate signing to use wc_SlhDsaKey_Sign with empty context
- Update certificate verification to use wc_SlhDsaKey_Verify (returns
  0 on success instead of setting int* res)
- Replace all SPHINCS key type, cert type, and OID constants
- Update PEM header/footer strings
- Update MakeAnyCert/MakeCertReq signatures
- Change HAVE_SPHINCS guards to WOLFSSL_HAVE_SLHDSA

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
- Remove sphincs.c/sphincs.h references from all IDE project files
  (VS, Xcode, Espressif, INTIME-RTOS, MPLABX, Zephyr, CSharp)
- Update scripts/asn1_oid_sum.pl with SLH-DSA OIDs (RFC 9909)
- Update certs/include.am, cmake/functions.cmake, rpm/spec.in
- Update INSTALL and doxygen docs
- Update .wolfssl_known_macro_extras

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
- Delete wolfcrypt/src/sphincs.c and wolfssl/wolfcrypt/sphincs.h
- Delete certs/sphincs/ directory with all test key DER files
- Fix remaining HAVE_SPHINCS reference in asn.h pkCurveOID guard

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
- Remove sphincs.c link from Renesas e2studio project file
- Update INSTALL to say "FIPS 205" instead of "SPHINCS+"

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
- Fix SignatureCtx key union member: slhDsa -> slhdsa (matching
  the member name defined in asn.h)
- Generate SLH-DSA test key DER files for all 6 SHAKE parameter
  sets using wc_SlhDsaKey_MakeKey + wc_SlhDsaKey_KeyToDer

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
- Initialize haveSlhDsaSig on client side in InitSSL and InitSSL_Ctx
- Initialize minSlhDsaKeySz from MIN_SLHDSAKEY_SZ
- Copy SLH-DSA settings from ctx to ssl
- Add SLH-DSA signature algorithms to AddSuiteHashSigAlgo for
  signature_algorithms extension (all 6 SHAKE variants)

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
Add SLH-DSA signature scheme support per draft-reddy-tls-slhdsa-02:
- EncodeSigAlg: map slhdsa_shake_*_sa_algo to wire bytes (0x09, 0x17-0x1C)
- DecodeTls13SigAlg: parse SLH-DSA minor bytes within SLHDSA_SA_MAJOR
- SendTls13CertificateVerify: add SLH-DSA signing path using
  wc_SlhDsaKey_Sign with empty context per RFC 9909
- DoTls13CertificateVerify: add SLH-DSA verification path
- MatchSigAlgo: add SLH-DSA signature algorithm matching
- Add WOLFSSL_HAVE_SLHDSA to all relevant conditional guards

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
Add individual ADD_HASH_SIG_ALGO dispatch entries for each SLH-DSA
SHAKE variant in the per-algorithm branch of AddSuiteHashSigAlgo,
encoding the SA_MAJOR/SA_MINOR wire bytes.

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
Comprehensive TLS 1.3 SLH-DSA support per draft-reddy-tls-slhdsa-02:

tls13.c:
- EncodeSigAlg/DecodeTls13SigAlg for all 6 SHAKE variants
- SendTls13CertificateVerify: SLH-DSA signing with empty context
- DoTls13CertificateVerify: SLH-DSA verification

internal.c:
- DecodeSigAlg: handle SLH-DSA minor bytes (shares 0x09 major with Dilithium)
- FreeKey/AllocKey/ReuseKey: SlhDsaKey lifecycle
- ProcessPeerCerts: extract SLH-DSA peer keys
- ProcessPeerCertCheckKey: size validation
- MatchSigAlgo: match SLH-DSA OIDs to sig algos
- SetCipherListFromBytes: include SLH-DSA in haveSig
- DecodePrivateKey_ex: SLH-DSA private key decoding

ssl_load.c:
- ProcessBufferKeySet/CertSetHave: detect SLH-DSA keys/certs
- ProcessBufferTryDecodeSlhDsa: new decode function

ssl.c:
- Copy haveSlhDsaSig from ctx to ssl

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
Fixes a bug where wc_SlhDsaKey_PrivateKeyDecode and
wc_SlhDsaKey_PublicKeyDecode only decoded DER keys whose OID matched
the parameter set the key was initialized with. This made it impossible
to load any variant other than SLHDSA_SHAKE128S through SSL/TLS, since
ProcessBufferTryDecodeSlhDsa initialized with a fixed placeholder param.

Both decode functions now iterate over the compiled-in parameter sets
and try each keytype against the DER OID until one matches. On success,
key->params is updated to the detected parameter set so subsequent
size/import calls use the correct values.

Also:
- Removed the "try raw import first" shortcut from PublicKeyDecode. It
  didn't match the function's documented contract (DER only) and could
  silently accept random bytes that happened to be 2*n long.
- Simplified GetKeyOID's SLH-DSA branch in asn.c from seven per-variant
  init/decode attempts to a single decode call plus a switch over the
  detected param. ~90 lines removed.
- Added test_wc_slhdsa_der_roundtrip which, for every compiled-in SHAKE
  parameter set, generates a key, encodes to DER, decodes into a key
  initialized with a DIFFERENT placeholder param, checks that the
  decoded key's params field matches the original, signs a message
  with the decoded private key, and verifies with the original and
  with the DER-decoded public key. This test would have caught the
  original bug immediately.

https://claude.ai/code/session_019gqvW3ZMKGGyi6zCRNPDYV
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