Understanding SPHINCS+

A visual walkthrough of how hash-based post-quantum signatures work and why Quantroot uses them to protect Bitcoin.

What is SPHINCS+?

SPHINCS+ (standardized as SLH-DSA in NIST FIPS 205) is a post-quantum digital signature scheme. It replaces the elliptic-curve math behind ECDSA and Schnorr with nothing but hash functions — the same primitives you already know from SHA-256 and block explorers. That substitution is what makes it safe against quantum computers.

At the highest level, SPHINCS+ is built from a handful of cryptographic pieces stacked on top of each other. You will see these names repeatedly:

WOTS+ One-time signature made from hash chains. Signs a single message.
Merkle tree Binary tree of hashes that commits to many values under one root.
XMSS A Merkle tree of WOTS+ keys — multi-time signing, but stateful.
FORS A "few-time" signature over a forest of small Merkle trees. Signs the message hash.
Hypertree A tree of XMSS trees that lets one small public key authenticate billions of signatures.

Prerequisites

This guide assumes passing familiarity with a few cryptographic primitives. If you'd like a refresher, the links below go to general-purpose introductions:

Digital signatures

A piece of math that proves "I, holder of this private key, authorized this exact message."

Wikipedia →
Hash functions

One-way fingerprints over arbitrary data, like SHA-256.

Wikipedia →
Merkle trees

Binary hash trees that commit to many values under a single root. We'll revisit these below.

Wikipedia →

Two things worth keeping in mind as you read on:

  • Hash functions are one-way. Computing H(x) from x is trivial; going backwards is infeasible. That asymmetry is the only hard problem SPHINCS+ depends on.
  • In Bitcoin, a broken signature scheme means an attacker can spend coins from any address whose public key has been revealed on-chain — i.e., any address that has been spent from. Post-quantum signatures exist to keep that window closed.

Why Hash-Based Signatures?

Bitcoin's current signatures — ECDSA and Schnorr — rest on the Elliptic Curve Discrete Logarithm Problem (ECDLP). A private key is a random 256-bit integer d. The public key is a point P = d·G on the secp256k1 curve. Computing P from d is fast; recovering d from P takes roughly 2128 operations classically — effectively infinite.

ECDSA / Schnorr
private: d
public:  P = d·G

Forward is easy; reverse (ECDLP) is hard classically but broken on a sufficiently large quantum computer via Shor's algorithm.

SPHINCS+
private: s
public:  H(H(...H(s)))

Forward is easy; reverse is infeasible both classically and on a quantum computer.

In 1994 Peter Shor published a quantum algorithm that solves discrete-log (and integer factoring) in polynomial time. A large, fault-tolerant quantum computer running Shor's could recover d from P in hours. This would break ECDSA, Schnorr, RSA, and every other scheme whose security reduces to discrete-log or factoring. Nobody has built such a machine yet, but cryptographic standards move slowly, and coins safe today need to stay safe in a decade.

Hash functions don't have the algebraic structure Shor's exploits. The best known quantum attack against a hash function is Grover's algorithm, which provides only a quadratic speedup (2n → 2n/2). Easy to absorb by using a larger hash output — a 256-bit hash still provides 128-bit security against a quantum attacker, the same level Bitcoin's Schnorr already targets.

Build a signature scheme whose only hard problem is "invert a hash function," and you get post-quantum security for free.

Building Block 1: WOTS+ (Hash Chains)

The Winternitz One-Time Signature (WOTS+) is the first primitive we'll build from scratch. The core idea is a hash chain: pick a random secret s and hash it w times in a row. The end of the chain is the public key. To sign a digit d, reveal the intermediate hash Hd(s); the verifier applies H an additional w − d times and checks that the result matches the public key.

Interactive: Click a position to "sign" that value
s
secret
1
2
3
H⁴
4
H⁵
5
H⁶
6
pk
public
Click any position to see how signing works

Security comes from hash-function asymmetry. The verifier can trivially hash forward; a forger trying to sign a lower digit would need to run H backwards, which is exactly what hash functions are built to prevent. But an attacker who sees the signature can hash forward and claim a higher digit. WOTS+ closes this hole with a separate checksum chain whose digits move in the opposite direction — any forward move on a message chain forces a backward move on the checksum chain, and forging in both directions at once is infeasible.

