This repository contains the specification for the Hybrid Hierarchical Deterministic (HHD) Wallet, a deterministic wallet system that enables coexistence of both classical (ECDSA secp256k1) and post-quantum (Falcon-512, ML-DSA-44, ML-DSA-65, ML-DSA-87) signature schemes. The HHD Wallet specification defines a hierarchical key derivation mechanism that allows a single BIP-39 mnemonic to deterministically generate and manage keypair hierarchies for multiple signature schemes without requiring independent mnemonic seeds. This design ensures cryptographic separation between different signature schemes while maintaining a unified wallet structure.
- ECDSA secp256k1: Classical elliptic curve signature scheme
- Falcon-512: Post-quantum signature scheme based on lattice cryptography
- ML-DSA-44/65/87: Post-quantum ML-DSA signature scheme (NIST Level 1/3/5)
For the complete technical specification, please refer to SPECIFICATION.md.
For a detailed blog post explaining the specification, design decisions, and security considerations, see: Hybrid Hierarchical Deterministic Wallets.
The HHD Wallet specification is built upon the following industry standards:
- BIP-39: Mnemonic code for generating deterministic keys
- BIP-32: Hierarchical Deterministic Wallets
- BIP-44: Multi-Account Hierarchy for Deterministic Wallets
- BIP-85: Deterministic Entropy From BIP32 Keychains
- SLIP-0010: Universal private key derivation from master private key
Tectonic team has proposed a PR to satoshilabs SLIPS to integrate ML-DSA into SLIP-0010 standard. You can check the updated proposed spec and the reference test vector code for more information.
The HHD Wallet uses a three-tier derivation model:
- Master Mnemonic (BIP-39): A single 24-word mnemonic phrase serves as the root entropy source
- Scheme-Specific Seeds (BIP-85): Each signature scheme receives its own 64-byte seed derived from the master mnemonic
- Keypairs (BIP-32/SLIP-0010): Individual keypairs are derived from scheme seeds using address indices
Each signature scheme gets its own unique seed through BIP-85 derivation from the master mnemonic:
- ECDSA secp256k1:
m/83696968'/83286642'/1' - Falcon-512:
m/83696968'/83286642'/2' - ML-DSA-44:
m/83696968'/83286642'/4' - ML-DSA-65:
m/83696968'/83286642'/5' - ML-DSA-87:
m/83696968'/83286642'/6'
The base path m/83696968' is the standard BIP-85 path, /83286642' stands for Tectonic in a T9 keypad, and the final component identifies the signature scheme.
Once a scheme-specific seed is obtained, individual keypairs are derived using address indices:
ECDSA secp256k1 (BIP-32, BIP-44):
- Domain separator:
Bitcoin seed - Base path:
m/44'/60'/0'/0 - Full path:
m/44'/60'/0'/0/{address_index} - Standard: BIP-32 (non-hardened address index)
Falcon-512 (SLIP-0010, hardened):
- Domain separator:
Falcon-512 seed - Base path:
m/44'/60'/0'/0' - Full path:
m/44'/60'/0'/0'/{address_index}' - Standard: SLIP-0010 (all components hardened)
ML-DSA variants (SLIP-0010, hardened):
- Domain separator:
ML-DSA-{variant} seed(e.g.,ML-DSA-44 seed) - Base path:
m/44'/60'/0'/0' - Full path:
m/44'/60'/0'/0'/{address_index}' - Standard: SLIP-0010 (all components hardened)
The reference implementation is provided by Bedrock, a production-ready Rust library implementing the HHD Wallet specification.
Add Bedrock to your Cargo.toml:
[dependencies]
tectonic-bedrock = { git = "https://github.com/tectonic-labs/bedrock", branch = "main" }Or use through crates.io:
cargo add tectonic-bedrock
use tectonic_bedrock::hhd::{HHDWallet, SignatureScheme};
// Create a new wallet with ECDSA and Falcon support
let wallet = HHDWallet::new(
vec![SignatureScheme::EcdsaSecp256k1, SignatureScheme::Falcon512],
None, // Optional BIP-39 passphrase
).unwrap();
// Derive a keypair for ECDSA at address index 0
let (ecdsa_sk, ecdsa_vk) = wallet.derive_ecdsa_secp256k1_keypair(0).unwrap();
// Derive a keypair for Falcon at address index 0
let (falcon_sk, falcon_vk) = wallet.derive_fn_dsa512_keypair(0).unwrap();use tectonic_bedrock::hhd::{HHDWallet, SignatureScheme};
use bip39::Mnemonic;
// Import from existing 24-word mnemonic
let mnemonic = Mnemonic::from_phrase(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
bip39::Language::English,
).unwrap();
let wallet = HHDWallet::new_from_mnemonic(
mnemonic,
vec![
SignatureScheme::EcdsaSecp256k1,
SignatureScheme::Falcon512,
SignatureScheme::MlDsa44,
SignatureScheme::MlDsa65,
SignatureScheme::MlDsa87,
],
None, // Optional BIP-39 passphrase
).unwrap();
// Derive keypairs for different schemes at the same address index
let (ecdsa_sk, ecdsa_vk) = wallet.derive_ecdsa_secp256k1_keypair(0).unwrap();
let (falcon_sk, falcon_vk) = wallet.derive_fn_dsa512_keypair(0).unwrap();
let (mldsa44_sk, mldsa44_vk) = wallet.derive_mldsa44_keypair(0).unwrap();// Create wallet with BIP-39 passphrase for additional security
let wallet = HHDWallet::new(
vec![SignatureScheme::EcdsaSecp256k1, SignatureScheme::Falcon512],
Some("my-secure-passphrase".to_string()),
).unwrap();// Derive multiple addresses for the same scheme
for i in 0..5 {
let (sk, vk) = wallet.derive_ecdsa_secp256k1_keypair(i).unwrap();
println!("Address {}: {:?}", i, vk);
}For more detailed API documentation and advanced usage, see the Bedrock repository.
- The specification requires a minimum of 24-word BIP-39 mnemonics to ensure 256 bits of entropy
- Each signature scheme uses cryptographically independent seeds derived via BIP-85
- The derivation process follows established standards (BIP-32, BIP-85, SLIP-0010) to ensure security properties
- Post-quantum schemes require hardened derivation paths due to the lack of rerandomization techniques
This specification is provided as-is for reference and implementation purposes.
