SLH-DSA: align HashSLH-DSA pre-hash API with HashML-DSA#19
Closed
Frauschi wants to merge 4 commits into
Closed
Conversation
…est)
The four wc_SlhDsaKey_*Hash* entry points used to take the full message and
hash it internally, while the ML-DSA counterparts (wc_dilithium_sign_ctx_hash
/ wc_dilithium_verify_ctx_hash) take the caller's pre-hashed digest. That
divergence made HashSLH-DSA the outlier among wolfSSL's PQ signature schemes
and against OpenSSL's HASH-ML-DSA, mldsa-native, leancrypto SLH-DSA, and
NIST ACVP signatureInterface=external/preHash test vectors -- all of which
take the digest at the pre-hash boundary.
Refactor slhdsakey_prehash_msg to validate that msgSz equals the digest size
for hashType (32 bytes for SHAKE128, 64 for SHAKE256 per FIPS 205 §10.2.2)
and copy the digest in place of the previous wc_*Hash call. Returns
BAD_LENGTH_E on size mismatch; BAD_FUNC_ARG / NOT_COMPILED_IN behaviour is
unchanged. Sign-/verify-side M' construction is untouched.
Also tighten the pre-existing wc_SlhDsaKey_SignMsg{Deterministic,WithRandom}
input validation (NULL/sigSz/key-flag checks) so they front-match the *Hash*
family. These are the FIPS 205 internal interface (Algorithm 19) and serve
as the SLH-DSA analog of wc_dilithium_verify_mu for ACVP signatureInterface=
internal testing -- documented as such.
In-tree callers updated to pre-hash before calling: wolfcrypt self-test in
slhdsa_test_param (SHA-256/SHAKE256/SHA-384 paths) and benchmark sign-pre/
vrfy-pre rows (SHA-256, hashed once outside the timed loop). API tests gain
BAD_LENGTH_E coverage, SHA-512 and SHAKE256 round-trips, and a new
test_wc_slhdsa_sign_msg covering the SignMsg* family plus a cross-check that
proves an externally-built M' sign matches a SignHashDeterministic byte-for-
byte. Doxygen and ChangeLog updated; entry flagged BREAKING.
https://claude.ai/code/session_015DkU7iXobMCKqSbMoTWCqd
- benchmark.c: gate the new prehash bench block on NO_SHA256 with a
SHAKE256 fallback so SHAKE-only SLH-DSA builds don't add a hard SHA-256
link-time dependency they didn't have before.
- ChangeLog.md: narrow the "stricter input validation" claim to
SignMsg{Deterministic,WithRandom}; VerifyMsg already had full validation
and was not changed.
- wc_slhdsa.c: rename the now-misnamed slhdsakey_prehash_msg helper to
slhdsakey_validate_prehash to reflect that it validates and copies
rather than hashing.
- Replace UTF-8 paragraph signs with ASCII "Section" in comments to match
the surrounding code's style and avoid encoding pitfalls in some
toolchains.
- doc/dox_comments: add \sa cross-references between the *Hash* and
*Msg* doxygen entries so users discovering one path can find the other.
- tests/api/test_slhdsa.c: add NOT_COMPILED_IN coverage for an
unsupported hashType, BAD_LENGTH_E coverage for the new
SignMsg{Deterministic,WithRandom} sigSz check, and reset sigLen
explicitly before SignMsgWithRandom in the round-trip rather than
silently relying on the prior call to have set it.
https://claude.ai/code/session_015DkU7iXobMCKqSbMoTWCqd
Frauschi
commented
May 10, 2026
…buffer Address PR review feedback: - Rename the public *Hash* API parameters from msg/msgSz to hash/hashSz to match the ML-DSA wc_dilithium_sign_ctx_hash / wc_dilithium_verify_ctx_hash parameter naming exactly. Affects wc_SlhDsaKey_SignHash, wc_SlhDsaKey_SignHashDeterministic, wc_SlhDsaKey_SignHashWithRandom, and wc_SlhDsaKey_VerifyHash. Callers using positional arguments are source-compatible. - Refactor slhdsakey_validate_prehash to drop the now-redundant ph output buffer. Previously the helper validated msgSz against the expected digest size and copied the input into a separate ph buffer; the callers then concatenated oid || ph or streamed them. Since ph was just a copy of the input, the helper now returns only oid/oidLen plus the validation error code, and the callers feed the caller-supplied digest directly into the M' construction. Eliminates the misleading ph[WC_MAX_DIGEST_SIZE] locals in slhdsakey_signhash_external and wc_SlhDsaKey_VerifyHash and the pointless XMEMCPY hop. - Update doxygen (source and dox_comments) accordingly. - ChangeLog: clarify the rename and that callers using positional arguments are source-compatible. https://claude.ai/code/session_015DkU7iXobMCKqSbMoTWCqd
Address self-review feedback: - Replace the magic byte phMsg[80] /* Max: 11 byte OID + 64 byte hash */ in slhdsakey_signhash_external and wc_SlhDsaKey_VerifyHash with byte phMsg[SLHDSA_PHMSG_MAX_LEN] where SLHDSA_PHMSG_MAX_LEN = SLHDSA_OID_MAX_LEN + WC_MAX_DIGEST_SIZE. The derived bound now scales automatically with the project-wide max digest size, so adding a future hashType with a larger digest in slhdsakey_validate_prehash won't silently overflow these buffers. - Replace three stale "/* Pre-hash sign. */" inline comments with "/* HashSLH-DSA sign with caller-supplied digest. */" since the functions no longer pre-hash anything. - ChangeLog: soften the "matches wc_dilithium_*_ctx_hash exactly" claim to acknowledge that ML-DSA uses hashLen while SLH-DSA keeps the local Sz suffix convention. https://claude.ai/code/session_015DkU7iXobMCKqSbMoTWCqd
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes an API inconsistency between wolfSSL's two FIPS-standardised post-quantum signature schemes:
wc_dilithium_sign_ctx_hash/wc_dilithium_verify_ctx_hash) takes the caller's pre-hashed digest.wc_SlhDsaKey_*Hash*) used to take the full message and hash it internally.This makes SLH-DSA the outlier within wolfSSL and against every other library checked (OpenSSL HASH-ML-DSA, mldsa-native, leancrypto SLH-DSA, sphincsplus reference) and against NIST ACVP
signatureInterface=external/preHash=preHashtest vectors — all of which take the digest at the pre-hash boundary. Breaking by design.Changes
wolfcrypt/src/wc_slhdsa.c— refactorslhdsakey_validate_prehash(renamed fromslhdsakey_prehash_msg) to validatemsgSz == digest size for hashTypeand copy, instead of callingwc_*Hash. ReturnsBAD_LENGTH_Eon size mismatch. SHAKE128/SHAKE256 lengths are fixed at 256/512 bits per FIPS 205 Section 10.2.2. Sign- and verify-side M' construction is unchanged.wc_SlhDsaKey_SignMsg{Deterministic,WithRandom}— gain front-door NULL / sigSz / key-flag validation matching the*Hash*family. These functions are the SLH-DSA analog ofwc_dilithium_verify_mufor ACVPsignatureInterface=internaltesting — documented as such.wolfcrypt/test/test.c(SHA-256/SHAKE256/SHA-384 paths inslhdsa_test_param) andwolfcrypt/benchmark/benchmark.c(SHA-256 prehash sub-bench, hashed once outside the timed loop, with a SHAKE256 fallback forNO_SHA256builds).tests/api/test_slhdsa.c):BAD_LENGTH_Erejection coverage for both*Hash*and*Msg*sigSzpathsNOT_COMPILED_INcoverage for unsupportedhashTypetest_wc_slhdsa_sign_msgcovering theSignMsg*family plus a byte-for-byte cross-check that an externally-built M' produces the same signature as the matchingSignHashDeterministiccallmsgsemantics,BAD_LENGTH_Ereturns, pre-hash examples, and\sacross-references between the*Hash*and*Msg*API families.BREAKING (FIPS 205 SLH-DSA)entry under the unreleased Enhancements with migration guidance.Verification
--enable-slhdsa(full)testwolfcryptSLH-DSA pass, all 16 SLH-DSA API tests pass--enable-slhdsa=verify-onlybenchmark -slhdsasign-pre/vrfy-prerows present, noBAD_LENGTH_ETest plan
--enable-slhdsa=verify-onlyNO_SHA256SLH-DSA configuration if such a target exists*Hash*and three*Msg*functionsMigration
Callers that need the previous "pass full message" semantics for SLH-DSA can use
wc_SlhDsaKey_Sign/wc_SlhDsaKey_Verify(the pure interface) instead.https://claude.ai/code/session_015DkU7iXobMCKqSbMoTWCqd
Generated by Claude Code