One chain signs a single log2(w)-bit digit; a full message hash needs several chains in parallel plus the checksum chain. The Winternitz parameter w trades signature size against hashing work: higher w gives shorter signatures but more hashing. Quantroot uses w = 256, the maximum FIPS 205 allows.

The catch: one WOTS+ key signs exactly one message. Signing a second message exposes two intermediate hashes in the same chain, letting an attacker hash either forward to forge signatures on many other messages. WOTS+ is one-time by construction, which is why we need the next building block.

Building Block 2: Merkle Trees

XMSS is just "put WOTS+ keys at the leaves of a Merkle tree," so it's worth spelling out exactly which property we'll use. A Merkle tree is a binary tree where each internal node is the hash of its two children concatenated. The single root hash commits to every value at the leaves, and any individual leaf can be proven in the tree with log₂(n) sibling hashes — the authentication path.

Root = H(H01 ‖ H23)
/       \
H01 = H(H0‖H1)     H23 = H(H2‖H3)
/   \         /   \
H0     H1     H2     H3
|     |      |     |
v₀     v₁     v₂     v₃

To prove that a specific leaf is in the tree, you provide the authentication path: the sibling hashes at each level on the way from the leaf to the root. The verifier walks up the path, recomputes the root, and checks it matches the published one. A million leaves costs twenty hashes — that logarithmic scaling is why Merkle trees show up in Bitcoin blocks, Git objects, and certificate transparency logs.

Building Block 3: XMSS

XMSS — the eXtended Merkle Signature Scheme — is exactly what the name suggests: a Merkle tree whose leaves are WOTS+ public keys. The XMSS public key is the Merkle root. To sign message i, use WOTS+ keypair i and attach the authentication path from leaf i to the root. A tree of height h gives 2h one-time keypairs — one-time signatures turned into many-time signatures.

Interactive: Click a leaf to see its authentication path
Root H01 H23 W₀ W₁ W₂ W₃ WOTS+ 0 WOTS+ 1 WOTS+ 2 WOTS+ 3
Click a leaf to highlight its authentication path

Every leaf is still a WOTS+ key, and WOTS+ is still one-time. Which brings us to the problem the next section is about.

The State Problem

XMSS requires the signer to remember which leaves have already been used. In practice, that means maintaining a counter: "I've signed k messages, the next signature uses leaf k, then increment to k+1." That counter is called state, and keeping it correct is suddenly part of the signer's job.

State is poison for Bitcoin wallets. Consider how a Bitcoin private key actually gets used:

Three ways state fails
1
Restore from seed. The mnemonic can regenerate the private key, but it cannot regenerate "which leaves did I already use." Your first post-restore signature might reuse leaf 5 — the same leaf you used last week. Silent break.
2
Two devices, one key. Laptop and phone both hold the same seed. Both think the next free leaf is 7. Each signs a different transaction with leaf 7. An observer who sees both signatures can forge a third.
3
Backup restored. You restore a backup taken a week ago. The counter rewinds. Every signature you make today reuses a leaf you used since the backup was taken. Undetectable compromise.

Worst of all, none of these failures are visible. A signature made with a reused leaf is structurally valid and every wallet will accept it. The compromise only becomes apparent once an attacker publishes a forgery — and by then it's too late. This is why "just use XMSS" was never acceptable for Bitcoin.

SPHINCS+: Stateless via PRF

The insight that makes SPHINCS+ work: instead of remembering which leaf you've used, derive the leaf from the message itself. SPHINCS+ uses a pseudorandom function (PRF) keyed by a secret — a function whose output looks random without the key but is perfectly deterministic with it. Same inputs always produce the same output.

XMSS (Stateful)
counter = load_from_disk()
leaf = counter
sign(message, leaf)
counter += 1
save_to_disk(counter)

If the counter is lost, corrupted, or reused across devices, security is silently broken.

SPHINCS+ (Stateless)
leaf = PRF(secret, message)
sign(message, leaf)
// no state to save

The message deterministically selects the leaf. Same message = same leaf = same signature (safe). Different message = different leaf (safe).

Every failure mode from the previous section evaporates. Same seed reproduces the same PRF and picks the same leaves. Two devices with the same key land on the same leaf for the same message. Restored backups don't affect leaf selection. There is simply nothing for the signer to track.

Deterministic leaf selection has one cost: if two messages collide on the same leaf, you get WOTS+-style reuse. To keep that collision probability astronomically small, SPHINCS+ needs a huge number of possible leaves — Quantroot targets 232, roughly four billion. A single XMSS tree that large is impractical to build: generating the root alone requires hashing every one of its four billion leaves. The fix is to compose the tree out of smaller trees.

