BIP39 Mnemonic Phrases

Overview

BIP39 (Bitcoin Improvement Proposal 39) defines a method for generating deterministic wallets using mnemonic phrases. It allows cryptographic keys to be represented as a sequence of human-readable words, making key backup and recovery much more user-friendly than handling raw binary data.

Key Features

Common Use Cases

Algorithm Details

Parameters

Parameter Description Values
Entropy Length Input randomness 128, 160, 192, 224, 256 bits
Mnemonic Length Number of words 12, 15, 18, 21, 24 words
Checksum Length Error detection bits 4, 5, 6, 7, 8 bits
Wordlist Size Words per language 2048 words
PBKDF2 Iterations Key stretching 2048 iterations

Security Properties

Implementation

Python Example

from metamui_crypto import BIP39, generate_random_bytes
import os

# Basic mnemonic generation
bip39 = BIP39()

# Generate 24-word mnemonic (256 bits entropy)
mnemonic = bip39.generate_mnemonic(strength=256)
print(f"Mnemonic: {mnemonic}")

# Generate 12-word mnemonic (128 bits entropy)
mnemonic_12 = bip39.generate_mnemonic(strength=128)
print(f"12-word mnemonic: {mnemonic_12}")

# Convert mnemonic to seed
seed = bip39.mnemonic_to_seed(mnemonic)
print(f"Seed: {seed.hex()}")

# Convert mnemonic to seed with passphrase
passphrase = "my secure passphrase"
seed_with_passphrase = bip39.mnemonic_to_seed(mnemonic, passphrase)
print(f"Seed with passphrase: {seed_with_passphrase.hex()}")

# Validate mnemonic
is_valid = bip39.validate_mnemonic(mnemonic)
print(f"Mnemonic valid: {is_valid}")

# Convert between entropy and mnemonic
entropy = generate_random_bytes(32)  # 256 bits
mnemonic_from_entropy = bip39.entropy_to_mnemonic(entropy)
recovered_entropy = bip39.mnemonic_to_entropy(mnemonic_from_entropy)
assert entropy == recovered_entropy

Advanced Usage

# Secure wallet implementation
class BIP39Wallet:
    def __init__(self, language='english'):
        self.bip39 = BIP39(language=language)
        self.mnemonic = None
        self.seed = None
    
    def create_wallet(self, strength=256, passphrase=""):
        """Create new wallet with mnemonic backup"""
        # Generate mnemonic
        self.mnemonic = self.bip39.generate_mnemonic(strength)
        
        # Derive seed
        self.seed = self.bip39.mnemonic_to_seed(self.mnemonic, passphrase)
        
        return {
            'mnemonic': self.mnemonic,
            'word_count': len(self.mnemonic.split()),
            'entropy_bits': strength,
            'instructions': [
                'Write down these words in order',
                'Store them in a secure location',
                'Never share them with anyone',
                'Test recovery before using'
            ]
        }
    
    def restore_wallet(self, mnemonic, passphrase=""):
        """Restore wallet from mnemonic"""
        # Validate mnemonic
        if not self.bip39.validate_mnemonic(mnemonic):
            raise ValueError("Invalid mnemonic phrase")
        
        # Restore seed
        self.mnemonic = mnemonic
        self.seed = self.bip39.mnemonic_to_seed(mnemonic, passphrase)
        
        return {
            'status': 'restored',
            'word_count': len(mnemonic.split()),
            'seed_preview': self.seed[:8].hex() + '...'
        }
    
    def export_seed(self, format='hex'):
        """Export seed in various formats"""
        if not self.seed:
            raise ValueError("No seed available")
        
        if format == 'hex':
            return self.seed.hex()
        elif format == 'base64':
            import base64
            return base64.b64encode(self.seed).decode()
        elif format == 'bytes':
            return self.seed
        else:
            raise ValueError("Unsupported format")
    
    def derive_key(self, purpose, index=0):
        """Derive specific key from master seed"""
        if not self.seed:
            raise ValueError("No seed available")
        
        # Simple key derivation (use BIP32 for full HD wallets)
        from metamui_crypto import HMAC, SHA512
        
        hmac = HMAC(self.seed, SHA512())
        key_material = hmac.compute(f"{purpose}:{index}".encode())
        
        return key_material[:32]  # 256-bit key

