BIP39 Mnemonic Security-Focused API Documentation
Algorithm: BIP39 (Bitcoin Improvement Proposal 39)
Type: Mnemonic Seed Phrase Generation
Security Level: 128-256 bits of entropy
Word Lists: 2048 words per language
Supported Languages: English, Japanese, French, Spanish, Chinese (Simplified/Traditional), Korean, Czech, Portuguese
Standard: BIP39 Specification
Table of Contents
- Security Contract
- Attack Resistance Matrix
- Secure Usage Examples
- Common Mistakes
- Performance vs Security
- Platform-Specific Notes
- Compliance Information
Security Contract
generateMnemonic(strength?: number, language?: string): string
Preconditions:
strength(optional) MUST be 128, 160, 192, 224, or 256 bits (default: 128)strengthMUST be divisible by 32language(optional) MUST be supported language code (default: ‘english’)- System MUST have cryptographically secure random source
Postconditions:
- Returns mnemonic phrase of (strength/32)*3 words
- Each word is from the specified language’s 2048-word list
- Mnemonic includes valid checksum
- Entropy is cryptographically random
- Generated mnemonic can recreate same seed deterministically
Side Effects:
- Consumes system entropy
- May load word list into memory
mnemonicToSeed(mnemonic: string, passphrase?: string): Uint8Array
Preconditions:
mnemonicMUST be valid BIP39 mnemonic phrasemnemonicwords MUST exist in word listmnemonicchecksum MUST be validpassphrase(optional) can be any string (default: empty string)
Postconditions:
- Returns 64-byte (512-bit) seed
- Same mnemonic + passphrase always produces same seed
- Seed is suitable for HD wallet derivation
- Uses PBKDF2-HMAC-SHA512 with 2048 iterations
Side Effects:
- CPU intensive operation (PBKDF2)
- No timing variations based on input
validateMnemonic(mnemonic: string, language?: string): boolean
Preconditions:
mnemonicis a string of space-separated wordslanguage(optional) specifies word list to validate against
Postconditions:
- Returns
trueif mnemonic is valid BIP39 phrase - Returns
falseif invalid (wrong words, bad checksum) - No information leaked about which validation failed
- Constant-time checksum verification
Side Effects:
- May load word list into memory
- No observable timing differences
entropyToMnemonic(entropy: Uint8Array, language?: string): string
Preconditions:
entropyMUST be 16, 20, 24, 28, or 32 bytesentropySHOULD be cryptographically randomlanguage(optional) MUST be supported language
Postconditions:
- Returns valid BIP39 mnemonic
- Mnemonic encodes the entropy with checksum
- Can recover exact entropy from mnemonic
Side Effects:
- None beyond memory allocation
Attack Resistance Matrix
✅ Attacks Prevented
| Attack Type | Protection Mechanism | Security Level |
|---|---|---|
| Brute Force (128-bit) | Large keyspace | 2^128 operations |
| Brute Force (256-bit) | Maximum keyspace | 2^256 operations |
| Dictionary Attack | PBKDF2 with 2048 iterations | Slows attacks |
| Checksum Forgery | SHA256 checksum validation | 2^-8 to 2^-4 probability |
| Word Substitution | Checksum detects changes | Invalid mnemonic |
| Timing Attacks | Constant-time validation | No information leakage |
| Weak Randomness | Uses secure random source | Full entropy |
❌ Attacks NOT Prevented
| Attack Type | Reason | Mitigation Required |
|---|---|---|
| Physical Observation | Words visible when entered | Use hardware wallets |
| Memory Disclosure | Mnemonic in plaintext memory | Secure memory handling |
| Weak Passphrase | User-chosen passphrase | Enforce strong passphrases |
| Social Engineering | Users may share mnemonics | User education |
| Clipboard Attacks | Copy/paste exposes mnemonic | Avoid clipboard usage |
| Screen Recording | Display of mnemonic | Secure display methods |
| Acoustic Analysis | Keyboard sounds during entry | Use virtual keyboards |
⚠️ Security Limitations
- Word List Dependency: Security depends on standardized word lists
- Human Memory: 12-24 words challenging to memorize accurately
- No Forward Secrecy: Compromised mnemonic reveals all derived keys
- Passphrase Weakness: Most users don’t use additional passphrase
- Error Correction: No built-in error correction beyond checksum
Secure Usage Examples
Secure Wallet Generation
import { BIP39 } from '@metamui/bip39';
import { secureErase } from '@metamui/secure-memory';
class SecureWallet {
static async generateWallet(): Promise<{
mnemonic: string;
seed: Uint8Array;
}> {
// Generate maximum entropy mnemonic (24 words)
const mnemonic = BIP39.generateMnemonic(256);
// Derive seed with optional passphrase
const passphrase = await this.getSecurePassphrase();
const seed = BIP39.mnemonicToSeed(mnemonic, passphrase);
// Clear passphrase from memory
secureErase(passphrase);
return { mnemonic, seed };
}
private static async getSecurePassphrase(): Promise<string> {
// In production, get from secure input
// This is just an example
return 'optional-secure-passphrase';
}
}
// Usage with secure display
async function displayMnemonicSecurely(mnemonic: string) {
const words = mnemonic.split(' ');
console.log('Write down these words in order:');
console.log('NEVER share them with anyone!');
console.log('NEVER enter them on a website!');
// Display words in groups to aid memorization
for (let i = 0; i < words.length; i += 4) {
const group = words.slice(i, i + 4).join(' ');
console.log(`Words ${i + 1}-${i + 4}: ${group}`);
// In real app, wait for user confirmation
await new Promise(resolve => setTimeout(resolve, 2000));
}
console.log('\nHave you written down all words? [yes/no]');
}
Secure Mnemonic Recovery
class MnemonicRecovery {
static async recoverWallet(
mnemonicWords: string[],
passphrase?: string
): Promise<Uint8Array | null> {
// Reconstruct mnemonic from array
const mnemonic = mnemonicWords.join(' ').toLowerCase().trim();
// Validate before processing
if (!BIP39.validateMnemonic(mnemonic)) {
// Don't reveal which word is wrong
console.error('Invalid recovery phrase');
return null;
}
// Derive seed
const seed = BIP39.mnemonicToSeed(
mnemonic,
passphrase || ''
);
// Clear sensitive data
mnemonicWords.forEach(word => secureErase(word));
secureErase(mnemonic);
return seed;
}
// Secure word-by-word entry
static async enterMnemonicSecurely(): Promise<string[]> {
const words: string[] = [];
const wordCount = await this.getWordCount(); // 12, 15, 18, 21, or 24
for (let i = 0; i < wordCount; i++) {
// In production, use secure input method
const word = await this.getWordSecurely(i + 1);
// Validate word exists in wordlist
if (!BIP39.wordlists.english.includes(word)) {
throw new Error(`Word ${i + 1} not in wordlist`);
}
words.push(word);
}
return words;
}
}
Multi-Language Support
// Generate mnemonic in different languages
const mnemonics = {
english: BIP39.generateMnemonic(128, 'english'),
japanese: BIP39.generateMnemonic(128, 'japanese'),
french: BIP39.generateMnemonic(128, 'french'),
spanish: BIP39.generateMnemonic(128, 'spanish')
};
// All generate different words but same entropy can produce same seed
const entropy = crypto.getRandomValues(new Uint8Array(16));
const sameSeedMnemonics = {
english: BIP39.entropyToMnemonic(entropy, 'english'),
japanese: BIP39.entropyToMnemonic(entropy, 'japanese')
};
// Both produce identical seeds despite different words
const seed1 = BIP39.mnemonicToSeed(sameSeedMnemonics.english);
const seed2 = BIP39.mnemonicToSeed(sameSeedMnemonics.japanese);
// seed1 equals seed2
Hierarchical Deterministic (HD) Wallet
import { Ed25519 } from '@metamui/ed25519';
import { HKDF } from '@metamui/hkdf';
class HDWallet {
private constructor(private seed: Uint8Array) {}
static fromMnemonic(mnemonic: string, passphrase?: string): HDWallet {
if (!BIP39.validateMnemonic(mnemonic)) {
throw new Error('Invalid mnemonic');
}
const seed = BIP39.mnemonicToSeed(mnemonic, passphrase);
return new HDWallet(seed);
}
async deriveKey(path: string): Promise<{
privateKey: Uint8Array;
publicKey: Uint8Array;
}> {
// BIP32/BIP44 path derivation
const pathSegments = path.split('/');
let key = this.seed;
for (const segment of pathSegments) {
const index = parseInt(segment.replace("'", ""));
const hardened = segment.includes("'");
// Derive child key
key = await HKDF.expand(
key,
new TextEncoder().encode(`${index}${hardened ? 'H' : 'S'}`),
32
);
}
// Generate Ed25519 keypair from derived seed
return Ed25519.generateKeyPair(key);
}
}
Common Mistakes
❌ Weak Entropy Sources
// NEVER DO THIS - Predictable entropy!
function insecureEntropy(): Uint8Array {
const entropy = new Uint8Array(16);
const timestamp = Date.now();
// Timestamp is predictable!
for (let i = 0; i < 8; i++) {
entropy[i] = (timestamp >> (i * 8)) & 0xFF;
}
// Math.random() is not cryptographically secure!
for (let i = 8; i < 16; i++) {
entropy[i] = Math.floor(Math.random() * 256);
}
return entropy;
}
// CORRECT: Use cryptographically secure random
function secureEntropy(): Uint8Array {
return crypto.getRandomValues(new Uint8Array(16));
}
❌ Storing Mnemonics Insecurely
// NEVER DO THIS - Plaintext storage!
function insecureStorage(mnemonic: string) {
// Browser localStorage is not secure!
localStorage.setItem('wallet_mnemonic', mnemonic);
// Plain files are not secure!
fs.writeFileSync('wallet.txt', mnemonic);
// Logging exposes secrets!
console.log('Generated mnemonic:', mnemonic);
}
// BETTER: Encrypt before storage (still risky)
async function betterStorage(mnemonic: string, password: string) {
const key = await deriveKey(password);
const encrypted = await encrypt(key, mnemonic);
// Store encrypted version only
localStorage.setItem('wallet_encrypted', encrypted);
// Clear plaintext immediately
secureErase(mnemonic);
}
❌ Poor Passphrase Practices
// BAD: Weak or no passphrase
const seed1 = BIP39.mnemonicToSeed(mnemonic); // No passphrase
const seed2 = BIP39.mnemonicToSeed(mnemonic, '123456'); // Weak
const seed3 = BIP39.mnemonicToSeed(mnemonic, 'password'); // Common
// GOOD: Strong passphrase
const strongPassphrase = generateStrongPassphrase();
const seed = BIP39.mnemonicToSeed(mnemonic, strongPassphrase);
// BEST: Separate passphrase from mnemonic storage
// Store mnemonic in one secure location
// Store passphrase in different secure location
❌ Validation Errors
// WRONG: Accepting invalid mnemonics
function acceptAnyWords(words: string) {
// This skips validation!
const seed = BIP39.mnemonicToSeed(words);
return seed;
}
// CORRECT: Always validate first
function validateBeforeUse(words: string) {
if (!BIP39.validateMnemonic(words)) {
throw new Error('Invalid mnemonic phrase');
}
const seed = BIP39.mnemonicToSeed(words);
return seed;
}
Performance vs Security
Entropy vs Word Count Trade-offs
| Entropy Bits | Word Count | Security Level | Use Case |
|---|---|---|---|
| 128 | 12 | Standard | Most wallets |
| 160 | 15 | Enhanced | Higher security |
| 192 | 18 | High | Important accounts |
| 224 | 21 | Very High | Rarely used |
| 256 | 24 | Maximum | Cold storage |
PBKDF2 Iteration Count
// BIP39 specifies 2048 iterations
// This is relatively low by modern standards
// But changing it breaks compatibility
// For additional security, use strong passphrase
// Or additional key stretching:
async function enhancedSeedDerivation(
mnemonic: string,
passphrase: string
): Promise<Uint8Array> {
// Standard BIP39 seed
const basicSeed = BIP39.mnemonicToSeed(mnemonic, passphrase);
// Additional stretching with Argon2
const enhancedSeed = await Argon2.hash(
basicSeed,
new TextEncoder().encode('enhanced-derivation'),
{
timeCost: 10,
memoryCost: 65536,
outputLength: 64
}
);
return enhancedSeed;
}
Platform-Specific Notes
Browser/JavaScript
- Use Web Crypto API for entropy:
crypto.getRandomValues() - Clear sensitive strings:
str = ''; for(let i=0; i<100; i++) str += ' '; - Avoid storing in IndexedDB or localStorage
- Consider memory-hard functions for additional security
Node.js
- Use
crypto.randomBytes()for entropy - Clear Buffers with
buffer.fill(0) - Use
--max-old-space-sizeto limit memory exposure - Consider using secure-random libraries
Python
- Use
secrets.randbits()for entropy - Clear with
ctypes.memset()orbytearrayoverwrite - Disable swap with
mlock()if available - Use
getpassfor secure passphrase input
Rust
- Use
getrandomcrate for entropy - Implement
Zeroizetrait for cleanup - Use
secrecycrate for sensitive data - Compile with security flags
Mobile (iOS/Android)
- Use platform secure storage (Keychain/Keystore)
- Implement app backgrounding protection
- Disable screenshots during mnemonic display
- Use biometric authentication for access
Compliance Information
Standards Compliance
- BIP39: Full compliance with Bitcoin Improvement Proposal 39
- BIP32: Compatible with HD wallet derivation
- BIP44: Compatible with multi-account hierarchy
- SLIP-0039: Not compatible (different standard - Shamir’s shares)
Security Considerations
- Entropy Requirements: Minimum 128 bits required
- Word Lists: Standardized 2048-word lists only
- Checksum: Last word partially determined by checksum
- PBKDF2: Fixed at 2048 iterations per spec
Best Practices
✅ DO:
- Generate mnemonics with 128+ bits of entropy
- Use passphrases for additional security
- Validate mnemonics before use
- Store securely (hardware wallet, paper, steel)
- Test recovery process before funding
❌ DON’T:
- Share mnemonics with anyone
- Enter mnemonics on websites
- Store digitally without encryption
- Use brain wallets (memorized phrases)
- Modify the word lists
Regulatory Notes
- Not Currency: Mnemonics are not cryptocurrency
- Export Control: May be subject to cryptography export rules
- Recovery: Users responsible for secure storage
- No Recovery Service: Lost mnemonics cannot be recovered
Security Notice: BIP39 mnemonics are the keys to your digital assets. Treat them with extreme care. Never share them. Never enter them online. Always verify you can recover your wallet before storing significant value.
Last Updated: 2025-07-06
**Version: 3.0.0
License: BSL-1.1
Security Analysis
Threat Model: BIP39 Threat Model
The comprehensive threat analysis covers:
- Algorithm-specific attack vectors
- Implementation vulnerabilities
- Side-channel considerations
- Quantum resistance analysis (where applicable)
- Deployment recommendations
For complete security analysis and risk assessment, see the dedicated threat model documentation.