Signature scheme
SLH-DSA explained mathematically
How SLH-DSA-SHA2-256f (FIPS 205) signs without any hard problem other than hash functions — from hash chains via Merkle trees to a forest of FORS trees.
Last updated: 2026-11-09
1. What this is about
At McGesund, customers on Reviews Premium and Signals Premium can choose the signature tier PQ-Dual-Diversified. Every QR code then carries two quantum-resistant signatures in addition to the conventional Ed25519 signature: ML-DSA-87 and SLH-DSA-SHA2-256f.
This article explains the second one. It is built differently from everything else we use.
SLH-DSA is not encryption. The content stays publicly readable. SLH-DSA proves origin and integrity — like any digital signature.
SLH-DSA stands for Stateless Hash-Based Digital Signature Algorithm. It was developed from SPHINCS+ and standardised in 2024 as FIPS 205. The name already gives away what is special about it: the scheme uses nothing but hash functions. No lattices, no elliptic curves, no large primes.
2. What exactly is signed?
At McGesund, SLH-DSA signs the same data that the Ed25519 signature in the QR code covers: the QR code’s payload. Among other things, it states which company the code belongs to (fid), what kind of code it is (typ), when it was issued (iat) and until when it is valid (exp) — as a compactly encoded byte string (CBOR).
This byte string is our message . If anyone later changes a single bit, none of the signatures match any more.
The SLH-DSA signature is not stored in the QR code itself. At around 49 KB it is far too large for that. It is kept as a separate “stamp” on the QR record in the McGesund backend and loaded into the browser when you click “Verify authenticity”.
3. Why a scheme built only from hash functions?
Every signature scheme rests on a task that is practically impossible without the secret key:
| Scheme | Security rests on |
|---|---|
| Ed25519 | discrete logarithm on an elliptic curve |
| FALCON | short vectors in NTRU lattices |
| ML-DSA | Module-LWE and Module-SIS (lattices) |
| SLH-DSA | properties of the hash function only |
A large quantum computer would break Ed25519. FALCON and ML-DSA are considered quantum-resistant — but both rely on lattice problems. Should a fundamental attack on lattices ever be found, both would be affected at once.
SLH-DSA, by contrast, only needs the hash function to do what it was built for:
This is the longest and most thoroughly studied assumption in all of cryptography. Even a quantum computer helps only to a limited extent: Grover’s algorithm halves the security bits. That is why the 256 variant uses hash values of bytes bits — after Grover, 128 bits still remain.
This is exactly why the tier is called Diversified: ML-DSA (lattices) and SLH-DSA (hashes) come from different families of mathematics. If one falls, the other still holds.
4. Building block 1: signing with hash chains (WOTS+)
The basic idea is surprisingly simple. We start with a single digit.
Take a secret random value and hash it again and again:
The end of the chain, , is published. It is the public key for this one digit.
To sign a digit between 0 and 15, you reveal the chain value at position :
Anyone who wants to verify hashes another times. If exactly comes out, the signature is genuine:
A tiny example
We sign the digit :
- The signer publishes .
- The verifier computes and compares it with . It matches.
A forger who has seen can easily compute , and so on from it — and thus sign larger digits. They cannot sign smaller ones, because that would require hashing backwards.
The checksum against “counting up”
To stop anyone from simply increasing digits, a checksum is signed as well:
If a forger increases a digit of the message, becomes smaller — and for that they would have to compute backwards in the checksum chain. Both directions are blocked.
Scaled up to a hash value
A 32-byte hash value has 64 digits in base 16. Add 3 checksum digits:
Such a signature is called WOTS+ (Winternitz One-Time Signature). Its big catch is in the name: one-time. Anyone who signs two different messages with the same key reveals so many chain values that forgeries become possible.
5. Building block 2: many one-time keys under one roof (Merkle tree)
If every key may be used only once, you need a great many of them — but you want to publish only one public key.
The solution is a Merkle tree: the public keys of WOTS+ key pairs form the leaves. Each pair of neighbours is hashed together, the results again in pairs, until a single root sits at the top.
In SLH-DSA-SHA2-256f, each tree has height , i.e. leaves.
To show that a particular leaf belongs to the tree, you provide its authentication path: the 4 sibling nodes on the way up. With them, the verifier hashes their way from the leaf to the root. If the known root comes out, the leaf belongs to the tree.
6. Building block 3: trees on top of trees (hypertree)
Sixteen one-time keys are of course not enough. So SLH-DSA stacks trees on top of each other: the root of a tree is signed with a WOTS+ key of the tree above it. This tower is called the hypertree.
SLH-DSA-SHA2-256f has layers, each of height 4:
At the very bottom, that gives
leaves. The root of the topmost tree is called — it is the actual public key.
Importantly, nobody computes these trees in advance. All secret values are derived on demand from a secret seed . Only four values of 32 bytes each are stored:
| Part | Content | Size |
|---|---|---|
| SK.seed | seed for all secret chain values | 32 B |
| SK.prf | seed for the randomiser | 32 B |
| PK.seed | public seed, separates key pairs from one another | 32 B |
| PK.root | root of the topmost tree | 32 B |
The private key consists of all four (128 bytes), the public key only of PK.seed and PK.root (64 bytes).
7. Building block 4: a forest against chance hits (FORS)
Now the real problem: how does the signer know which of the leaves is still unused?
Older hash-based schemes (XMSS, LMS) keep a counter for this. It must never be reset — not by a backup, not by a second server. If that happens anyway, a one-time key is used twice and the scheme is broken. Such schemes are called stateful.
SLH-DSA is stateless: the leaf is chosen pseudo-randomly from the message itself. Then two messages may hit the same leaf. That is why the bottom layer is not a one-time signature but a few-time signature: FORS (Forest of Random Subsets).
FORS consists of small Merkle trees, each with leaves. The leaves are secret random values.
To sign, the message hash is split into 35 numbers of 9 bits each. Each number selects exactly one leaf in “its” tree. The signature contains, for every tree:
- the secret value of that leaf
- its authentication path (9 nodes)
For a different message, a forger would have to hit leaves in all 35 trees whose secrets have already been revealed. Even if the same FORS instance has been used a few times, that is hopeless.
8. The whole signing process
- Randomiser. From , a fresh random number and the message, a 32-byte value is derived.
- Message hash.
The 49 bytes of the digest are split up: 40 bytes select the 35 FORS leaves, the rest determines which tree (
idx_tree) and which leaf (idx_leaf) at the bottom of the hypertree is used. - FORS signature over the message. It yields a FORS public key.
- Hypertree signature over this FORS key: 17 WOTS+ signatures, each with an authentication path, layer by layer up to .
The finished signature is simply the concatenation:
9. Why the signature is 49,856 bytes
With the parameters of SLH-DSA-SHA2-256f, the size can be computed exactly ( bytes per hash value):
| Part | Calculation | Bytes |
|---|---|---|
| 32 | ||
| FORS | 11,200 | |
| Hypertree | 38,624 | |
| Total | 49,856 |
For comparison: an Ed25519 signature is 64 bytes, ML-DSA-87 is 4,627 bytes. The size is the price for SLH-DSA getting by with nothing but hash functions.
10. How the browser verifies
With SLH-DSA, verifying means: hashing again.
- Recompute the digest from , the public key and the message.
- From the 35 revealed FORS leaves and their paths, compute the 35 tree roots and from them the FORS public key.
- With each of the 17 WOTS+ signatures, hash the chains to their ends, form the WOTS+ public key and hash up the path to the tree root.
- At the end, exactly must come out.
That is a few thousand hash calls — a trifle for a computer. In the browser, verification takes us about 6 to 12 milliseconds.
11. What happens if someone changes the data?
If even one bit of the message changes, the digest changes completely. The 35 numbers then point to different FORS leaves, and the scheme to a different place in the hypertree.
The revealed secrets no longer fit: the recomputed roots differ, and does not come out at the end. Verification fails.
To build a valid signature anyway, an attacker would need secrets that were never published — that is, compute a hash function backwards.
12. Why 256f in particular?
FIPS 205 defines twelve parameter sets: two hash families (SHA-2 and SHAKE), three security levels (128, 192, 256), and for each a fast (“f”) and a small (“s”) variant.
| Variant | Signature | Signing (our measurement) | Verifying |
|---|---|---|---|
| SHA2-256s | 29,792 B | approx. 2.6 s | approx. 3 ms |
| SHA2-256f | 49,856 B | approx. 0.26 s | approx. 6 ms |
256 means NIST security category 5 — the same level as FALCON-1024 and ML-DSA-87.
We chose “f” because companies can issue up to 500 QR codes in one go. With “s”, signing would take more than 20 minutes; with “f”, less than a minute. The signature is larger in return — since it does not live in the QR code, that is acceptable.
SHA-2 rather than SHAKE, because SHA-256 and SHA-512 are hardware-accelerated on practically every device.
13. Randomness when signing
FIPS 205 allows two ways of generating :
- deterministic: same message, same signature
- randomised (“hedged”): a fresh random number is mixed in
We use the randomised variant. Two signatures over the same message therefore look different — both are valid. This makes attacks harder that combine measurements on the device (power consumption, timing) with repeated signatures.
14. What McGesund does with it
The tier. PQ-Dual-Diversified is available on Reviews Premium and Signals Premium. A QR code then carries three signatures:
| Signature | Family | Location |
|---|---|---|
| Ed25519 | elliptic curve | in the QR code |
| ML-DSA-87 | lattices | stamp in the backend |
| SLH-DSA-SHA2-256f | hash functions | stamp in the backend |
Signing. An SLH-DSA signature costs about a quarter of a second of computing time. So that the server stays available to everyone else in the meantime, dedicated background threads do the work.
Verification. The reader’s browser loads both stamps and checks them with the same WASM module as all other PQ signatures. We supply the data; the computation happens on the visitor’s device.
The NIST check. Our module passes the official NIST test vectors for SLH-DSA-SHA2-256f: NIST’s seeds yield byte-for-byte the same keys, and all deliberately tampered signatures are rejected. You can check this yourself in the browser on the NIST test page.
The time anchor. As with all keys, the fingerprint of the SLH-DSA key is anchored in a Bitcoin block via OpenTimestamps.
15. SLH-DSA, ML-DSA and FALCON side by side
| Property | FALCON (FN-DSA) | ML-DSA-87 | SLH-DSA-SHA2-256f |
|---|---|---|---|
| Foundation | NTRU lattices | module lattices | hash functions only |
| Standard | FIPS 206 (in progress) | FIPS 204, final | FIPS 205, final |
| Public key | 1,793 B | 2,592 B | 64 B |
| Signature | 1,280 B | 4,627 B | 49,856 B |
| Signing | milliseconds | milliseconds | approx. 0.26 s |
| Verifying | milliseconds | milliseconds | milliseconds |
| Assumption | lattice problem | lattice problem | hash function |
In short: SLH-DSA is the slowest and largest of the three schemes — and the most conservative. It relies on nothing that every other signature does not already assume anyway: a secure hash function.
16. One sentence to take away
Whoever holds the secret seed signs in a quarter of a second. Whoever does not would have to compute SHA-256 or SHA-512 backwards — even with a quantum computer.
For the reader of a review, this means: even if all lattice schemes should fall one day, this signature remains verifiable. They do not have to believe us. They can do the maths.