Skip to main content

Try it Live

Run BIP39 examples in the interactive playground

Overview

BIP-39 mnemonics are the master keys to cryptocurrency wallets. Proper security prevents loss of funds through theft, compromise, or accidental destruction.

Threat Model

Attack Vectors

1. Physical Theft
  • Stolen paper backup
  • Photographed mnemonic
  • Shoulder surfing during entry
2. Digital Compromise
  • Keylogger malware
  • Screenshot malware
  • Clipboard monitoring
  • Network sniffing (if transmitted)
3. Social Engineering
  • Phishing websites
  • Fake wallet software
  • Support scams
4. Environmental Damage
  • Fire
  • Flood
  • Physical deterioration

Entropy Requirements

Minimum Entropy

128 bits (12 words):
// 2^128 possible combinations
const entropy128 = Math.pow(2, 128);
console.log(entropy128); // 3.4e38

// Brute force time (1 billion attempts/second):
const seconds = entropy128 / 1e9;
const years = seconds / (365.25 * 24 * 3600);
console.log(years); // 1.08e22 years
256 bits (24 words):
// 2^256 possible combinations
const entropy256 = Math.pow(2, 256);

// Essentially unbreakable by brute force
// More combinations than atoms in observable universe

Entropy Source Quality

Cryptographically Secure RNG:
// ✅ SECURE - Web Crypto API
const secure = crypto.getRandomValues(Bytes32());
const mnemonic = Bip39.entropyToMnemonic(secure);

// ✅ SECURE - Node.js crypto
import { randomBytes } from 'crypto';
const nodeEntropy = randomBytes(32);

// ❌ INSECURE - Never use Math.random()
const insecure = Bytes32();
for (let i = 0; i < 32; i++) {
  insecure[i] = Math.floor(Math.random() * 256); // PREDICTABLE!
}
Hardware RNG (Ideal):
// Hardware wallets use dedicated secure elements
// - TRNG (True Random Number Generator)
// - Protected from software attacks
// - Tamper-resistant

// Example: Ledger, Trezor
const hardwareMnemonic = await hardwareWallet.generateMnemonic();

Secure Generation

Offline Generation (Cold Wallet)

/**
 * Maximum security setup:
 * 1. Air-gapped computer (never connected to network)
 * 2. Live OS (Tails, Ubuntu) on USB
 * 3. Generate mnemonic
 * 4. Write on paper
 * 5. Wipe computer
 */

// On air-gapped machine:
const mnemonic = Bip39.generateMnemonic(256);

// Write down manually (never digital)
console.log('Write this down:');
console.log(mnemonic);

// Verify backup
const verified = prompt('Enter mnemonic to verify:');
if (verified !== mnemonic) {
  console.error('Verification failed. Re-write backup.');
}

// Clear clipboard and screen
// Power off machine

Online Generation (Hot Wallet)

/**
 * Acceptable for small amounts:
 * 1. Trusted device
 * 2. Updated OS
 * 3. No malware
 * 4. Strong passphrase
 */

const mnemonic = Bip39.generateMnemonic(256);
const passphrase = 'strong memorable passphrase';
const seed = await Bip39.mnemonicToSeed(mnemonic, passphrase);

// Store encrypted
const encrypted = await encryptMnemonic(mnemonic, userPassword);
await secureStorage.save(encrypted);

Storage Best Practices

Physical Storage

Paper Backup:
/**
 * Write mnemonic on acid-free paper
 * - Use archival-quality pen
 * - Write clearly (no ambiguous characters)
 * - Include word numbers
 * - Store in fireproof safe
 * - Consider duplicate in different location
 */

// Format:
// 1. abandon
// 2. ability
// 3. able
// ...
// 24. art
Metal Backup:
Superior durability:
- Fireproof (up to 1500°C)
- Waterproof
- Corrosion resistant
- Impact resistant

Products: Cryptosteel, Billfodl, Steely
Split Storage (Shamir’s Secret Sharing):
/**
 * Split mnemonic into N shares, require M to recover
 * Example: 3-of-5 scheme (any 3 shares reconstruct)
 */

// Not native BIP-39 (use SLIP-39 for standard split)
// Or implement Shamir Secret Sharing separately

