import { generateMnemonic, validateMnemonic } from "@scure/bip39"; import { wordlist } from "@scure/bip39/wordlists/english.js"; import { BufferUtils } from "libnexa-ts"; export function generateNewMnemonic(size: 12 | 24 = 12): string { return generateMnemonic(wordlist, size === 24 ? 256 : 128); } export function isMnemonicValid(mnemonic: string): boolean { return validateMnemonic(mnemonic, wordlist); } async function deriveKey(secret: Uint8Array, salt: Uint8Array): Promise { const baseKey = await crypto.subtle.importKey( 'raw', new Uint8Array(secret), 'PBKDF2', false, ['deriveKey'] ); return crypto.subtle.deriveKey( { name: 'PBKDF2', salt: new Uint8Array(salt), iterations: 210_000, hash: 'SHA-256', }, baseKey, { name: 'AES-GCM', length: 256, }, false, ['encrypt', 'decrypt'] ); } export async function encryptMnemonic(phrase: string, password: string): Promise { const secret = BufferUtils.utf8ToBuffer(password); const salt = BufferUtils.getRandomBuffer(16); const iv = BufferUtils.getRandomBuffer(12); const data = BufferUtils.utf8ToBuffer(phrase); const key = await deriveKey(secret, salt); const cipher = await crypto.subtle.encrypt( { name: 'AES-GCM', iv: new Uint8Array(iv), }, key, new Uint8Array(data) ); const encBuf = BufferUtils.concat([salt, iv, new Uint8Array(cipher)]); return BufferUtils.bufferToBase64(encBuf); } async function decryptMnemonic(encSeed: string, password: string): Promise { const secret = BufferUtils.utf8ToBuffer(password); const encBuf = BufferUtils.base64ToBuffer(encSeed); const salt = encBuf.slice(0, 16); const iv = encBuf.slice(16, 28); const cipher = encBuf.slice(28); const key = await deriveKey(secret, salt); const data = await crypto.subtle.decrypt( { name: 'AES-GCM', iv, }, key, cipher ); return BufferUtils.bufferToUtf8(new Uint8Array(data)); } export async function validateAndDecryptMnemonic(encSeed: string, password: string): Promise { try { if (encSeed) { const decMn = await decryptMnemonic(encSeed, password); if (decMn && isMnemonicValid(decMn)) { return decMn; } } return false; } catch { return false; } }