# Multi-language support
class MultiLanguageBIP39:
    def __init__(self):
        self.supported_languages = [
            'english', 'japanese', 'chinese_simplified',
            'chinese_traditional', 'french', 'italian',
            'korean', 'spanish', 'czech'
        ]
    
    def generate_multilingual_backup(self, entropy, languages=None):
        """Generate mnemonic in multiple languages"""
        if languages is None:
            languages = ['english', 'japanese']
        
        backups = {}
        
        for language in languages:
            if language not in self.supported_languages:
                continue
            
            bip39 = BIP39(language=language)
            mnemonic = bip39.entropy_to_mnemonic(entropy)
            backups[language] = mnemonic
        
        return backups
    
    def validate_multilingual(self, mnemonic, language):
        """Validate mnemonic in specific language"""
        bip39 = BIP39(language=language)
        return bip39.validate_mnemonic(mnemonic)

# Hardware wallet simulation
class HardwareWalletSim:
    def __init__(self):
        self.bip39 = BIP39()
        self.device_entropy = None
        self.user_entropy = None
        self.combined_entropy = None
    
    def initialize_device(self):
        """Initialize hardware wallet with device entropy"""
        # Device generates its own entropy
        self.device_entropy = os.urandom(32)
        
        return {
            'status': 'initialized',
            'device_id': self.device_entropy[:8].hex(),
            'ready_for_setup': True
        }
    
    def setup_wallet(self, user_entropy=None):
        """Setup wallet combining device and user entropy"""
        if not self.device_entropy:
            raise ValueError("Device not initialized")
        
        # User can provide additional entropy
        if user_entropy is None:
            user_entropy = os.urandom(32)
        
        self.user_entropy = user_entropy
        
        # Combine entropies securely
        from metamui_crypto import SHA256
        hasher = SHA256()
        hasher.update(self.device_entropy)
        hasher.update(self.user_entropy)
        hasher.update(b"BIP39_ENTROPY_COMBINATION")
        self.combined_entropy = hasher.finalize()
        
        # Generate mnemonic
        mnemonic = self.bip39.entropy_to_mnemonic(self.combined_entropy)
        
        return {
            'mnemonic': mnemonic,
            'entropy_sources': 'device + user',
            'total_entropy_bits': 256
        }
    
    def verify_backup(self, mnemonic):
        """Verify user wrote down mnemonic correctly"""
        if not self.combined_entropy:
            raise ValueError("Wallet not set up")
        
        # Check if provided mnemonic matches generated one
        expected_mnemonic = self.bip39.entropy_to_mnemonic(self.combined_entropy)
        
        return {
            'verified': mnemonic == expected_mnemonic,
            'word_count': len(mnemonic.split()),
            'checksum_valid': self.bip39.validate_mnemonic(mnemonic)
        }

# Mnemonic recovery tools
class MnemonicRecovery:
    def __init__(self):
        self.bip39 = BIP39()
        self.wordlist = self.bip39.get_wordlist()
    
    def suggest_words(self, partial_word):
        """Suggest words based on partial input"""
        partial = partial_word.lower()
        suggestions = [
            word for word in self.wordlist 
            if word.startswith(partial)
        ]
        return suggestions[:10]  # Limit to 10 suggestions
    
    def find_missing_word(self, incomplete_mnemonic, position):
        """Find missing word at specific position"""
        words = incomplete_mnemonic.split()
        
        if len(words) not in [11, 14, 17, 20, 23]:  # Missing one word
            raise ValueError("Invalid word count for missing word recovery")
        
        # Try each word in wordlist at the missing position
        for candidate in self.wordlist:
            test_words = words.copy()
            test_words.insert(position, candidate)
            test_mnemonic = ' '.join(test_words)
            
            if self.bip39.validate_mnemonic(test_mnemonic):
                return candidate
        
        return None
    
    def fix_typo(self, mnemonic_with_typo):
        """Attempt to fix single-word typo"""
        words = mnemonic_with_typo.split()
        
        # Try replacing each word
        for i, word in enumerate(words):
            if word not in self.wordlist:
                # Find closest matches
                candidates = self.find_similar_words(word)
                
                for candidate in candidates:
                    test_words = words.copy()
                    test_words[i] = candidate
                    test_mnemonic = ' '.join(test_words)
                    
                    if self.bip39.validate_mnemonic(test_mnemonic):
                        return {
                            'corrected_mnemonic': test_mnemonic,
                            'position': i,
                            'original_word': word,
                            'corrected_word': candidate
                        }
        
        return None
    
    def find_similar_words(self, word):
        """Find words similar to input (edit distance)"""
        def edit_distance(s1, s2):
            if len(s1) < len(s2):
                return edit_distance(s2, s1)
            
            if len(s2) == 0:
                return len(s1)
            
            previous_row = range(len(s2) + 1)
            for i, c1 in enumerate(s1):
                current_row = [i + 1]
                for j, c2 in enumerate(s2):
                    insertions = previous_row[j + 1] + 1
                    deletions = current_row[j] + 1
                    substitutions = previous_row[j] + (c1 != c2)
                    current_row.append(min(insertions, deletions, substitutions))
                previous_row = current_row
            
            return previous_row[-1]
        
        # Find words with edit distance <= 2
        similar = []
        for wordlist_word in self.wordlist:
            distance = edit_distance(word.lower(), wordlist_word)
            if distance <= 2:
                similar.append((wordlist_word, distance))
        
        # Sort by distance
        similar.sort(key=lambda x: x[1])
        return [word for word, _ in similar[:5]]

