crypto-shelf
Version:
Library collection for password hashing, HMAC-based signature generation, and symmetric encryption. Build on top of Node's crypto module
62 lines (48 loc) • 1.45 kB
JavaScript
import {
randomBytes,
scrypt,
timingSafeEqual,
} from 'node:crypto';
import { promisify } from 'node:util';
import assert from 'node:assert/strict';
import Cursor from './utils/Cursor.js';
import { merge } from './utils/helper.js';
const scryptAsync = promisify(scrypt);
export const defaults = {
keyLength: 48,
saltLength: 16,
encoding: 'base64url',
};
export async function hashPassword(password, options = {}) {
const { keyLength, saltLength, encoding } = merge(defaults, options);
assert(keyLength > 0 && keyLength < 256, '"keyLength" must be between 1 and 255');
assert(saltLength > 0 && saltLength < 256, '"saltLength" must be between 1 and 255');
password = password.normalize('NFKC');
const salt = randomBytes(saltLength);
const key = await scryptAsync(
password,
salt,
keyLength,
);
const hash = Cursor.new(2 + saltLength + keyLength)
.writeUInt8(saltLength)
.writeUInt8(keyLength)
.write(salt)
.write(key);
return encoding ? hash.toString(encoding) : hash.buffer;
}
export async function comparePassword(clear, hash, options = {}) {
const { encoding } = merge(defaults, options);
clear = clear.normalize('NFKC');
hash = Cursor.from(hash, encoding);
const saltLength = hash.readUInt8();
const keyLength = hash.readUInt8();
const salt = hash.read(saltLength);
const key = hash.read();
const compare = await scryptAsync(
clear,
salt,
keyLength,
);
return timingSafeEqual(compare, key);
}