Skip to content

Claude/add slh dsa sha2 variants followup#12

Closed
Frauschi wants to merge 6 commits intomasterfrom
claude/add-slh-dsa-sha2-variants-followup
Closed

Claude/add slh dsa sha2 variants followup#12
Frauschi wants to merge 6 commits intomasterfrom
claude/add-slh-dsa-sha2-variants-followup

Conversation

@Frauschi
Copy link
Copy Markdown
Owner

Description

Please describe the scope of the fix or feature addition.

Fixes zd#

Testing

How did you test?

Checklist

  • added tests
  • updated/added doxygen
  • updated appropriate READMEs
  • Updated manual and documentation

Frauschi and others added 6 commits April 21, 2026 10:52
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 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, 4*n bytes = SK.seed || SK.prf ||
  PK.seed || PK.root, no nested wrapper) and OID auto-detection across
  all six SHAKE parameter sets.
- 12 standardized NIST OIDs (6 SHA2 + 6 SHAKE) per RFC 9909. OID
  collision mechanism cleaned up since NIST OIDs don't collide.
- Complete ASN.1 layer replacement (~500 lines in asn.c) and X.509
  public-key handling in x509.c.
- SHA2-SLH-DSA OIDs are recognized but the native backend is
  SHAKE-only; Private/PublicKeyDecode and ConfirmSignature return
  NOT_COMPILED_IN for SHA2 variants so callers get an informative
  error instead of a generic parse failure.
- wc_GetKeyOID selects its placeholder parameter from whatever SHAKE
  variant is compiled in, so auto-detect works when specific variants
  (e.g. SHAKE128F) are disabled.
- wc_SlhDsaKey_PublicKeyDecode saves and restores key->params and
  *inOutIdx on ImportPublic failure, matching PrivateKeyDecode.
- DER round-trip and on-disk decode tests (bench_slhdsa_shake*_key.der
  fixtures regenerated with wolfSSL's own encoder, guaranteed
  RFC 9909 compliant) guard against future encoding drift.
- SLH-DSA test cert chain generated with OpenSSL 3.5.
- DYNAMIC_TYPE_SPHINCS = 98 kept as RESERVED with a tombstone comment
  for ABI stability; new code should use DYNAMIC_TYPE_SLHDSA (107).
- All build system / IDE project files updated; SPHINCS+ sources,
  headers, and test data removed.
Commit 9c22cf1 integrated SLH-DSA at the cert layer with all twelve
RFC 9909 OIDs recognised but only the six SHAKE variants functional;
SHA-2 OIDs were short-circuited to NOT_COMPILED_IN pending the native
SHA-2 backend. Upstream PR wolfSSL#9843 (now merged at master via cf2db42)
added that backend, so this commit drops the short-circuits and finishes
the dispatch wiring.

