HD Wallet Derivation

The hardest unsolved problem in post-quantum Bitcoin: can you recover all your keys from a single seed phrase? Short answer — not yet. Not in any cryptographically meaningful way.

Why HD Wallets Matter

BIP-32 hierarchical deterministic wallets are one of the most important usability innovations in Bitcoin’s history. They let you generate an unlimited number of addresses from a single 12- or 24-word seed phrase. Lose your hardware wallet, and you can recover every key, every address, every coin — from those words alone.

The mechanism relies on a specific algebraic property of elliptic curve cryptography: private keys are 256-bit integers, and you can derive child keys by adding another integer to the parent. The corresponding public key can be derived independently by adding the same value multiplied by the generator point. This linearity — the fact that scalar addition on the private key corresponds to point addition on the public key — is what makes non-hardened derivation possible.

Non-hardened derivation is what enables watch-only wallets: a server or auditor can hold only the extended public key, derive all child public keys to monitor incoming payments, and never possess the ability to spend. Hardware wallets, payment processors, exchanges, and custody solutions all depend on this property. Without it, every new address requires a round trip to the signing device.

What BTQ Has: The BIP-32 Skeleton

The BTQ codebase contains a BIP-32-shaped class hierarchy for Dilithium HD key derivation. The structure is familiar to anyone who has read Bitcoin Core’s key handling code:

DilithiumExtKey

Methods: SetSeed(), Derive(), Neuter()

Extended private key with chaincode and depth tracking

DilithiumExtPubKey

Methods: Derive()

Extended public key for watch-only derivation

The scaffolding is complete: depth counters, parent fingerprints, child indices, chaincodes, and separate paths for hardened (index ≥ 231) and non-hardened derivation. Both Derive() methods use HMAC-SHA512 to produce derivation material, exactly as BIP-32 specifies.

View source: src/crypto/dilithium_hd_key.h · Class hierarchy, btq-core v0.3.0-testnet

Why the Stub Breaks

The actual derivation logic — the part that transforms a parent key into a child key — is a placeholder. Both the private and public derivation paths use the same approach: XOR the first 32 bytes of HMAC output into the key material, and leave the remaining bytes unchanged.

// Non-hardened private key derivation (dilithium_hd_key.cpp:73-77)

for (size_t i = 0; i < std::min(key.size(), (size_t)32); ++i) {

new_key_data[i] ^= vout[i];

}

View source: dilithium_hd_key.cpp L73–77 · Private key derivation, btq-core v0.3.0-testnet

A Dilithium2 private key is 2,528 bytes. This code modifies only the first 32 — roughly 1.3% of the key material. The same pattern appears in public key derivation, where 32 bytes of a 1,312-byte public key are XORed:

// Non-hardened public key derivation (dilithium_hd_key.cpp:151-155)

for (size_t i = 0; i < std::min(pubkey.size(), (size_t)32); ++i) {

new_pubkey_data[i] ^= vout[i];

}

View source: dilithium_hd_key.cpp L151–155 · Public key derivation, btq-core v0.3.0-testnet

This produces three distinct failures:

  • Key pair inconsistency: The derived private key does not correspond to the derived public key. Signing with the child private key produces signatures that the child public key will not verify.
  • No security guarantees: XORing arbitrary bytes into a structured lattice key does not preserve any of the mathematical properties that make Dilithium secure. The resulting key may not even be a valid Dilithium key.
  • No derivation consistency: The same derivation applied independently to a private key and its corresponding public key produces unrelated results, breaking the fundamental property that makes watch-only wallets possible.

The code itself acknowledges this limitation. A comment in SetSeed() reads: “This is a simplified approach — in practice, Dilithium needs 1312 bytes.”

View source: dilithium_hd_key.cpp L93 · SetSeed comment, btq-core v0.3.0-testnet

Why Lattices Break Naive BIP-32

The stub in BTQ’s codebase is not simply incomplete engineering — it reflects two fundamental structural obstacles that prevent any straightforward translation of BIP-32 to the lattice setting.

Obstacle 1: Rounding

ML-DSA (standardized Dilithium) drops low-order bits from the public key to reduce its size. In the notation of the specification, the published key is t̂ = ⌊As + e⌉ rather than the raw value. Rounding is a lossy, non-linear operation — it does not distribute over addition. This makes non-hardened derivation structurally impossible for any scheme that rounds its public keys, because you cannot add a derivation offset to a rounded value and get a result that matches rounding the sum.

Obstacle 2: Noise Accumulation

