SLH-DSA in Depth · 3 of 5L3algorithms
SHA-2 versus SHAKE in SLH-DSA: The Category-Five Break, the SHA-512 Patch and the Single-Block Trick
FIPS 205 quietly swaps SHA-256 for SHA-512 in four of six functions at its higher categories. The reason is a 256-bit internal state, and it explains which functions changed.
Abstract
SLH-DSA can be instantiated with SHAKE256 or with SHA-2, and the two are not mirror images. The SHAKE sets use one function everywhere; the SHA-2 sets at categories 3 and 5 mix SHA-256 and SHA-512, a change made late in standardisation after Sydney Antonov observed that the SHA-256 construction lacked the multi-target property the proof needs at category 5, and Perlner, Kelsey and Cooper turned that observation into a forgery costing about 40 bits of classical security. This article explains the difference between a Merkle-Damgard chaining value and a sponge capacity, reconstructs why a 256-bit internal state is too narrow for 256-bit multi-target security, and offers an explanation, our own analysis rather than NIST's stated rationale, of why exactly H, T, H_msg and PRF_msg moved to SHA-512 while F and PRF stayed on SHA-256: the functions that changed are the ones whose tweak is absorbed before the last compression. It then covers the related MGF1 fix, the padding and compressed-address trick that makes SHA-2 fast, the pre-hash pairing rule, and how to choose a family for long-lived keys that sign model releases and agent tools.
Most readers of FIPS 205 meet the hash family as a label in the parameter-set name, SHA2 or SHAKE, and assume the choice is about speed or taste. Read Section 11 carefully and an asymmetry appears. The six SHAKE parameter sets use SHAKE256 for every function at every security level. The two category-1 SHA-2 sets use SHA-256 throughout. But the four SHA-2 sets at categories 3 and 5 use SHA-512 for the message hash, the message PRF, the tree hash H and the compression function T, while keeping SHA-256 for the chain function F and the secret-generating PRF. That split was not in the round-three submission. It is the visible scar of an attack found in 2022, and once its cause is understood, the exact set of functions that changed stops looking arbitrary and starts looking inevitable.
Six functions, two families, three recipes
FIPS 205 defines SLH-DSA in terms of six functions: PRF_msg, which makes the per-message randomiser; H_msg, which turns the randomiser, the public key and the message into the digest; PRF, which expands the secret seed into every one-time and few-time secret; and the three tweakable hashes F, H and T, which build Winternitz chains, Merkle-tree nodes and the compressions of many values into one. A parameter set is a choice of sizes plus an instantiation of these six.
For SHAKE, Section 11.1 is uniform: every function is SHAKE256 applied to a simple concatenation of its inputs with the output length set to what the function needs. For SHA-2 there are two recipes. Section 11.2.1, for the category-1 sets, builds the tweakable hashes and PRF from SHA-256 over a padded public seed, a compressed address and the input, uses HMAC-SHA-256 for PRF_msg and MGF1 over SHA-256 for H_msg. Section 11.2.2, for categories 3 and 5, keeps that recipe for F and PRF but replaces SHA-256 by SHA-512 in H, T, PRF_msg and H_msg.
Appendix A of the standard records the change and its reason in one line: for the category 3 and 5 SHA-2 sets, SHA-256 was replaced by SHA-512 in H_msg, PRF_msg, H and T, based on weaknesses discovered when using SHA-256 to obtain category-5 security, with citations to a 2021 forum discussion, Antonov's 2022 official comment and the 2022 paper by Perlner, Kelsey and Cooper. To see why, it helps to look inside the two hash designs.
| Function | SHAKE sets (all categories) | SHA-2, category 1 | SHA-2, categories 3 and 5 |
|---|---|---|---|
| F (chain step) | SHAKE256 | SHA-256 | SHA-256 |
| PRF (secret values) | SHAKE256 | SHA-256 | SHA-256 |
| H (tree node) | SHAKE256 | SHA-256 | SHA-512 |
| T (compression of many values) | SHAKE256 | SHA-256 | SHA-512 |
| PRF_msg (randomiser) | SHAKE256 | HMAC-SHA-256 | HMAC-SHA-512 |
| H_msg (message digest) | SHAKE256 | MGF1-SHA-256 over SHA-256 | MGF1-SHA-512 over SHA-512 |
Chaining value versus capacity
SHA-256 and SHA-512 are Merkle-Damgard hashes. They split the input into fixed-size blocks, 64 bytes for SHA-256 and 128 bytes for SHA-512, and carry an internal chaining value from block to block, 256 bits for SHA-256 and 512 bits for SHA-512. Each block is mixed into the chaining value by a compression function; the final chaining value, or a truncation of it, is the output. SHAKE256 is a sponge. It absorbs input into a 1,600-bit state, of which a portion called the capacity, 512 bits for SHAKE256, is never directly exposed to input or output, and it is the capacity that bounds generic attacks on the internal state.
For short outputs the two designs look alike: a 256-bit SHA-256 output and a 256-bit SHAKE256 output both offer 256-bit second-preimage resistance against a single target. The difference appears when an attacker can aim at internal states rather than outputs. In a Merkle-Damgard hash, every intermediate chaining value of a long input is itself a 256-bit target: match it with a different prefix and the rest of the input completes a second preimage. Kelsey and Schneier showed in 2005 that long messages, which offer many intermediate states, make second preimages much cheaper than the output length suggests. A sponge with a 512-bit capacity has internal states twice as wide as a 256-bit output, so the same kind of attack has no room to work at the 256-bit level.
The SPHINCS+ version 3.1 specification states the consequence directly. Its section on SHAKE notes that attacks searching for 512-bit internal collisions are no concern even at the highest category. Its section on SHA-2 notes that SHA-256 has a chaining value of only 256 bits, making it slightly weaker in some metrics than SHAKE256 with a 256-bit output, and that SHA-512 is therefore used in some cases to reach the target level.
The category-five break
The security proof of SPHINCS+ needs the tweakable hashes to resist second preimages in a multi-target, distinct-function setting: an attacker facing many targets, each produced by a function with a different tweak, should gain nothing from their number. The tweaked SHA-2 construction gets its tweak from the public seed and address, which are absorbed into the chaining value at the start of the computation. For a long input, every chaining value computed after the tweak has been absorbed is a 256-bit value that no longer remembers which tweak produced it.
In 2022 Sydney Antonov observed on the NIST post-quantum forum that, as a result, the SHA-256-based construction did not have the required distinct-function multi-target second-preimage resistance at category 5 for several of the submitted parameter sets. The abstract of the subsequent paper by Ray Perlner, John Kelsey and David Cooper of NIST states the open question plainly, whether the observation led to a forgery, and answers it: a complete forgery attack that reduces the concrete classical security of those parameter sets by approximately 40 bits. In outline, the attack applies Antonov's technique to the WOTS+ public-key compression, where many long inputs are hashed, obtains a new one-time key that can sign a very limited set of values, and from it builds a slightly altered hypertree that signs arbitrary messages with signatures that appear valid.
The weakness was confined to the SHA-2 instantiation at the high categories; SHAKE was unaffected, and so was SHA-256 at category 1, where 128-bit targets sit far below a 256-bit chaining value. The fix in version 3.1 of the specification, carried into FIPS 205, was to give the affected functions a wider internal state by moving them to SHA-512. FIPS 205 applies the change at category 3 as well. By the same arithmetic, which is our reading rather than NIST's stated reason, a 256-bit state leaves only 64 bits of headroom above a 192-bit target, less than the roughly 70 bits of targets that one key exposes over its budget.
Why exactly four functions changed
NIST's text lists what changed but does not explain why F and PRF were left on SHA-256. The following explanation is our own analysis, built from the input formats in Section 11.2, and it accounts for the pattern exactly. The weakness needs an intermediate chaining value computed after the tweak is absorbed and before the output is produced. Whether such a value exists depends on how many compression-function calls follow the block that contains the address.
Every tweaked SHA-256 call starts with the public seed zero-padded to a full 64-byte block, which the implementation can hash once per key and reuse. What follows is the 22-byte compressed address and the input. For F at category 5 the input is one 32-byte value, so the remaining bytes, 22 plus 32, plus SHA-256's mandatory padding of at least 9 bytes, total 63 and fit in one final block. The address and the input are absorbed by the same compression call that produces the output; there is no post-tweak intermediate state to target. PRF has the same shape with SK.seed as its input. H, by contrast, hashes two 32-byte values, 86 bytes after the prefix, which spills into a second block; T hashes dozens or hundreds of values; PRF_msg and H_msg hash the whole message. Each of these four has intermediate chaining values after the tweak, and those are exactly the four functions that moved to SHA-512.
The move to SHA-512 helps H twice over. SHA-512 works on 128-byte blocks, and FIPS 205 pads the public seed to 128 bytes for the SHA-512 functions, so the 22-byte address, the two 32-byte children and SHA-512's minimum padding of 17 bytes come to 103 bytes and fit in one final block. Under SHA-512 the tree hash, like F under SHA-256, absorbs its tweak in the call that produces the output; and even where longer inputs still need several blocks, as in T and H_msg, the intermediate states are now 512 bits wide, twice the largest target. Either property alone would close the gap the attack used; for H the standard gets both.
The observation doubles as a review rule. Any future instantiation, or any custom variant a team is tempted to build, should be checked for intermediate states that are narrower than twice the security target and are computed after the domain separation is absorbed. The SHAKE instantiation passes that check everywhere because its 512-bit capacity already exceeds twice the largest target of 256 bits.
| Function | Bytes after the seed block | Calls after the tweak | Post-tweak intermediate state? | Category 5 hash in FIPS 205 |
|---|---|---|---|---|
| F | 22 + 32 = 54 | 1 | No | SHA-256 |
| PRF | 22 + 32 = 54 | 1 | No | SHA-256 |
| H | 22 + 64 = 86 | 2 | Yes | SHA-512 |
| T for a WOTS+ key | 22 + 67 x 32 = 2,166 | 34 | Yes | SHA-512 |
| H_msg, PRF_msg | message length | many | Yes | SHA-512 |
The long-message fix inside H_msg
A second, related change sits inside the message hash. For the SHA-2 sets, H_msg is computed in two stages: an inner SHA-256 or SHA-512 hash of the randomiser, the public seed, the public root and the message, then MGF1, a mask-generation function from the RSA standard, which stretches that inner hash to the m bytes the digest needs. Appendix A of FIPS 205 notes that R and the public seed were added as inputs to MGF1 in order to mitigate multi-target long-message second-preimage attacks.
The logic mirrors the chaining-value story. Messages can be arbitrarily long, so the inner hash of a long message passes through many intermediate states, and an attacker who wants any digest to collide with any earlier one benefits from the number of targets. Feeding R and the public seed into the outer MGF1 stage re-tweaks the final expansion with per-signature and per-key values, so a match on an inner state is not automatically a match on the digest that selects FORS positions. Together with the move to SHA-512 at the higher categories, the change leaves the message path with the same kind of margin as the tree path.
For SHAKE there is no separate stage: H_msg is SHAKE256 over the randomiser, the public seed, the public root and the message, read out to m bytes. The sponge's capacity provides the margin directly, which is one reason the SHAKE instantiation reads as the simpler of the two.
The single-block trick, and the bugs it invites
If SHA-2 needs this much care, why keep it? Speed and ubiquity. The SPHINCS+ specification explains that most of the running time of SLH-DSA, XMSS and LMS is spent in F, and borrows an idea from LMS: shape the inputs so that each F call costs one compression-function invocation. The public seed is zero-padded to a full 64-byte block for SHA-256, or 128 bytes for SHA-512, so the hash state after absorbing it can be computed once per key and cached. The address is compressed from 32 bytes to 22 by shortening the layer, type and tree fields, so that the address, a 16 or 32-byte input and the padding fit in the single remaining block.
The specification reports that this reduces the online work of F to a single compression call for the simple SHA-2 instances. For SHAKE the trick has no effect, because a single SHAKE256 permutation call already absorbs the whole input.
The trick is also a source of interoperability defects. The compressed address is used only by the SHA-2 sets; the SHAKE sets hash the full 32-byte address. The standard defines separate member functions for the compressed layout. An implementation that shares code between the two families and applies the wrong layout will produce valid-looking but non-interoperable signatures, and an implementation that reuses the cached seed state across keys will silently break the key separation the public seed provides. Both defects pass self-consistency tests and fail only against independent test vectors, which is a strong argument for conformance testing against an external reference before any key signs a release.
- SHA-2 sets: compressed 22-byte address, seed padded to one block, cached prefix state per key.
- SHAKE sets: full 32-byte address, no padding, no prefix caching needed.
- Category 3 and 5 SHA-2 sets: two hash cores, SHA-256 for F and PRF, SHA-512 for the rest.
- Test against external vectors: self-signed round trips cannot reveal a wrong address layout.
The family that was dropped, and what each survivor is trusted to do
The submission offered a third instantiation that the standard did not keep. SPHINCS+-Haraka used Haraka, a short-input hash built from AES rounds, and was the fastest option on processors with AES instructions. Its specification section is a useful counterpoint to the other two. The sponge construction it used for longer inputs had a capacity of only 256 bits, which the designers say allows at most security level 2, and because Haraka is built from AES it inherits AES's well-known timing risks in software, so the designers advised using it only where AES runs in hardware. FIPS 205 approves only SHA-2 and SHAKE, which removes both concerns from the standardised scheme.
For the two survivors, the SPHINCS+ designers make an argument that is worth reading as a statement of trust rather than of proof. The simple instances need the hash to behave like a random function, a property no standard defines or tests. The designers point out that cryptanalysts do not restrict themselves to textbook properties such as collision or preimage resistance; they search for any feasibly detectable non-random behaviour, and NIST's call for SHA-3 explicitly listed such behaviour as a reason to reject a design. On that view, a faster-than-generic attack on the properties SLH-DSA needs, for example finding an input whose output bits fall into a prescribed pattern, would itself be a landmark result against SHA-2 or SHA-3.
The two families earn that trust in slightly different ways. For SHAKE, the argument rests on the multi-year public competition behind Keccak, its large security margin against known attacks, and the fact that using it as a pseudorandom function with a partly secret input is uncontroversial. For SHA-2, it rests on long standardisation and intense scrutiny, and on the observation that the accepted analysis of HMAC-SHA-256 already assumes SHA-256 behaves pseudorandomly. Neither argument is a reduction to a hard problem; both are evidence from decades of attack effort, which is the honest foundation for every hash-based signature.
The practical upshot is that the instantiation question is not 'which hash is stronger' but 'which hash's structure fits the security level'. The category-5 episode shows that a sound hash can still be a poor fit if its internal width is too narrow for the targets the scheme exposes; the Haraka design shows the same lesson from the other direction, where a 256-bit capacity capped the level the instantiation could claim.
Choosing a family
With the history in view, the choice between SHA2 and SHAKE parameter sets comes down to four questions rather than one. First, which hash cores does the signing and verifying platform already have, in hardware or in validated software? Category 1 SHA-2 needs only SHA-256; categories 3 and 5 SHA-2 need both SHA-256 and SHA-512; SHAKE needs Keccak. A constrained verifier, such as boot code or a small agent runtime, may already contain one of these and not the others.
Second, how will the message be presented? FIPS 205 defines a pre-hash variant in which an application signs a digest of the content, and states that SHA-256 and SHAKE128 are appropriate as the pre-hash only with parameter sets claimed to be in category 1. A category-5 signature over a SHA-256 digest inherits SHA-256's 128-bit collision resistance, so pre-hash and parameter set must be chosen together. Third, what policy regime applies? The NSA's CNSA 2.0 frequently asked questions document of April 2024 states that SHA-3 and SHAKE are not approved as hash algorithms in CNSA and are allowed only where an approved algorithm's standard calls them internally; organisations aligning with that regime may prefer SHA-2 throughout, even though SLH-DSA itself is not part of CNSA. Fourth, how much uniformity do reviewers value? SHAKE uses one function and one address layout everywhere, which shrinks the surface for the defects described above.
There is no security ranking between the two families as standardised: both rely on the hash behaving like a random oracle for the simple instances, and both meet their claimed categories. The difference is in how much structure an implementer must get right, and in which external constraints a deployment inherits.
Why this matters for agents and model releases
Signing pipelines for models and agent tools rarely sign raw content. They sign manifests, attestations or digests, and those digests are very often SHA-256. The weakest hash anywhere in that chain bounds the assurance of the whole: a category-5 SLH-DSA signature over a manifest of SHA-256 file digests cannot offer more than SHA-256's collision resistance for the files the manifest names. That is acceptable for many uses, but it should be a decision rather than an accident, and FIPS 205's pre-hash rule is a useful reminder to make it explicitly.
Long-lived release roots deserve a second check. Any key generated with a round-three SPHINCS+ library at category 3 or 5 with SHA-256 is now known to be weaker than its label; any signatures made with it are also incompatible with FIPS 205 for the reasons recorded in Appendix A. A provenance system that keeps such signatures alive for compatibility keeps the weakness alive with them.
Finally, the episode is a model for how agents' own cryptographic dependencies should be reviewed. The flaw was not in the hash function, which remains sound, nor in the signature design, but in the fit between the two: an internal width that was adequate for one security level and too narrow for another. Library choices inside agent platforms deserve the same kind of fit review whenever a parameter is raised.
A word on the limits of this analysis. The roughly 40-bit figure and the attack outline come from the published abstract of the Perlner, Kelsey and Cooper paper and from FIPS 205's change notes; the explanation of why F and PRF were left unchanged is our own reading of the input formats, consistent with every change but not stated by NIST. The article makes no performance claims; relative speed depends on platforms and is best measured where it will run.
Key takeaways
- FIPS 205's SHAKE sets use SHAKE256 everywhere; its SHA-2 sets use SHA-256 at category 1 but SHA-512 for H, T, H_msg and PRF_msg at categories 3 and 5.
- SHA-256's 256-bit chaining value leaves no margin for multi-target attacks on internal states at category 5; SHAKE256's 512-bit capacity does.
- Antonov's 2022 observation and the Perlner, Kelsey and Cooper forgery cost the round-three category-5 SHA-256 sets about 40 bits of classical security.
- Our analysis: the four functions that moved are exactly those with an intermediate chaining value after the tweak; F and PRF finish in a single block.
- The SHA-2 speed trick, a padded seed block and a 22-byte compressed address, is also a source of interoperability defects that only external test vectors reveal.
- Choose the family by platform cores, pre-hash pairing (SHA-256 pre-hash only for category 1) and policy regime; neither family is more secure as standardised.
Practitioner Toolkit
Copy-paste, strictly defensive artifacts you can use today. Nothing here attacks a real system.
Run before adopting a library for long-lived release or registry keys.
- Category 3 and 5 SHA-2 sets use SHA-512 for H, T, H_msg (with MGF1-SHA-512) and HMAC-SHA-512 for PRF_msg; SHA-256 only for F and PRF.
- H_msg for SHA-2 sets feeds R and PK.seed into MGF1 as well as into the inner hash.
- SHA-2 sets use the 22-byte compressed address and a seed padded to a full block; SHAKE sets use the full 32-byte address.
- Cached seed-block hash states are per key and never shared across keys.
- Conformance is demonstrated against external NIST test vectors, not only by sign-then-verify round trips.
- Pre-hash use pairs SHA-256 or SHAKE128 only with category-1 parameter sets.
A design-review aid that flags any function whose domain separation is absorbed before the final compression call.
function audit(function_name, prefix_len, tweak_len, input_len, block_len, state_bits, target_bits):
# bytes hashed after the cached prefix block, including mandatory padding
tail = tweak_len + input_len + min_padding(block_len)
calls_after_tweak = ceil(tail / block_len)
if calls_after_tweak > 1 and state_bits < 2 * target_bits:
flag(function_name, 'post-tweak chaining value narrower than twice the target')
else:
ok(function_name)
audit('F', 64, 22, 32, 64, 256, 256) # ok: single block
audit('H', 64, 22, 64, 64, 256, 256) # flagged: why FIPS 205 uses SHA-512
audit('H', 128, 22, 64, 128, 512, 256) # ok with SHA-512A documentation stub making the weakest-hash decision explicit.
release_signing:
algorithm: SLH-DSA-SHAKE-256s
mode: pure # sign the manifest itself
manifest_file_digest: SHA3-512
rationale: no hash in the chain narrower than the signature category
alternative_if_sha2_platform:
algorithm: SLH-DSA-SHA2-256s
manifest_file_digest: SHA-512
prohibited:
- pre_hash_sha256_with_category_3_or_5
- round_three_sphincs_plus_sha256_keysGlossary
- Merkle-Damgard construction
- A hash design that processes fixed-size blocks in sequence, carrying a chaining value from one compression call to the next.
- Chaining value
- The internal state passed between compression calls in a Merkle-Damgard hash; 256 bits for SHA-256 and 512 bits for SHA-512.
- Sponge construction
- A hash design that absorbs input into a large state and squeezes output from it, with a hidden part called the capacity.
- Capacity
- The portion of a sponge's state never directly touched by input or output; 512 bits for SHAKE256, it bounds generic internal attacks.
- DM-SPR
- Distinct-function multi-target second-preimage resistance: no advantage from many targets each computed under a different tweak.
- MGF1
- A mask-generation function from the RSA standard that stretches a hash output to any length by hashing it with a counter.
- Compressed address (ADRSc)
- The 22-byte address layout used only by the SHA-2 sets so that a chain step fits in one compression call.
- Pre-hash signing
- Signing a digest of the content rather than the content itself; FIPS 205 encodes the hash function's identifier in the signed data.
- Long-message second-preimage attack
- A generic attack on Merkle-Damgard hashes that exploits the many intermediate states of a long input.
References
- NIST FIPS 205, Stateless Hash-Based Digital Signature Standard (2024), Section 11 and Appendix A
- SPHINCS+ Submission to the NIST post-quantum project, specification v3.1 (2022), Sections 7.2, 8.1.6, 9.5 and 9.6
- Perlner, Kelsey and Cooper, Breaking Category Five SPHINCS+ with SHA-256 (PQCrypto 2022)
- Kelsey and Schneier, Second Preimages on n-bit Hash Functions for Much Less than 2^n Work (EUROCRYPT 2005)
- NIST FIPS 180-4, Secure Hash Standard (SHA-256 and SHA-512)
- NIST FIPS 202, SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions
- RFC 8017, PKCS #1: RSA Cryptography Specifications Version 2.2 (MGF1)
- Bernstein et al., The SPHINCS+ Signature Framework (ACM CCS 2019)