Digital Storage (Encrypted)

import * as AesGcm from '@tevm/voltaire/AesGcm';

async function encryptMnemonic(
  mnemonic: string,
  password: string
): Promise<{ encrypted: Uint8Array; nonce: Uint8Array }> {
  // Derive key from password
  const encoder = new TextEncoder();
  const passwordBytes = encoder.encode(password);
  const salt = crypto.getRandomValues(Bytes16());

  const keyMaterial = await crypto.subtle.importKey(
    'raw',
    passwordBytes,
    'PBKDF2',
    false,
    ['deriveBits']
  );

  const keyBits = await crypto.subtle.deriveBits(
    {
      name: 'PBKDF2',
      salt,
      iterations: 100000,
      hash: 'SHA-256'
    },
    keyMaterial,
    256
  );

  const key = new Uint8Array(keyBits);

  // Encrypt mnemonic
  const nonce = AesGcm.generateNonce();
  const mnemonicBytes = encoder.encode(mnemonic);
  const encrypted = await AesGcm.encrypt(mnemonicBytes, key, nonce);

  // Store: encrypted + nonce + salt
  return { encrypted, nonce };
}

Never Store Digitally (Unencrypted)

// ❌ NEVER DO THIS
localStorage.setItem('mnemonic', mnemonic);
await fetch('/api/backup', { body: mnemonic });
await fs.writeFile('mnemonic.txt', mnemonic);
email.send({ attachment: mnemonic });

// ✅ ONLY IF ENCRYPTED
const encrypted = await encryptMnemonic(mnemonic, strongPassword);
localStorage.setItem('wallet', JSON.stringify(encrypted));

Passphrase Security

Passphrase as 25th Word

/**
 * Advantages:
 * - Plausible deniability (decoy wallet)
 * - Two-factor security
 * - Memory-based protection
 *
 * Risks:
 * - Forget passphrase = lose funds forever
 * - No recovery mechanism
 */

const mnemonic = Bip39.generateMnemonic(256);

// Decoy wallet (small amount, no passphrase)
const decoySeed = await Bip39.mnemonicToSeed(mnemonic);

// Real wallet (main funds, with passphrase)
const passphrase = 'correct horse battery staple ancient wisdom';
const realSeed = await Bip39.mnemonicToSeed(mnemonic, passphrase);

Strong Passphrases

// ❌ Weak passphrases
const weak = [
  '',              // No passphrase
  '1234',         // Trivial
  'password',     // Dictionary word
  'mybirthday',   // Personal info
];

// ✅ Strong passphrases
const strong = [
  'correct horse battery staple ancient wisdom mountain',  // Diceware
  'My cat Fluffy was born in 2015 on a Tuesday!',        // Personal sentence
  'L3t$_M@k3-A_R@nd0m!P@$$phr@$3#2024',                  // Complex
];

Passphrase Storage

/**
 * If using passphrase:
 * - Store separately from mnemonic
 * - Memorize if possible
 * - If written, encrypt differently
 * - Never store together
 */

// ❌ NEVER
const backup = {
  mnemonic: '...',
  passphrase: '...'
}; // Single point of failure

// ✅ BETTER
// Mnemonic: fireproof safe at home
// Passphrase: memorized OR different location

Operational Security

Never Share Mnemonic

// ❌ NEVER share via:
// - Email
// - SMS
// - Messaging apps
// - Phone call
// - Screenshot
// - Photo
// - Cloud storage
// - Support ticket

// ✅ ONLY share:
// - xpub (view-only, no spending)
// - Individual addresses
// - Signed messages (proof of ownership)

Avoid Digital Exposure

/**
 * Minimize digital footprint:
 */

// ❌ Type on networked device
const typed = prompt('Enter mnemonic:'); // Keylogger risk

// ✅ Offline entry (hardware wallet)
const hw = await hardwareWallet.connect();
await hw.confirmMnemonic(); // Never leaves device

// ✅ QR code (air-gapped)
const qr = generateQR(mnemonic);
// Display on offline device, scan from online device

Secure Disposal

/**
 * When decommissioning wallet:
 */

// 1. Transfer all funds
await transferAllFunds(newWallet);