The Hypertree

SPHINCS+ stacks d smaller XMSS trees into a hypertree — a tree of trees — with a dedicated few-time signer at the very bottom. Each XMSS tree has small height h/d, so its root is cheap to compute. The "signature" at each layer above 0 is really a WOTS+ signature on the root of the tree directly below. At the very bottom, each leaf of the lowest XMSS tree points to a fresh instance of a special few-time scheme that actually signs the message hash — the next section covers that scheme.

Layer 3
(top)
XMSS Tree
Root = Public Key
Layer 2
XMSS
XMSS
Layer 1
XMSS
XMSS
XMSS
XMSS
Layer 0
X
X
X
X
X
X
X
X
Few-time
(bottom)
T0
T1
T2
T3
T4
T5
T6
T7
T8
T9
↑ signs the message

Signing walks the full chain — PRF picks a bottom-layer instance, the few-time scheme signs the message hash, and each layer's auth path threads up to the SPHINCS+ root. Verification runs the chain in reverse. The 232 leaf capacity is spread across d=4 layers, so no single tree is ever bigger than 28 = 256 leaves. Signing and verification each touch one small tree per layer, not one giant tree.

Building Block 4: FORS

FORS — Forest Of Random Subsets — is the few-time scheme at the bottom of the hypertree. Unlike WOTS+, it signs the full message hash directly, and it tolerates a small number of reuses before collapsing. A FORS key is k small Merkle trees, each of height a. Each leaf is a secret value. The FORS public key is the hash of all k tree roots concatenated.

Tree 0
R₀
/ \
.   .
[★]   .
reveal leaf i₀
Tree 1
R₁
/ \
.   .
.   [★]
reveal leaf i₁
Tree k-1
Rk-1
/ \
.   .
[★]   .
reveal leaf ik-1

To sign a message hash, split it into k indices, one per tree, and reveal one leaf per tree plus the authentication path from that leaf up to its tree's root.

Why few-time?

If two messages overlap in some but not all trees, an attacker can mix revealed leaves from both signatures and forge a third message. The probability of overlap grows quickly with the number of messages signed under one FORS key, so a single FORS instance is only safe for a handful of signatures.

SPHINCS+ sidesteps this by using a fresh FORS key per signature. The PRF picks a different bottom-XMSS leaf for almost every message, and each leaf points to its own FORS instance. The few-time safety budget resets every time.

Bitcoin's SPHINCS+ Parameters

Quantroot tunes SPHINCS+ for Bitcoin's constraints. Bigger isn't always better — we care about signature size, verification speed, and how many signatures a single key can safely make.

Param Value Meaning
n 16 Hash output size in bytes — security parameter
h 32 Total hypertree height (232 possible signatures)
d 4 Number of XMSS layers (8 levels per layer)
w 256 Winternitz parameter — maximum, shortest WOTS+ sig
k 10 Number of FORS trees
a 14 FORS tree height (214 = 16,384 leaves per tree)
32 B
Public key
64 B
Secret key
4,080 B
Signature

Inside a 4,080-Byte Signature

Randomizer (16 B)
FORS signature (2,400 B)
XMSS Layer 0 (416 B)
XMSS Layer 1 (416 B)
XMSS Layer 2 (416 B)
XMSS Layer 3 (416 B)
64 B
Schnorr signature
4,080 B
SPHINCS+ signature (64× larger)

That 64× size increase is the price of building a signature out of nothing but hashing.

How Quantroot Uses SPHINCS+

SPHINCS+ is not used alone in Bitcoin. Quantroot's soft fork pairs it with Schnorr in a hybrid Tapscript leaf:

<sphincs_pk> OP_CHECKSPHINCSVERIFY OP_DROP <schnorr_pk> OP_CHECKSIG

The 4,080-byte SPHINCS+ signature is carried in the Taproot annex — a dedicated witness field — rather than on the script stack, which has a 520-byte element limit.

Before the soft fork activates, OP_CHECKSPHINCSVERIFY is a no-op: the script still passes or fails on the Schnorr check. That keeps quantum-insured outputs spendable on today's mainnet and lets wallets opt in now. After activation, the SPHINCS+ check becomes mandatory and the output is protected against both classical and quantum key recovery.