import crypto from 'crypto' // scrypt params: N=16384 gives ~100ms on modern hardware — deliberate slowdown const SCRYPT_N = 16384 const SCRYPT_R = 8 const SCRYPT_P = 1 const KEY_LEN = 64 /** Hashes a password using scrypt. Returns the hex hash and a random hex salt. */ export async function hashPassword(password: string): Promise<{ hash: string; salt: string }> { const salt = crypto.randomBytes(16).toString('hex') const hash = await deriveKey(password, salt) return { hash, salt } } /** Constant-time comparison to prevent timing attacks. */ export async function verifyPassword( password: string, hash: string, salt: string, ): Promise { const derived = await deriveKey(password, salt) const a = Buffer.from(derived, 'hex') const b = Buffer.from(hash, 'hex') if (a.length !== b.length) return false return crypto.timingSafeEqual(a, b) } function deriveKey(password: string, salt: string): Promise { return new Promise((resolve, reject) => { crypto.scrypt(password, salt, KEY_LEN, { N: SCRYPT_N, r: SCRYPT_R, p: SCRYPT_P }, (err, key) => { if (err) reject(err) else resolve(key.toString('hex')) }) }) }