42 lines
1.2 KiB
TypeScript
42 lines
1.2 KiB
TypeScript
import { randomBytes, scryptSync, timingSafeEqual } from 'node:crypto'
|
|
|
|
const PASSWORD_HASH_PREFIX = 'scrypt'
|
|
const DERIVED_KEY_LENGTH = 64
|
|
|
|
/**
|
|
* Gera hash de senha com scrypt e salt aleatório.
|
|
*
|
|
* @param password Senha em texto puro.
|
|
* @returns Hash no formato `scrypt$salt$hash`.
|
|
*/
|
|
export function hashPassword(password: string): string {
|
|
const salt = randomBytes(16).toString('base64url')
|
|
const derivedKey = scryptSync(password, salt, DERIVED_KEY_LENGTH).toString('base64url')
|
|
|
|
return `${PASSWORD_HASH_PREFIX}$${salt}$${derivedKey}`
|
|
}
|
|
|
|
/**
|
|
* Compara senha em texto puro com hash salvo no banco.
|
|
*
|
|
* @param password Senha informada no login.
|
|
* @param encodedHash Hash armazenado no banco.
|
|
* @returns `true` quando a senha confere; caso contrário, `false`.
|
|
*/
|
|
export function verifyPassword(password: string, encodedHash: string): boolean {
|
|
const [prefix, salt, storedHash] = encodedHash.split('$')
|
|
|
|
if (prefix !== PASSWORD_HASH_PREFIX || !salt || !storedHash) {
|
|
return false
|
|
}
|
|
|
|
const derivedKey = scryptSync(password, salt, DERIVED_KEY_LENGTH)
|
|
const storedKeyBuffer = Buffer.from(storedHash, 'base64url')
|
|
|
|
if (derivedKey.length !== storedKeyBuffer.length) {
|
|
return false
|
|
}
|
|
|
|
return timingSafeEqual(derivedKey, storedKeyBuffer)
|
|
}
|