// 2. Zero out memory
const mnemonicBytes = new TextEncoder().encode(mnemonic);
mnemonicBytes.fill(0);

const seedBytes = await Bip39.mnemonicToSeed(mnemonic);
seedBytes.fill(0);

// 3. Destroy physical backups
// - Shred paper
// - Melt metal plates
// - Wipe encrypted storage

// 4. Clear browser/device
// - Clear history
// - Clear clipboard
// - Restart device

Attack Mitigation

Phishing Protection

/**
 * Verify wallet software authenticity:
 */

// ✅ Check domain
const legitDomain = 'example-wallet.com';
if (window.location.hostname !== legitDomain) {
  throw new Error('Phishing detected!');
}

// ✅ Verify code signature
// Download from official source
// Check GPG signature
// Verify hash matches official

// ❌ Never download from:
// - Search engine ads
// - Third-party app stores
// - Email links
// - Social media links

Malware Protection

/**
 * Defense layers:
 */

// 1. Hardware wallet (best)
const hw = await connectHardwareWallet();
// Mnemonic never leaves device

// 2. Air-gapped device (very good)
const offline = generateOnAirGappedDevice();

// 3. Clean OS (good)
// - Fresh install
// - Minimal software
// - No network during generation

// 4. Updated OS + antivirus (baseline)
// - Keep OS patched
// - Run antivirus
// - Avoid pirated software

Clipboard Hijacking

// Some malware monitors clipboard for crypto addresses

// ❌ Vulnerable
navigator.clipboard.writeText(mnemonic); // Malware can read

// ✅ Never copy mnemonic to clipboard
// ✅ Type manually when needed
// ✅ Use QR codes for transfer

Recovery Security

Testing Recovery

/**
 * Verify backup before adding large funds:
 */

async function testRecovery(mnemonic: string, passphrase?: string) {
  // 1. Validate mnemonic
  if (!Bip39.validateMnemonic(mnemonic)) {
    throw new Error('Invalid mnemonic');
  }

  // 2. Derive seed
  const seed = await Bip39.mnemonicToSeed(mnemonic, passphrase);

  // 3. Derive first address
  const root = HDWallet.fromSeed(seed);
  const eth0 = HDWallet.deriveEthereum(root, 0, 0);
  const address = deriveAddress(eth0);

  // 4. Verify matches original
  console.log('Recovered address:', address);
  return address;
}

// Test with small amount first
const recovered = await testRecovery(writtenMnemonic, passphrase);
console.log('Recovery successful:', recovered);

Inheritance Planning

/**
 * Ensure heirs can recover:
 */

interface InheritancePackage {
  // Encrypted mnemonic
  encrypted: Uint8Array;

  // Instructions (no secrets)
  instructions: string;

  // Decryption hints (not password)
  hints: string[];

  // Software/wallet info
  wallet: {
    name: string;
    version: string;
    derivation: string; // "m/44'/60'/0'/0/0"
  };
}

const package: InheritancePackage = {
  encrypted: await encryptMnemonic(mnemonic, masterPassword),
  instructions: 'Use password we discussed. Contact @lawyer for legal access.',
  hints: ['Favorite book title', 'Wedding anniversary'],
  wallet: {
    name: 'tevm',
    version: '1.0.0',
    derivation: "m/44'/60'/0'/0/0"
  }
};

Advanced Security

Multi-Signature Alternative

/**
 * Instead of single mnemonic, use multisig:
 * - Requires M of N signatures
 * - No single point of failure
 * - Better for high-value wallets
 */

// Example: 2-of-3 multisig
const mnemonic1 = Bip39.generateMnemonic(256);
const mnemonic2 = Bip39.generateMnemonic(256);
const mnemonic3 = Bip39.generateMnemonic(256);

// Requires any 2 of 3 to sign transactions
// More complex but more secure

Time-Locked Recovery

/**
 * Add time lock for recovery:
 * - Prevents immediate theft
 * - Allows cancellation if compromised
 */

// Not part of BIP-39, but can be implemented
// at smart contract level

Compliance

Regulatory Considerations

/**
 * Some jurisdictions require:
 * - Key escrow
 * - Recovery mechanisms
 * - Reporting thresholds
 *
 * Consult legal counsel for compliance
 */

References