# Secure storage and retrieval
class SecureMnemonicStorage:
    def __init__(self, master_password):
        self.master_password = master_password
        self.bip39 = BIP39()
    
    def encrypt_mnemonic(self, mnemonic):
        """Encrypt mnemonic for secure storage"""
        from metamui_crypto import ChaCha20Poly1305, Argon2
        
        # Derive key from master password
        argon2 = Argon2(memory_cost=65536, time_cost=3)
        salt = os.urandom(32)
        key = argon2.derive(self.master_password.encode(), salt)
        
        # Encrypt mnemonic
        cipher = ChaCha20Poly1305(key)
        nonce = os.urandom(12)
        ciphertext = cipher.encrypt(mnemonic.encode(), nonce)
        
        # Return encrypted data with metadata
        return {
            'salt': salt,
            'nonce': nonce,
            'ciphertext': ciphertext,
            'algorithm': 'ChaCha20Poly1305',
            'kdf': 'Argon2id'
        }
    
    def decrypt_mnemonic(self, encrypted_data):
        """Decrypt stored mnemonic"""
        from metamui_crypto import ChaCha20Poly1305, Argon2
        
        # Derive key from master password
        argon2 = Argon2(memory_cost=65536, time_cost=3)
        key = argon2.derive(
            self.master_password.encode(), 
            encrypted_data['salt']
        )
        
        # Decrypt mnemonic
        cipher = ChaCha20Poly1305(key)
        plaintext = cipher.decrypt(
            encrypted_data['ciphertext'],
            encrypted_data['nonce']
        )
        
        mnemonic = plaintext.decode()
        
        # Validate recovered mnemonic
        if not self.bip39.validate_mnemonic(mnemonic):
            raise ValueError("Decrypted mnemonic is invalid")
        
        return mnemonic
    
    def create_backup_shares(self, mnemonic, threshold=2, shares=3):
        """Create Shamir's Secret Sharing backup"""
        # Convert mnemonic to entropy
        entropy = self.bip39.mnemonic_to_entropy(mnemonic)
        
        # Simple secret sharing (use proper Shamir's in production)
        shares_data = []
        
        for i in range(shares):
            # Generate random share
            share = os.urandom(len(entropy))
            shares_data.append({
                'share_id': i + 1,
                'share_data': share,
                'threshold': threshold,
                'total_shares': shares
            })
        
        # Adjust last share to make reconstruction work
        # (This is simplified - use proper Shamir's Secret Sharing)
        
        return shares_data

Implementation Details

