import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { HDKey } from '@scure/bip32'; /** * NIP-06: Basic key derivation from mnemonic seed phrase * https://github.com/nostr-protocol/nips/blob/master/06.md * * Uses BIP39 for mnemonic generation and BIP32 for key derivation * Derivation path: m/44'/1237'/account'/0/0 */ export const NIP06_PATH = "m/44'/1237'/0'/0/0"; /** * Generate a new mnemonic seed phrase * @param strength - 128 for 12 words, 256 for 24 words (default: 128) * @returns 12 or 24 word mnemonic phrase */ export function generateMnemonicPhrase(strength: 128 | 256 = 128): string { return generateMnemonic(wordlist, strength); } /** * Validate a mnemonic seed phrase * @param mnemonic - Space-separated words * @returns true if valid, false otherwise */ export function validateMnemonicPhrase(mnemonic: string): boolean { try { return validateMnemonic(mnemonic, wordlist); } catch { return false; } } /** * Derive a private key from a mnemonic phrase using NIP-06 * @param mnemonic - Space-separated mnemonic phrase * @param account - Account index (default: 0) * @returns 32-byte private key as Uint8Array */ export function privateKeyFromMnemonic(mnemonic: string, account: number = 0): Uint8Array { if (!validateMnemonicPhrase(mnemonic)) { throw new Error('Invalid mnemonic phrase'); } // Generate seed from mnemonic const seed = mnemonicToSeedSync(mnemonic); // Derive key using NIP-06 path const path = account === 0 ? NIP06_PATH : `m/44'/1237'/${account}'/0/0`; const hdKey = HDKey.fromMasterSeed(seed); const derivedKey = hdKey.derive(path); if (!derivedKey.privateKey) { throw new Error('Failed to derive private key'); } return derivedKey.privateKey; } /** * Derive multiple accounts from a single mnemonic * @param mnemonic - Space-separated mnemonic phrase * @param count - Number of accounts to derive (default: 1) * @returns Array of 32-byte private keys */ export function deriveMultipleAccounts(mnemonic: string, count: number = 1): Uint8Array[] { const accounts: Uint8Array[] = []; for (let i = 0; i < count; i++) { accounts.push(privateKeyFromMnemonic(mnemonic, i)); } return accounts; } /** * Normalize mnemonic phrase (trim, lowercase, normalize whitespace) * @param mnemonic - Raw mnemonic input * @returns Normalized mnemonic */ export function normalizeMnemonic(mnemonic: string): string { return mnemonic .trim() .toLowerCase() .replace(/\s+/g, ' '); // Normalize whitespace to single spaces } /** * Split mnemonic into word array * @param mnemonic - Space-separated mnemonic phrase * @returns Array of words */ export function mnemonicToWords(mnemonic: string): string[] { return normalizeMnemonic(mnemonic).split(' '); } /** * Join word array into mnemonic string * @param words - Array of mnemonic words * @returns Space-separated mnemonic phrase */ export function wordsToMnemonic(words: string[]): string { return words.join(' '); }