Even without rounding, lattice public keys have the form t = As + e. Each derivation step adds fresh noise, widening the distribution and potentially making derived keys statistically distinguishable from freshly generated ones. This breaks unlinkability — an observer could tell that two addresses share a parent, which BIP-32 is specifically designed to prevent.

These are not engineering challenges to be solved with better code. They are mathematical properties of the ML-DSA construction itself. Any solution must either accept the limitations (hardened derivation only) or use a different signature scheme entirely.

Construction 1: ML-DSA (Hardened Only)

A 2026 paper by Deegan, Fitzwater, Gur, and Nugent provides the first formal treatment of post-quantum HD wallets with provable security under standard lattice assumptions (M-LWE, M-SIS). Their first construction uses NIST-standardized ML-DSA but accepts a significant limitation: all derivation requires the private key.

The approach is straightforward: use HMAC to derive a fresh seed from the parent’s extended private key, then feed that seed into ML-DSA’s deterministic DetKeyGen to produce a complete child key pair. Because the public key is never involved in the derivation input, CKDerpub (child key derivation from public key) cannot be defined.

// Construction 1: Hardened derivation (ML-DSA)

Setup: sd, cc ← {0,1}^λ; (sk, pk) := SIG.DetKeyGen(seed)

CKDer: (sd', cc') := HMAC(xpriv, cc, t)

