Files
soul/lib/password.ts

57 lines
1.8 KiB
TypeScript
Raw Normal View History

/**
* Web /
* 使 Node crypto.scrypt saltHex:hashHex
*/
import { scryptSync, timingSafeEqual, randomFillSync } from 'crypto'
const SALT_LEN = 16
const KEYLEN = 32
function bufferToHex(buf: Buffer): string {
return buf.toString('hex')
}
function hexToBuffer(hex: string): Buffer {
return Buffer.from(hex, 'hex')
}
/**
*
* 格式: saltHex:hashHex 97 VARCHAR(100)
* verifyPassword trim//
*/
export function hashPassword(plain: string): string {
const trimmed = String(plain).trim()
const salt = Buffer.allocUnsafe(SALT_LEN)
randomFillSync(salt)
const hash = scryptSync(trimmed, salt, KEYLEN, { N: 16384, r: 8, p: 1 })
return bufferToHex(salt) + ':' + bufferToHex(hash)
}
/**
* salt:hash
* hashPassword trim
*/
export function verifyPassword(plain: string, stored: string | null | undefined): boolean {
const trimmed = String(plain).trim()
if (stored == null || stored === '') {
return trimmed === ''
}
if (stored.includes(':')) {
const [saltHex, hashHex] = stored.split(':')
if (!saltHex || !hashHex || saltHex.length !== SALT_LEN * 2 || hashHex.length !== KEYLEN * 2) {
return false
}
try {
const salt = hexToBuffer(saltHex)
const expected = hexToBuffer(hashHex)
const derived = scryptSync(trimmed, salt, KEYLEN, { N: 16384, r: 8, p: 1 })
return derived.length === expected.length && timingSafeEqual(derived, expected)
} catch {
return false
}
}
return trimmed === stored
}