# BIP39 core implementation
class BIP39Core:
    def __init__(self, language='english'):
        self.language = language
        self.wordlist = self.load_wordlist(language)
        self.word_to_index = {word: i for i, word in enumerate(self.wordlist)}
    
    def load_wordlist(self, language):
        """Load BIP39 wordlist for specified language"""
        # Simplified - in practice, load from files
        if language == 'english':
            return [
                'abandon', 'ability', 'able', 'about', 'above', 'absent',
                # ... 2048 words total
            ]
        else:
            raise ValueError(f"Unsupported language: {language}")
    
    def generate_entropy(self, strength):
        """Generate cryptographically secure entropy"""
        if strength not in [128, 160, 192, 224, 256]:
            raise ValueError("Invalid strength")
        
        return os.urandom(strength // 8)
    
    def entropy_to_mnemonic(self, entropy):
        """Convert entropy to mnemonic phrase"""
        if len(entropy) not in [16, 20, 24, 28, 32]:
            raise ValueError("Invalid entropy length")
        
        # Calculate checksum
        from metamui_crypto import SHA256
        hash_bytes = SHA256().hash(entropy)
        checksum_bits = len(entropy) * 8 // 32
        checksum = hash_bytes[0] >> (8 - checksum_bits)
        
        # Combine entropy and checksum
        entropy_int = int.from_bytes(entropy, 'big')
        combined = (entropy_int << checksum_bits) | checksum
        
        # Convert to mnemonic
        word_count = (len(entropy) * 8 + checksum_bits) // 11
        words = []
        
        for i in range(word_count):
            word_index = combined & 0x7FF  # 11 bits
            words.append(self.wordlist[word_index])
            combined >>= 11
        
        return ' '.join(reversed(words))
    
    def mnemonic_to_entropy(self, mnemonic):
        """Convert mnemonic phrase to entropy"""
        words = mnemonic.strip().split()
        
        if len(words) not in [12, 15, 18, 21, 24]:
            raise ValueError("Invalid mnemonic length")
        
        # Convert words to indices
        try:
            indices = [self.word_to_index[word] for word in words]
        except KeyError as e:
            raise ValueError(f"Invalid word: {e}")
        
        # Combine indices
        combined = 0
        for index in indices:
            combined = (combined << 11) | index
        
        # Split entropy and checksum
        checksum_bits = len(words) // 3
        entropy_bits = len(words) * 11 - checksum_bits
        
        entropy_int = combined >> checksum_bits
        checksum = combined & ((1 << checksum_bits) - 1)
        
        # Convert to bytes
        entropy = entropy_int.to_bytes(entropy_bits // 8, 'big')
        
        # Verify checksum
        from metamui_crypto import SHA256
        hash_bytes = SHA256().hash(entropy)
        expected_checksum = hash_bytes[0] >> (8 - checksum_bits)
        
        if checksum != expected_checksum:
            raise ValueError("Invalid mnemonic checksum")
        
        return entropy
    
    def mnemonic_to_seed(self, mnemonic, passphrase=""):
        """Convert mnemonic to seed using PBKDF2"""
        from metamui_crypto import PBKDF2, SHA512
        
        # Normalize mnemonic and passphrase
        mnemonic_bytes = mnemonic.encode('utf-8')
        salt = ("mnemonic" + passphrase).encode('utf-8')
        
        # PBKDF2 with 2048 iterations
        pbkdf2 = PBKDF2(iterations=2048, hash_function=SHA512())
        seed = pbkdf2.derive(mnemonic_bytes, salt, key_length=64)
        
        return seed
    
    def validate_mnemonic(self, mnemonic):
        """Validate mnemonic phrase"""
        try:
            self.mnemonic_to_entropy(mnemonic)
            return True
        except ValueError:
            return False

Security Considerations

Best Practices

  1. Entropy Generation
  2. Mnemonic Handling
  3. Backup and Storage
  4. Passphrase Usage

Common Pitfalls

# DON'T: Use weak entropy sources
import random
weak_entropy = bytes([random.randint(0, 255) for _ in range(32)])
# WRONG: Python's random is not cryptographically secure
mnemonic = bip39.entropy_to_mnemonic(weak_entropy)

# DO: Use cryptographically secure entropy
strong_entropy = os.urandom(32)  # Cryptographically secure
mnemonic = bip39.entropy_to_mnemonic(strong_entropy)

# DON'T: Store mnemonics in plain text
# with open("mnemonic.txt", "w") as f:
#     f.write(mnemonic)  # WRONG: Plain text storage

# DO: Use secure storage or physical backup
# Write on paper and store in safe, or use encrypted storage
encrypted_mnemonic = secure_storage.encrypt_mnemonic(mnemonic)

# DON'T: Skip mnemonic validation
def unsafe_restore(mnemonic):
    # WRONG: No validation
    return bip39.mnemonic_to_seed(mnemonic)

# DO: Always validate mnemonics
def safe_restore(mnemonic):
    if not bip39.validate_mnemonic(mnemonic):
        raise ValueError("Invalid mnemonic")
    return bip39.mnemonic_to_seed(mnemonic)

# DON'T: Ignore checksum errors
def ignore_checksum(words):
    # WRONG: Bypassing checksum validation
    return ' '.join(words)

# DO: Respect checksum validation
def validate_checksum(words):
    mnemonic = ' '.join(words)
    if not bip39.validate_mnemonic(mnemonic):
        raise ValueError("Checksum validation failed")
    return mnemonic

Performance Characteristics

Benchmarks

Operation Time (μs) Throughput
Generate Mnemonic (12 words) 45 22,222 ops/sec
Generate Mnemonic (24 words) 48 20,833 ops/sec
Validate Mnemonic 52 19,230 ops/sec
Mnemonic to Seed 2,100 476 ops/sec
Entropy to Mnemonic 15 66,667 ops/sec

Performance Optimization

# Batch mnemonic generation
class BatchBIP39:
    def __init__(self):
        self.bip39 = BIP39()
    
    def generate_batch(self, count, strength=256):
        """Generate multiple mnemonics efficiently"""
        mnemonics = []
        
        for _ in range(count):
            entropy = os.urandom(strength // 8)
            mnemonic = self.bip39.entropy_to_mnemonic(entropy)
            mnemonics.append(mnemonic)
        
        return mnemonics

# Cached wordlist for faster lookups
class OptimizedBIP39:
    def __init__(self, language='english'):
        self.bip39 = BIP39(language)
        # Pre-compute reverse lookup
        self.word_to_index = {
            word: i for i, word in enumerate(self.bip39.wordlist)
        }
    
    def fast_validate(self, mnemonic):
        """Faster mnemonic validation with cached lookups"""
        words = mnemonic.split()
        
        # Quick word validation
        for word in words:
            if word not in self.word_to_index:
                return False
        
        # Full validation
        return self.bip39.validate_mnemonic(mnemonic)

# Parallel seed generation
from concurrent.futures import ThreadPoolExecutor

def parallel_seed_generation(mnemonics, passphrase=""):
    """Generate seeds for multiple mnemonics in parallel"""
    def generate_seed(mnemonic):
        bip39 = BIP39()
        return bip39.mnemonic_to_seed(mnemonic, passphrase)
    
    with ThreadPoolExecutor() as executor:
        seeds = list(executor.map(generate_seed, mnemonics))
    
    return seeds

Use Cases

1. Cryptocurrency Wallet

class CryptoWallet:
    def __init__(self):
        self.bip39 = BIP39()
        self.mnemonic = None
        self.master_seed = None
        self.accounts = {}
    
    def create_wallet(self, passphrase=""):
        """Create new cryptocurrency wallet"""
        # Generate 24-word mnemonic for maximum security
        self.mnemonic = self.bip39.generate_mnemonic(strength=256)
        
        # Derive master seed
        self.master_seed = self.bip39.mnemonic_to_seed(
            self.mnemonic, 
            passphrase
        )
        
        # Create default account
        self.create_account("default", 0)
        
        return {
            'mnemonic': self.mnemonic,
            'instructions': [
                'Write down these 24 words in order',
                'Store them securely offline',
                'Never share with anyone',
                'Test recovery before depositing funds'
            ]
        }
    
    def restore_wallet(self, mnemonic, passphrase=""):
        """Restore wallet from mnemonic"""
        if not self.bip39.validate_mnemonic(mnemonic):
            raise ValueError("Invalid mnemonic phrase")
        
        self.mnemonic = mnemonic
        self.master_seed = self.bip39.mnemonic_to_seed(mnemonic, passphrase)
        
        # Restore default account
        self.create_account("default", 0)
        
        return {'status': 'restored'}
    
    def create_account(self, name, account_index):
        """Create account using BIP44 derivation"""
        if not self.master_seed:
            raise ValueError("Wallet not initialized")
        
        # Simplified BIP44 key derivation
        from metamui_crypto import HMAC, SHA512
        
        # Derive account key
        hmac = HMAC(self.master_seed, SHA512())
        account_path = f"m/44'/0'/{account_index}'"
        account_key = hmac.compute(account_path.encode())
        
        self.accounts[name] = {
            'index': account_index,
            'key': account_key[:32],  # 256-bit key
            'addresses': {}
        }
        
        return self.accounts[name]
    
    def generate_address(self, account_name, address_index):
        """Generate address for account"""
        if account_name not in self.accounts:
            raise ValueError("Account not found")
        
        account = self.accounts[account_name]
        
        # Derive address key
        from metamui_crypto import HMAC, SHA256
        hmac = HMAC(account['key'], SHA256())
        address_key = hmac.compute(f"address:{address_index}".encode())
        
        # Generate address (simplified)
        address = f"addr_{address_key[:8].hex()}"
        
        account['addresses'][address_index] = {
            'address': address,
            'private_key': address_key,
            'balance': 0
        }
        
        return address

2. Hardware Wallet Interface

class HardwareWalletInterface:
    def __init__(self):
        self.bip39 = BIP39()
        self.device_connected = False
        self.device_info = None
    
    def connect_device(self):
        """Connect to hardware wallet"""
        # Simulate device connection
        self.device_connected = True
        self.device_info = {
            'model': 'SecureWallet Pro',
            'firmware': '2.1.0',
            'initialized': False
        }
        
        return self.device_info
    
    def initialize_device(self, strength=256):
        """Initialize new hardware wallet"""
        if not self.device_connected:
            raise ValueError("Device not connected")
        
        # Device generates entropy internally
        device_entropy = os.urandom(strength // 8)
        
        # Convert to mnemonic
        mnemonic = self.bip39.entropy_to_mnemonic(device_entropy)
        
        # Device would display mnemonic for user to write down
        self.device_info['initialized'] = True
        
        return {
            'mnemonic': mnemonic,
            'display_instructions': [
                'Write down all words in order',
                'Verify each word carefully',
                'Store backup securely',
                'Confirm on device when ready'
            ]
        }
    
    def restore_device(self, mnemonic):
        """Restore hardware wallet from mnemonic"""
        if not self.device_connected:
            raise ValueError("Device not connected")
        
        # Validate mnemonic
        if not self.bip39.validate_mnemonic(mnemonic):
            raise ValueError("Invalid mnemonic")
        
        # Device would verify and store mnemonic securely
        self.device_info['initialized'] = True
        
        return {'status': 'restored'}
    
    def sign_transaction(self, transaction_data, derivation_path):
        """Sign transaction using hardware wallet"""
        if not self.device_info.get('initialized'):
            raise ValueError("Device not initialized")
        
        # Device would derive key and sign transaction
        # This is simplified - real implementation would be more complex
        
        return {
            'signature': 'device_signature_' + os.urandom(8).hex(),
            'signed_transaction': transaction_data
        }
    
    def export_public_key(self, derivation_path):
        """Export public key for address generation"""
        if not self.device_info.get('initialized'):
            raise ValueError("Device not initialized")
        
        # Device would derive and return public key
        return {
            'public_key': 'pubkey_' + os.urandom(16).hex(),
            'derivation_path': derivation_path
        }

3. Multi-Signature Wallet

class MultiSigWallet:
    def __init__(self, threshold, total_signers):
        self.bip39 = BIP39()
        self.threshold = threshold
        self.total_signers = total_signers
        self.signers = {}
        self.wallet_seed = None
    
    def setup_signer(self, signer_id, mnemonic=None):
        """Setup individual signer"""
        if mnemonic is None:
            # Generate new mnemonic for signer
            mnemonic = self.bip39.generate_mnemonic(strength=256)
        else:
            # Validate provided mnemonic
            if not self.bip39.validate_mnemonic(mnemonic):
                raise ValueError("Invalid mnemonic")
        
        # Derive signer seed
        seed = self.bip39.mnemonic_to_seed(mnemonic)
        
        # Store signer info
        self.signers[signer_id] = {
            'mnemonic': mnemonic,
            'seed': seed,
            'public_key': self.derive_public_key(seed)
        }
        
        return {
            'signer_id': signer_id,
            'mnemonic': mnemonic,
            'public_key': self.signers[signer_id]['public_key']
        }
    
    def create_multisig_wallet(self):
        """Create multi-signature wallet"""
        if len(self.signers) < self.total_signers:
            raise ValueError("Not enough signers configured")
        
        # Combine public keys to create wallet
        public_keys = [
            signer['public_key'] for signer in self.signers.values()
        ]
        
        # Create wallet identifier
        from metamui_crypto import SHA256
        hasher = SHA256()
        for pk in sorted(public_keys):
            hasher.update(pk)
        wallet_id = hasher.finalize()[:8].hex()
        
        return {
            'wallet_id': wallet_id,
            'threshold': self.threshold,
            'total_signers': self.total_signers,
            'public_keys': public_keys
        }
    
    def derive_public_key(self, seed):
        """Derive public key from seed"""
        from metamui_crypto import HMAC, SHA256
        hmac = HMAC(seed, SHA256())
        private_key = hmac.compute(b"multisig_key_derivation")
        
        # Simplified public key derivation
        return f"pubkey_{private_key[:16].hex()}"
    
    def backup_recovery_info(self):
        """Create recovery information for wallet"""
        recovery_info = {
            'wallet_type': 'multisig',
            'threshold': self.threshold,
            'total_signers': self.total_signers,
            'signer_mnemonics': {},
            'instructions': [
                f'This is a {self.threshold}-of-{self.total_signers} multisig wallet',
                f'You need {self.threshold} mnemonics to recover funds',
                'Store each mnemonic separately and securely',
                'Test recovery process before using'
            ]
        }
        
        for signer_id, signer in self.signers.items():
            recovery_info['signer_mnemonics'][signer_id] = signer['mnemonic']
        
        return recovery_info

4. Enterprise Key Management

class EnterpriseKeyManager:
    def __init__(self):
        self.bip39 = BIP39()
        self.departments = {}
        self.audit_log = []
    
    def create_department_wallet(self, department_name, authorized_users):
        """Create department-specific wallet"""
        # Generate master mnemonic for department
        master_mnemonic = self.bip39.generate_mnemonic(strength=256)
        master_seed = self.bip39.mnemonic_to_seed(master_mnemonic)
        
        # Create department entry
        self.departments[department_name] = {
            'master_mnemonic': master_mnemonic,
            'master_seed': master_seed,
            'authorized_users': authorized_users,
            'user_keys': {},
            'created_at': time.time()
        }
        
        # Generate user-specific keys
        for user_id in authorized_users:
            user_key = self.derive_user_key(department_name, user_id)
            self.departments[department_name]['user_keys'][user_id] = user_key
        
        # Log creation
        self.audit_log.append({
            'action': 'create_department',
            'department': department_name,
            'users': authorized_users,
            'timestamp': time.time()
        })
        
        return {
            'department': department_name,
            'master_mnemonic': master_mnemonic,
            'user_count': len(authorized_users),
            'backup_required': True
        }
    
    def derive_user_key(self, department_name, user_id):
        """Derive user-specific key from department master"""
        if department_name not in self.departments:
            raise ValueError("Department not found")
        
        dept = self.departments[department_name]
        
        # Derive user key from master seed
        from metamui_crypto import HKDF, SHA256
        hkdf = HKDF(hash_function=SHA256())
        
        user_key = hkdf.derive(
            ikm=dept['master_seed'],
            info=f"user:{user_id}:dept:{department_name}".encode(),
            length=32
        )
        
        return user_key
    
    def create_backup_shares(self, department_name, threshold=3, shares=5):
        """Create Shamir's Secret Sharing backup for department"""
        if department_name not in self.departments:
            raise ValueError("Department not found")
        
        mnemonic = self.departments[department_name]['master_mnemonic']
        entropy = self.bip39.mnemonic_to_entropy(mnemonic)
        
        # Create backup shares (simplified)
        backup_shares = []
        for i in range(shares):
            share_data = {
                'share_id': i + 1,
                'department': department_name,
                'threshold': threshold,
                'total_shares': shares,
                'share_mnemonic': self.create_share_mnemonic(entropy, i, threshold)
            }
            backup_shares.append(share_data)
        
        # Log backup creation
        self.audit_log.append({
            'action': 'create_backup',
            'department': department_name,
            'threshold': threshold,
            'shares': shares,
            'timestamp': time.time()
        })
        
        return backup_shares
    
    def create_share_mnemonic(self, entropy, share_index, threshold):
        """Create mnemonic for backup share"""
        # Simplified share creation (use proper Shamir's in production)
        from metamui_crypto import HMAC, SHA256
        
        hmac = HMAC(entropy, SHA256())
        share_entropy = hmac.compute(f"share:{share_index}:threshold:{threshold}".encode())
        
        return self.bip39.entropy_to_mnemonic(share_entropy)
    
    def audit_report(self):
        """Generate audit report"""
        return {
            'total_departments': len(self.departments),
            'total_users': sum(
                len(dept['authorized_users']) 
                for dept in self.departments.values()
            ),
            'audit_events': len(self.audit_log),
            'recent_activity': self.audit_log[-10:] if self.audit_log else []
        }

Comparison with Other Methods

BIP39 vs Raw Keys

Feature BIP39 Raw Keys
Human Readable Yes No
Error Detection Yes No
Backup Friendly Yes No
Standardization Wide Limited
Recovery Easy Difficult

BIP39 vs Other Standards

Standard Purpose Word Count Checksum
BIP39 Crypto wallets 12-24 Yes
Electrum Electrum wallet 12-24 Yes
Monero Monero wallet 25 Yes
Diceware Passwords Variable No

When to Use BIP39

# Use BIP39 for cryptocurrency applications
if building_crypto_wallet:
    key_backup = BIP39()

# Use BIP39 for user-friendly key backup
if need_human_readable_backup:
    mnemonic = bip39.generate_mnemonic()

# Use BIP39 for standardized recovery
if need_cross_wallet_compatibility:
    # BIP39 is widely supported
    backup_system = BIP39()

Migration Guide

From Raw Keys to BIP39

# Before: Raw key backup (difficult)
private_key = os.urandom(32)
# User must backup 64 hex characters: difficult and error-prone

# After: BIP39 mnemonic (easy)
entropy = os.urandom(32)
mnemonic = bip39.entropy_to_mnemonic(entropy)
# User backs up 24 words: much easier and has error detection

From Custom Wordlist to BIP39

# Before: Custom word encoding
custom_words = ["apple", "banana", "cherry"]  # Custom list
# Non-standard, no error detection

# After: BIP39 standard
bip39 = BIP39()
mnemonic = bip39.generate_mnemonic()
# Standardized, error detection, wide support

Test Vectors

BIP39 Test Vectors

# Test Vector 1: 128-bit entropy
entropy = bytes.fromhex("00000000000000000000000000000000")
expected_mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"

mnemonic = bip39.entropy_to_mnemonic(entropy)
assert mnemonic == expected_mnemonic

# Verify round trip
recovered_entropy = bip39.mnemonic_to_entropy(mnemonic)
assert recovered_entropy == entropy

# Test Vector 2: 256-bit entropy
entropy_256 = bytes.fromhex("0000000000000000000000000000000000000000000000000000000000000000")
expected_mnemonic_256 = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"

mnemonic_256 = bip39.entropy_to_mnemonic(entropy_256)
assert mnemonic_256 == expected_mnemonic_256

# Test Vector 3: Seed generation
test_mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
expected_seed = bytes.fromhex("c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04")

seed = bip39.mnemonic_to_seed(test_mnemonic, passphrase="TREZOR")
assert seed == expected_seed

Validation Test Vectors

# Valid mnemonics
valid_mnemonics = [
    "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
    "legal winner thank year wave sausage worth useful legal winner thank yellow",
    "letter advice cage absurd amount doctor acoustic avoid letter advice cage above"
]

for mnemonic in valid_mnemonics:
    assert bip39.validate_mnemonic(mnemonic)

# Invalid mnemonics (bad checksum)
invalid_mnemonics = [
    "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon",
    "legal winner thank year wave sausage worth useful legal winner thank winner",
    "letter advice cage absurd amount doctor acoustic avoid letter advice cage cage"
]

for mnemonic in invalid_mnemonics:
    assert not bip39.validate_mnemonic(mnemonic)

References