import * as plugins from './plugins.js'; const HASH_PREFIX = 'scrypt:v1'; export class PasswordHasher { public static async hashPassword(passwordArg: string): Promise { const salt = plugins.crypto.randomBytes(16).toString('base64url'); const key = await this.scrypt(passwordArg, salt); return `${HASH_PREFIX}:${salt}:${key.toString('base64url')}`; } public static async verifyPassword(passwordArg: string, passwordHashArg?: string): Promise { if (!passwordHashArg) { return false; } const [prefix, version, salt, storedKey] = passwordHashArg.split(':'); if (`${prefix}:${version}` !== HASH_PREFIX || !salt || !storedKey) { return false; } const candidate = await this.scrypt(passwordArg, salt); const stored = Buffer.from(storedKey, 'base64url'); if (candidate.byteLength !== stored.byteLength) { return false; } return plugins.crypto.timingSafeEqual(candidate, stored); } private static async scrypt(passwordArg: string, saltArg: string): Promise { return new Promise((resolve, reject) => { plugins.crypto.scrypt(passwordArg, saltArg, 64, (error, derivedKey) => { if (error) { reject(error); return; } resolve(derivedKey as Buffer); }); }); } }