@tectonic-labs/hybrid-hd-wallet
v0.1.0-rc.1
Published
Hybrid HD wallet with ECDSA and Falcon-512 signatures from single mnemonic (Rust + WASM)
Maintainers
Readme
Hybrid HD Wallet
A hybrid hierarchical deterministic wallet supporting both classical (ECDSA secp256k1) and post-quantum (Falcon-512) signature schemes from a single BIP-39 mnemonic.
Built with Rust and compiled to WebAssembly for maximum performance and security.
Features
- Multi-Scheme Support: Derive keys for multiple signature schemes (ECDSA secp256k1, Falcon-512)
- Single Mnemonic: Use one BIP-39 mnemonic to derive all scheme-specific seeds
- BIP-85 Derivation: Derive scheme-specific seeds using the BIP-85 standard
- Deterministic: All keys are deterministically derived from the master seed
- Cryptographic Separation: Each signature scheme uses independent derivation paths
- WebAssembly: Works in Node.js, browsers, and modern bundlers (Vite, Webpack, Rollup)
- Type-Safe: Full TypeScript definitions included
Installation
npm install @tectonic-labs/hybrid-hd-wallet
# or
yarn add @tectonic-labs/hybrid-hd-wallet
# or
pnpm add @tectonic-labs/hybrid-hd-walletQuick Start
Node.js
import { HHDWallet, SignatureScheme } from "@tectonic-labs/hybrid-hd-wallet";
// Create wallet with both signature schemes
const wallet = new HHDWallet(
[SignatureScheme.EcdsaSecp256k1, SignatureScheme.Falcon512],
null // Optional BIP-39 passphrase
);
// Get mnemonic phrase (24 words)
const mnemonic = wallet.mnemonic();
console.log("Mnemonic:", mnemonic);
// Derive keypair for ECDSA at address index 0
const ecdsaKeypair = wallet.deriveKeypairForScheme(
SignatureScheme.EcdsaSecp256k1,
0
);
console.log("ECDSA Public Key:", ecdsaKeypair.publicKey);
console.log("ECDSA Private Key:", ecdsaKeypair.privateKey);
// Sign with ECDSA
const message = "Hello, Web3!";
const signature = wallet.signWithScheme(
SignatureScheme.EcdsaSecp256k1,
0, // address index
message
);
// Verify signature
const isValid = wallet.verifyWithScheme(
SignatureScheme.EcdsaSecp256k1,
0,
message,
signature
);
console.log("Valid:", isValid); // true
// Clean up WASM resources when done
ecdsaKeypair.free();
wallet.free();Browser with Vite
Step 1: Install the Vite WASM plugin:
npm install vite-plugin-wasmStep 2: Configure your vite.config.ts:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import wasm from "vite-plugin-wasm";
export default defineConfig({
plugins: [react(), wasm()],
});Step 3: Initialize WASM and use the library:
import init, {
HHDWallet,
SignatureScheme,
} from "@tectonic-labs/hybrid-hd-wallet";
// IMPORTANT: Call init() before using any WASM functionality
// The vite-plugin-wasm helps load .wasm files, but doesn't auto-initialize
await init();
// Now create wallet
const wallet = new HHDWallet(
[SignatureScheme.EcdsaSecp256k1, SignatureScheme.Falcon512],
null
);
// Derive keypair
const keypair = wallet.deriveKeypairForScheme(
SignatureScheme.EcdsaSecp256k1,
0
);
console.log("Public key:", keypair.publicKey);
// Don't forget to clean up!
keypair.free();
wallet.free();Note: For Web Workers or other browser environments, you can also manually load the WASM:
import init from "@tectonic-labs/hybrid-hd-wallet";
// Fetch and initialize manually
const wasmUrl = new URL(
"@tectonic-labs/hybrid-hd-wallet/dist/web/hybrid_hd_wallet_bg.wasm",
import.meta.url
);
const response = await fetch(wasmUrl);
const wasmBytes = await response.arrayBuffer();
await init(wasmBytes);Environment Support
Node.js
- Node.js 18+ (ESM and CommonJS)
- Auto-detects and uses Node.js-optimized build
- WASM auto-initializes - no setup needed
Browser (with bundlers)
- Vite - Requires
vite-plugin-wasmplugin (see example above) - Webpack 5+ - Auto-handles WASM (may need config for Webpack 4)
- Rollup - Works with @rollup/plugin-wasm
- Next.js - Works with next.config.js wasm config
- Important: Call
await init()before using the library in browser environments
Browser Support
- Chrome/Edge 91+
- Firefox 89+
- Safari 15+
- Requires WebAssembly support
API Reference
WASM Initialization
init()
init(module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>Initializes the WASM module. Required for browser/web environments before using any other functions. Node.js auto-initializes, so this is a no-op there.
Example:
import init, { HHDWallet, SignatureScheme } from "@tectonic-labs/hybrid-hd-wallet";
// Browser: Initialize once before using the library
await init();
// Now you can use the library
const wallet = new HHDWallet([SignatureScheme.EcdsaSecp256k1], null);HHDWallet
Constructor
new HHDWallet(schemes: number[], passphrase: string | null)Creates a new wallet with a random mnemonic.
schemes: Array of signature schemes to enable (e.g.,[SignatureScheme.EcdsaSecp256k1])passphrase: Optional BIP-39 passphrase for additional security
Static Methods
HHDWallet.newFromMnemonic(
mnemonic: string,
schemes: number[],
passphrase: string | null
): HHDWalletCreates a wallet from an existing BIP-39 mnemonic phrase (24 words).
Instance Methods
mnemonic(): stringReturns the wallet's BIP-39 mnemonic phrase (24 words).
deriveKeypairForScheme(scheme: SignatureScheme, addressIndex: number): KeypairDerives a keypair for the specified scheme and address index. Returns a Keypair object with publicKey and privateKey as Uint8Array.
Important: Call keypair.free() when done to release WASM memory.
signWithScheme(
scheme: SignatureScheme,
addressIndex: number,
message: string
): stringSigns a message with the specified scheme and address index. Returns hex-encoded signature.
verifyWithScheme(
scheme: SignatureScheme,
addressIndex: number,
message: string,
signature: string
): booleanVerifies a signature. Returns true if valid.
signWithAllSchemes(addressIndex: number, message: string): Map<number, string>Signs with all enabled schemes. Returns a map of scheme ID to signature.
verifyWithAllSchemes(
addressIndex: number,
message: string,
signatures: Map<number, string>
): booleanVerifies signatures for all enabled schemes. Returns true if all are valid.
free(): voidReleases WASM memory. Call this when you're done with the wallet instance.
SignatureScheme
Enum of available signature schemes:
enum SignatureScheme {
EcdsaSecp256k1 = 1,
Falcon512 = 2,
}Keypair
class Keypair {
publicKey: Uint8Array; // Raw public key bytes
privateKey: Uint8Array; // Raw private key bytes
free(): void; // Release WASM memory
}Utility Functions
generateMnemonic(): stringGenerates a new 24-word BIP-39 mnemonic phrase.
validateMnemonic(mnemonic: string): booleanValidates a BIP-39 mnemonic phrase.
Architecture
The hybrid HD wallet architecture enables multiple signature schemes to coexist within a single wallet structure while maintaining cryptographic separation.
Derivation Paths
BIP-85 Scheme Seed Derivation
Each signature scheme gets its own unique seed through BIP-85 derivation:
- ECDSA secp256k1:
m/83696968'/83286642'/1' - Falcon-512:
m/83696968'/83286642'/2'
The base path m/83696968' is the standard BIP-85 path, /83286642' stands for "Tectonic" in T9 keypad encoding, and the final component identifies the signature scheme.
Key Derivation Paths
ECDSA secp256k1 (BIP-32, BIP-44):
- Domain separator:
Bitcoin seed - 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-v1 seed - Full path:
m/44'/60'/0'/0'/{address_index}' - Standard: SLIP-0010 (all components hardened)
For more details, see the Architecture documentation.
Standards Used
- 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
Security
This library handles sensitive cryptographic material. Always:
- Store mnemonics securely (never in plain text)
- Use BIP-39 passphrases for additional security
- Verify signatures before trusting them
- Keep your dependencies up to date
- Call
.free()on WASM objects to clear memory
Note: While Falcon-512 is a NIST-selected post-quantum algorithm, this library is provided as-is for research and development. Conduct your own security audit before using in production.
FAQ
Why post-quantum cryptography?
Quantum computers pose a threat to current cryptographic systems like ECDSA. Falcon-512 is a NIST-selected post-quantum signature scheme that remains secure against quantum attacks.
Can I use just ECDSA or just Falcon?
Yes! Specify only the schemes you need when creating the wallet:
// ECDSA only
const wallet = new HHDWallet([SignatureScheme.EcdsaSecp256k1], null);
// Falcon only
const wallet = new HHDWallet([SignatureScheme.Falcon512], null);Is the mnemonic compatible with other wallets?
The mnemonic is BIP-39 compatible (24 words). However, the scheme-specific derivation uses BIP-85, so you'll need this library to restore the same keys. Standard BIP-44 wallets will derive different keys.
Do I need to call .free()?
Yes, for optimal memory management. WebAssembly doesn't have automatic garbage collection, so you should call .free() on wallet and keypair objects when you're done with them.
License
MIT © Tectonic Labs
