Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | 2x 2x 2x 2x 2x 2x 2x 8x 8x 2x 8x 2x 8x 1x 8x 2x 8x 2x 12x 12x 12x 2x 28x 28x 2x 26x 26x 26x 26x | import { scrypt, randomBytes, timingSafeEqual } from "crypto";
import { promisify } from "util";
const scryptAsync = promisify(scrypt);
// Scrypt parameters (recommended values for 2024+)
const SALT_LENGTH = 32;
const KEY_LENGTH = 64;
const SCRYPT_PARAMS = { N: 16384, r: 8, p: 1 };
export interface PasswordValidationResult {
valid: boolean;
errors: string[];
}
/**
* Password requirements:
* - Minimum 8 characters
* - At least 1 uppercase letter
* - At least 1 lowercase letter
* - At least 1 number
*/
export function validatePasswordStrength(password: string): PasswordValidationResult {
const errors: string[] = [];
if (password.length < 8) {
errors.push("Password must be at least 8 characters long");
}
if (!/[A-Z]/.test(password)) {
errors.push("Password must contain at least one uppercase letter");
}
if (!/[a-z]/.test(password)) {
errors.push("Password must contain at least one lowercase letter");
}
if (!/[0-9]/.test(password)) {
errors.push("Password must contain at least one number");
}
return {
valid: errors.length === 0,
errors
};
}
/**
* Hash a password using Node's built-in scrypt
* Returns format: salt:hash (both hex encoded)
*/
export async function hashPassword(password: string): Promise<string> {
const salt = randomBytes(SALT_LENGTH);
const derivedKey = await scryptAsync(password, salt, KEY_LENGTH) as Buffer;
return `${salt.toString("hex")}:${derivedKey.toString("hex")}`;
}
/**
* Verify a password against a scrypt hash
* Expects format: salt:hash (both hex encoded)
*/
export async function verifyPassword(password: string, storedHash: string): Promise<boolean> {
const [saltHex, hashHex] = storedHash.split(":");
if (!saltHex || !hashHex) {
return false;
}
const salt = Buffer.from(saltHex, "hex");
const storedKey = Buffer.from(hashHex, "hex");
const derivedKey = await scryptAsync(password, salt, KEY_LENGTH) as Buffer;
// Use timing-safe comparison to prevent timing attacks
return timingSafeEqual(derivedKey, storedKey);
}
|