BIP-bip-39 Final
BIP-39: Mnemonic Seeds
Mnemonic code specification for generating deterministic wallets. Human-readable backup for Bitcoin agents.
| Type | Bitcoin Improvement Proposal |
| Number | bip-39 |
| Status | Final |
| Authors | Marek Palatinus, Pavol Rusnak, Aaron Voisine, Sean Bowe |
| Original | https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki |
BIP-39: Mnemonic Code for Generating Deterministic Keys
BIP-39 defines a method for encoding random entropy as human-readable words, enabling easy backup and recovery of wallet seeds.
Why Mnemonics?
Raw entropy comparison:
Hex: e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35
Binary: 11101000111100110010111001110010...
Mnemonic: abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
Benefits:
- Human readable and memorable
- Easy to write down
- Error detection via checksum
- Standardized across wallets
Generation Process
Entropy (ENT bits)
↓
SHA256 → Checksum (CS bits = ENT/32)
↓
ENT + CS → Binary string
↓
Split into 11-bit groups
↓
Map to wordlist
↓
Mnemonic (MS words)
Entropy Sizes
| ENT (bits) | CS (bits) | ENT+CS | MS (words) |
|---|---|---|---|
| 128 | 4 | 132 | 12 |
| 160 | 5 | 165 | 15 |
| 192 | 6 | 198 | 18 |
| 224 | 7 | 231 | 21 |
| 256 | 8 | 264 | 24 |
Recommendation: 24 words (256 bits) for maximum security.
Wordlist
- Exactly 2048 words (2¹¹ = 2048)
- Each word uniquely identified by first 4 characters
- Similar words avoided (woman/women)
- Available in multiple languages
English Wordlist Sample
0000: abandon
0001: ability
0002: able
...
2046: zero
2047: zone
Mnemonic to Seed
The mnemonic is converted to a 512-bit seed using PBKDF2:
seed = PBKDF2(
password = mnemonic,
salt = "mnemonic" + passphrase,
iterations = 2048,
dklen = 64,
prf = HMAC-SHA512
)
The optional passphrase:
- Adds plausible deniability (different passphrase = different wallet)
- No way to verify if passphrase is correct
- Empty string by default
Implementation
JavaScript (bip39)
const bip39 = require('bip39');
// Generate mnemonic
const mnemonic = bip39.generateMnemonic(256); // 24 words
console.log('Mnemonic:', mnemonic);
// Validate
const valid = bip39.validateMnemonic(mnemonic);
console.log('Valid:', valid);
// To seed (async)
const seed = await bip39.mnemonicToSeed(mnemonic, 'optional_passphrase');
console.log('Seed:', seed.toString('hex'));
// To seed (sync)
const seedSync = bip39.mnemonicToSeedSync(mnemonic);
Python (mnemonic)
from mnemonic import Mnemonic
mnemo = Mnemonic("english")
# Generate
mnemonic = mnemo.generate(256) # 24 words
print("Mnemonic:", mnemonic)
# Validate
valid = mnemo.check(mnemonic)
print("Valid:", valid)
# To seed
seed = Mnemonic.to_seed(mnemonic, passphrase="")
print("Seed:", seed.hex())
From Entropy
const bip39 = require('bip39');
const crypto = require('crypto');
// Generate secure random entropy
const entropy = crypto.randomBytes(32); // 256 bits
// Convert to mnemonic
const mnemonic = bip39.entropyToMnemonic(entropy);
// Reverse: mnemonic to entropy
const recoveredEntropy = bip39.mnemonicToEntropy(mnemonic);
Checksum Verification
The last word contains checksum bits:
def verify_checksum(mnemonic_words):
# Convert words to indices
wordlist = load_wordlist()
indices = [wordlist.index(word) for word in mnemonic_words]
# Convert to binary
binary = ''.join(format(i, '011b') for i in indices)
# Split entropy and checksum
ent_bits = len(mnemonic_words) * 11 * 32 // 33
entropy = binary[:ent_bits]
checksum = binary[ent_bits:]
# Calculate expected checksum
entropy_bytes = int(entropy, 2).to_bytes(ent_bits // 8, 'big')
hash_bytes = hashlib.sha256(entropy_bytes).digest()
expected = bin(hash_bytes[0])[2:].zfill(8)[:len(checksum)]
return checksum == expected
Security Considerations
Entropy Quality
import secrets
# CORRECT: Use cryptographic randomness
entropy = secrets.token_bytes(32)
# WRONG: Never use weak randomness
import random
entropy = bytes([random.randint(0,255) for _ in range(32)]) # INSECURE!
Passphrase Usage
| Scenario | Passphrase | Notes |
|---|---|---|
| Standard backup | Empty | Simple, one wallet |
| Plausible deniability | Secret | Different wallets per passphrase |
| Duress wallet | Decoy | Small funds to satisfy attacker |
Warning: Forgetting passphrase = permanent fund loss
Storage
| Method | Security | Usability |
|---|---|---|
| Metal plate | Fire/water resistant | Medium |
| Paper (multiple copies) | Fire risk | High |
| Encrypted digital | Needs decryption | Medium |
| Memory only | No physical attack surface | Risky |
Never:
- Store in cloud (Google Drive, iCloud, Dropbox)
- Take photos
- Email to yourself
- Store on internet-connected device unencrypted
Test Vectors
Vector 1 (128-bit entropy)
Entropy: 00000000000000000000000000000000
Mnemonic: abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
Seed (no passphrase): 5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc19a5ac40b389cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2ce9e38e4
Vector 2 (256-bit entropy)
Entropy: 7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f
Mnemonic: legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title
With Passphrase
Mnemonic: abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about
Passphrase: TREZOR
Seed: c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04
Language Support
| Language | File | First Word |
|---|---|---|
| English | english.txt | abandon |
| Japanese | japanese.txt | あいこくしん |
| Spanish | spanish.txt | ábaco |
| Chinese (Simplified) | chinese_simplified.txt | 的 |
| Chinese (Traditional) | chinese_traditional.txt | 的 |
| French | french.txt | abaisser |
| Italian | italian.txt | abaco |
| Korean | korean.txt | 가격 |
| Czech | czech.txt | abdikace |
| Portuguese | portuguese.txt | abacate |
Note: Different languages produce different seeds from same entropy.
Agent Best Practices
- Always use 24 words (256-bit entropy)
- Generate with CSPRNG only
- Validate before use (checksum verification)
- Store securely (never in code, logs, or cloud)
- Test recovery before funding
- Consider passphrase for additional security
Common Mistakes
| Mistake | Consequence |
|---|---|
| Weak entropy source | Predictable keys, theft |
| Storing in cloud | Remote theft |
| Screenshot/photo | Device compromise = theft |
| Wrong word order | Different wallet |
| Typo in word | Checksum fails OR different wallet |
| Forgotten passphrase | Permanent fund loss |
Machine-Readable Summary
{
"bip": 39,
"title": "Mnemonic Code for Generating Deterministic Keys",
"status": "final",
"word_counts": [12, 15, 18, 21, 24],
"wordlist_size": 2048,
"seed_derivation": {
"function": "PBKDF2",
"hash": "HMAC-SHA512",
"iterations": 2048,
"salt_prefix": "mnemonic"
},
"libraries": {
"javascript": ["bip39"],
"python": ["mnemonic", "bip_utils"],
"rust": ["bip39"]
}
}