30 lines
1.0 KiB
TypeScript
30 lines
1.0 KiB
TypeScript
import { randomBytes, scryptSync, timingSafeEqual } from "node:crypto";
|
|
|
|
const SCRYPT_KEY_LENGTH = 64;
|
|
|
|
export function normalizePhone(raw: string) {
|
|
const trimmed = (raw ?? "").trim();
|
|
if (!trimmed) return "";
|
|
const hasPlus = trimmed.startsWith("+");
|
|
const digits = trimmed.replace(/\D/g, "");
|
|
if (!digits) return "";
|
|
return `${hasPlus ? "+" : ""}${digits}`;
|
|
}
|
|
|
|
export function hashPassword(password: string) {
|
|
const salt = randomBytes(16).toString("base64url");
|
|
const digest = scryptSync(password, salt, SCRYPT_KEY_LENGTH).toString("base64url");
|
|
return `scrypt$${salt}$${digest}`;
|
|
}
|
|
|
|
export function verifyPassword(password: string, encodedHash: string) {
|
|
const [algo, salt, digest] = (encodedHash ?? "").split("$");
|
|
if (algo !== "scrypt" || !salt || !digest) return false;
|
|
|
|
const actual = scryptSync(password, salt, SCRYPT_KEY_LENGTH);
|
|
const expected = Buffer.from(digest, "base64url");
|
|
if (actual.byteLength !== expected.byteLength) return false;
|
|
|
|
return timingSafeEqual(actual, expected);
|
|
}
|