Node Crypto Scrypt Password Hashing
Hash passwords with the built-in scrypt using random per-user salts, explicit cost parameters, and timing-safe comparison on verification.
Code
import { randomBytes, scrypt, timingSafeEqual } from 'node:crypto';
import { promisify } from 'node:util';
const scryptAsync = promisify(scrypt);
const PARAMS = { N: 16384, r: 8, p: 1, keylen: 64 };
async function hashPassword(password) {
const salt = randomBytes(16);
const derived = await scryptAsync(password.normalize('NFKC'), salt, PARAMS.keylen, PARAMS);
return `scrypt:${PARAMS.N}:${PARAMS.r}:${PARAMS.p}:${salt.toString('base64')}:\
${derived.toString('base64')}`;
}
async function verifyPassword(password, stored) {
const [scheme, N, r, p, saltB64, hashB64] = stored.split(':');
if (scheme !== 'scrypt') return false;
const salt = Buffer.from(saltB64, 'base64');
const expected = Buffer.from(hashB64, 'base64');
const actual = await scryptAsync(password.normalize('NFKC'), salt, expected.length, {
N: Number(N), r: Number(r), p: Number(p),
});
return actual.length === expected.length && timingSafeEqual(actual, expected);
}
const record = await hashPassword('correct horse battery staple');
console.log(await verifyPassword('wrong password', record));
console.log(await verifyPassword('correct horse battery staple', record));Report a problem with this exampleOpens a pre-filled issue on GitHub.