Changes:
- wolfcrypt/src/wc_slhdsa.c: replace the SHA-2 NOT_COMPILED_IN cases in
  slhdsa_keytype_to_param() and slhdsa_param_to_keytype() with proper
  parameter mappings, gated by WOLFSSL_SLHDSA_SHA2 +
  WOLFSSL_SLHDSA_PARAM_NO_SHA2_* (PR wolfSSL#9843's macro names). Resolve
  merge conflict in wc_SlhDsaKey_ImportPublic() by keeping the |= flag
  preservation from the cert-layer integration and the SHA-2 midstate
  precomputation from PR wolfSSL#9843.
- wolfcrypt/src/asn.c: extend ConfirmSignature() (SIG_STATE_KEY,
  SIG_STATE_DO, SIG_STATE_CHECK arms) and the keyType -> SLHDSA_*
  switches in EncodePublicKey(), MakeAnyCert(), wc_MakeCert_ex(),
  wc_SignCert_ex(), and the four other sites that dispatch on
  SLH_DSA_SHAKE_*_TYPE. Add SHA-2 PEM header/footer strings.
- src/x509.c: extend the if-else chain that maps pubKeyOID to
  (type, SlhDsaParam) for SLH-DSA cert public-key load and the
  matching cleanup chain.
- certs/slhdsa/bench_slhdsa_sha2_*_key.der: add six DER fixtures
  (one per SHA-2 parameter set) for the decode-files round-trip test.
- tests/api/test_slhdsa.c: extend test_wc_slhdsa, _sizes,
  _der_roundtrip, _der_decode_files for SHA-2 variants. Add a new
  test_wc_slhdsa_cert_roundtrip that mints a self-signed cert with
  each of the twelve variants via wc_MakeCert_ex/wc_SignCert_ex and
  parses it back through ParseCert.
- scripts/slhdsa-interop.test: new driver that runs the four-stage
  matrix (testwolfcrypt, cert-roundtrip, der-roundtrip, decode-files)
  for all twelve variants and optionally exercises OpenSSL+oqs-provider
  cross-stack when OQS_PROVIDER=1 is set.
- scripts/include.am: wire the new test into BUILD_WC_SLHDSA.

Verified: 48/48 PASS in scripts/slhdsa-interop.test; oqs-provider
absent on this host so the cross-stack arm reports SKIPPED-EXTERNAL.

https://claude.ai/code/session_01QV3GAEfzRSTiGsptuh8kdD
Fixes raised in self-review of 0030231:

ConfirmSignature SLH-DSA arm fixes wc_SlhDsaKey_PublicKeyDecode (which
expects an SPKI envelope) being called with cert->ca->publicKey -- which
is the raw key bytes that StoreKey() copies out of the BIT STRING. The
SHAKE side had this latent bug since 9c22cf1; it surfaced when the new
test_wc_slhdsa_cert_roundtrip drove ConfirmSignature for the first
time. Replaced with wc_SlhDsaKey_ImportPublic on the raw bytes.

slhdsa_keytype_to_param() and slhdsa_param_to_keytype(): the SHA-2 arms
now use the same axis macros as the SHAKE side
(WOLFSSL_SLHDSA_PARAM_NO_SHA2_{128,192,256} and
WOLFSSL_SLHDSA_PARAM_NO_SHA2_{SMALL,FAST}) instead of the per-variant
WOLFSSL_SLHDSA_PARAM_NO_SHA2_*S/F. Setting WOLFSSL_SLHDSA_PARAM_NO_SHA2_128
now drops both 128S and 128F symmetrically with WOLFSSL_SLHDSA_NO_128.

Restored the NOT_COMPILED_IN diagnostic when a SHA-2 SLH-DSA OID is
parsed in a build that did not enable WOLFSSL_SLHDSA_SHA2: both
slhdsa_keytype_to_param and the SIG_STATE_KEY arm of ConfirmSignature
now report NOT_COMPILED_IN rather than the generic ASN_UNKNOWN_OID_E /
BAD_FUNC_ARG.

Documented the safety reasoning for the |= flag preservation in
wc_SlhDsaKey_ImportPublic and removed the now-stale workaround comment
in test_wc_slhdsa_check_key. Documented the underscore PEM label
non-interop convention.

Tests: extended test_wc_slhdsa_make_key and test_wc_slhdsa_sign_vfy to
iterate all six SHA-2 variants (they previously only iterated SHAKE).
test_wc_slhdsa_cert_roundtrip now drives ConfirmSignature directly with
the parsed cert components (NO_VERIFY parse + manual ConfirmSignature),
so the SHA-2 OID dispatch in ConfirmSignature is actually exercised end
to end -- not just claimed to be.

scripts/slhdsa-interop.test rewritten for genuine per-variant
resolution: forks one process per variant via the new
wolfcrypt/test/slhdsa_interop_one helper which mints a self-signed cert
in that variant and runs ConfirmSignature against it. PASS/FAIL/SKIP-LOCAL
status is now per-variant rather than a single test result fanned out.
The OQS_PROVIDER stage is relabeled as a registration probe rather than
"interop" since it doesn't run a TLS handshake.

scripts/gen-slhdsa-fixtures.c: committed the previously-throwaway
generator for the bench_slhdsa_*_key.der fixtures so reviewers and
future maintainers can regenerate them. Run from the source root after
build; not part of the default build (kept under scripts/).

Verified: ./tests/unit.test reports test_wc_slhdsa_cert_roundtrip
passing for all 12 variants; ./scripts/slhdsa-interop.test reports
12 PASS / 0 FAIL / 0 SKIP-LOCAL via 12 separate processes.

https://claude.ai/code/session_01QV3GAEfzRSTiGsptuh8kdD
Addresses the second-round review of fac240d:

ConfirmSignature SLH-DSA arm: deleted the orphaned word32 idx
declaration and (void)idx cast. Tightened the comment to flag that
this changes verify behaviour for ALL SLH-DSA variants (SHAKE +
SHA-2), since the prior code path was never working in either case.

Dispatch gating in slhdsa_keytype_to_param() and
slhdsa_param_to_keytype() now uses per-variant macros
(WOLFSSL_SLHDSA_PARAM_NO_SHA2_*S/F) matching the SlhDsaParams[] table,
not the axis macros (_128/_192/_256/_SMALL/_FAST). A user_settings.h
that disables a single SHA-2 variant via the per-variant macro alone
no longer leaves the dispatch pointing at a parameter set that
slhdsa_find_params() will not find.

Added SLHDSA_SHA2_OID_CASE_LABELS to wolfssl/wolfcrypt/asn.h so the
six SHA-2 OID case labels exist as a single definition. Used at the
four sites that previously open-coded the list (NOT_COMPILED_IN tail
in slhdsa_keytype_to_param; SIG_STATE_KEY / SIG_STATE_DO /
SIG_STATE_CHECK arms in ConfirmSignature).

scripts/gen-slhdsa-fixtures.c: deterministic. Switched from
wc_SlhDsaKey_MakeKey(rng) to MakeKeyWithRandom() with fixed
per-variant fill bytes for SK.seed, SK.prf, PK.seed. Re-running the
generator now produces byte-identical .der files. Wired into
autotools as a noinst_PROGRAMS target so it builds with `make` (no
more bespoke gcc invocation in the header) under
--enable-slhdsa --enable-keygen --enable-certgen. All twelve
bench_slhdsa_*_key.der fixtures regenerated deterministically.

scripts/slhdsa-interop.test:
  - Honours $(top_builddir) / $(top_srcdir) when run from automake
    so out-of-tree builds find the helper binary.
  - Counts PROBE-OK in the summary so totals balance to 24 (12
    variants x 2 stages) when the OQS path runs successfully.
  - Aborts on rc=99 (framework error) from the helper rather than
    treating it as a regular FAIL.
  - Comment on the note_* helpers warns the next maintainer that the
    counters are shell-local and a subshell wrapper would zero them.

wolfcrypt/test/slhdsa_interop_one: returns 99 (automake hard error)
for unknown-variant or wrong-arg-count, so a typo in a future driver
won't be misclassified as a per-variant test FAIL. Added a sister-
function pointer to test_wc_slhdsa_cert_roundtrip so future API
changes can find both copies.

DISTCLEANFILES: added wolfcrypt/test/.libs/slhdsa_interop_one and
scripts/.libs/gen-slhdsa-fixtures so `make distclean` sweeps them.

tests/api/test_slhdsa.c: removed the leftover <wolfssl/ssl.h>
include (the CertManager attempt was abandoned in the previous round
but the include lingered).

Verified: all 13 SLH-DSA unit tests pass; scripts/slhdsa-interop.test
reports 12 PASS / 0 FAIL / 0 SKIP-LOCAL / 0 PROBE-OK / 12 SKIP-EXTERNAL
with balanced totals; gen-slhdsa-fixtures produces byte-stable
fixtures across re-runs.

https://claude.ai/code/session_01QV3GAEfzRSTiGsptuh8kdD
@Frauschi Frauschi closed this Apr 27, 2026
@Frauschi Frauschi deleted the claude/add-slh-dsa-sha2-variants-followup branch May 6, 2026 09:19
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