(sk', pk') := SIG.DetKeyGen(sd')

This is what BTQ’s stub should be doing for hardened derivation: instead of XORing 32 bytes into an existing key, it should use the HMAC output as a complete seed for DetKeyGen, producing a fresh, valid ML-DSA key pair. The security proof bounds the unlinkability advantage by qhmac·(qpk+1)/2λ + εML-DSA.

The tradeoff: seed-based recovery works (you can regenerate every key from the master seed), but watch-only wallets do not. Every address derivation requires access to the private key, eliminating one of BIP-32’s most important features.

Construction 2: Raccoon-G (Non-Hardened)

The paper’s primary contribution is a second construction that recovers full non-hardened derivation — including watch-only wallets — but requires a different signature scheme: Raccoon-G, a variant of the Raccoon threshold signature scheme that replaces sums-of-uniform secrets with discrete Gaussian secrets.

Two key modifications make this possible. First, the full unrounded public key t = As + e is published (not the rounded ). Rounding to is performed inline during Sign and Verify only, preserving linearity for derivation. Second, the scheme introduces two rerandomization functions:

// Construction 2: Non-hardened derivation (Raccoon-G)

RandPK(t, ρ): (s', e') ← D^ℓ_σt × D^k_σt via Sam(ρ)

return t' = As' + e' + t

RandSK(s, ρ): return s + s'

// Child derivation from public key alone:

CKDerpub: (ω, cc') := HMAC(pk, cc, t)

pk' := SIG.RandPK(pk, ω)

The critical insight is Gaussian stability: by the Micciancio–Peikert lemma, the sum of independent Gaussians of width σ is statistically close to a Gaussian of width σ√n at derivation depth n. This means derived keys remain in the same distributional family as freshly generated ones, enabling provable unlinkability — an observer cannot tell that two child keys share a parent.

The security proof uses a five-hybrid argument showing that depth-n signatures are indistinguishable from depth-1 signatures, relying on H-MLWE (Hint Module Learning With Errors) hardness. Unforgeability is proved under UF-CMA-HRK (unforgeability under honestly rerandomized keys).

Bounded derivation depth: Unlike classical BIP-32 (where non-hardened derivation depth is unlimited), noise accumulation imposes a maximum tree depth fixed at key generation time. In practice this matches BIP-44’s deployment pattern: hardened derivation for the first three levels (purpose, coin type, account) resets noise, and non-hardened derivation at the final two levels (change, address index) branches from those hardened roots.

Chain code caveat: The same warning applies as in classical BIP-32 — if an attacker obtains a child’s secret key together with the chain code, they can recover the parent secret key for all siblings in the non-hardened subtree. Hardened nodes eliminate this by deriving the offset from the parent private key.

The Size Cost

Raccoon-G recovers the algebraic properties needed for non-hardened derivation, but at a dramatic cost in key and signature sizes. The unrounded public key and Gaussian secret distribution both require substantially more storage than ML-DSA:

Private key

BIP-32: 32 B
ML-DSA: 2,560 B
Raccoon-G: 16,128 B

Public key

BIP-32: 33 B
ML-DSA: 1,312 B
Raccoon-G: 16,144 B

Signature

BIP-32: 64 B
ML-DSA: 2,420 B
Raccoon-G: 20,768 B

Raccoon-G signatures are nearly 9x larger than ML-DSA and over 320x larger than classical ECDSA. A single Raccoon-G transaction would consume approximately 20 KB for the signature alone — compared to the roughly 250-byte total size of a standard Bitcoin transaction today.

Secret distribution

ML-DSA: Bounded uniform
Raccoon-G: Discrete Gaussian

Public key structure

ML-DSA: Rounded (lossy)
Raccoon-G: Full (linear)

Non-hardened derivation

ML-DSA: Not possible
Raccoon-G: Supported via Gaussian stability

Signing architecture

ML-DSA: Rejection sampling
Raccoon-G: Fiat-Shamir without aborts

NIST status

ML-DSA: Standardized (FIPS 204)
Raccoon-G: Not on NIST track

Hardware implementation

ML-DSA: Straightforward
Raccoon-G: Challenging (Gaussian sampling)

This is the fundamental tradeoff: non-hardened derivation (and thus watch-only wallets) is possible in the post-quantum setting, but only with a signature scheme that produces keys and signatures an order of magnitude larger than even ML-DSA’s already-large outputs.

The Gap Between Paper and Code

The following summarizes what the research literature describes versus what exists in the BTQ v0.3.0-testnet codebase:

Hardened derivation with unlinkability and unforgeability proofs

Research: Provably secure via DetKeyGen (Construction 1)
BTQ v0.3: Stub only — XOR on 32 of 2,528 bytes

Non-hardened public key derivation (watch-only wallets)

Research: Raccoon-G with Gaussian linearity (Construction 2)
BTQ v0.3: Not implemented — XOR produces invalid keys

Raccoon-G signature scheme

Research: Introduced, formally analyzed, reference implementation in Rust
BTQ v0.3: Not present in codebase

Watch-only wallet via public derivation

Research: Provably secure under M-LWE, M-SIS, H-MLWE
BTQ v0.3: Broken — derived pubkey does not match derived privkey

Rerandomizable key pairs from fixed randomness

Research: RandPK/RandSK construction with proofs
BTQ v0.3: Absent

The project has the BIP-32 scaffolding — depth, fingerprint, chaincode, the hardened/non-hardened flag — but the cryptographic core that would make any of it functional is absent. The paper’s primary contribution (provably-secure non-hardened post-quantum public key derivation via Raccoon-G) would need to be implemented from scratch. A reference implementation exists in Rust with a Python bridge to the Raccoon-G reference code, but it is proof-of-concept only — unaudited and not intended for production.

Deegan, Fitzwater, Gur, Nugent. “Lattice HD Wallets: Post-Quantum BIP32 Hierarchical Deterministic Wallets from Lattice Assumptions.” ePrint 2026/380.

Reference implementation: github.com/p-11/lattice-hd-wallets (commit 461a5ed) · Project Eleven, proof-of-concept

What This Means in Practice

For users of a Dilithium-based Bitcoin fork today, the practical consequence is significant: you cannot rely on a seed phrase to recover your wallet. Each Dilithium key pair must be backed up individually, and the wallet file itself becomes the critical backup artifact rather than a mnemonic phrase.

The path forward involves hard choices:

  • Construction 1 (hardened-only) is implementable today using NIST-standardized ML-DSA. It restores seed-based recovery by using DetKeyGen to derive complete key pairs from HMAC-expanded seeds. Watch-only wallets are sacrificed. This is what BTQ’s stub should be replaced with as a minimum viable improvement.
  • Construction 2 (Raccoon-G) recovers full BIP-32 functionality including watch-only wallets, but requires adopting an unstandardized signature scheme with 20 KB signatures and 16 KB keys. No production implementation exists. Hardware wallet support would be challenging due to Gaussian sampling requirements.
  • Hybrid approaches could use classical BIP-32 derived seeds at each path as input to Dilithium key generation. This preserves seed recovery and uses standardized algorithms, but does not enable public-key-only derivation.

This is arguably the hardest unsolved problem in post-quantum Bitcoin engineering. The signature swap, the block size expansion, the address format changes — those are large engineering tasks, but they have clear solutions. HD wallet derivation for lattice signatures remains an open research problem with no production-ready answer. The Deegan et al. paper demonstrates that the problem is solvable in principle — but the solution demands either accepting reduced functionality or adopting cryptographic primitives that have not yet been vetted by the standards process.

Latest Dispatches