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.
| 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 |
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
# 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
# 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
# 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
| 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 |
# 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
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
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
}
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
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 []
}
| Feature | BIP39 | Raw Keys |
|---|---|---|
| Human Readable | Yes | No |
| Error Detection | Yes | No |
| Backup Friendly | Yes | No |
| Standardization | Wide | Limited |
| Recovery | Easy | Difficult |
| 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 |
# 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()
# 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
# 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 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
# 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)