import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { describe, expect, test } from "vitest"; import { verifyPassword } from "../src/auth/password.js"; interface Argon2Vector { password: string; phc: string; } const vectors = JSON.parse(readFileSync( resolve(import.meta.dirname, "fixtures/argon2id-vectors.json"), "utf8", )) as Argon2Vector[]; describe("local Argon2id password verification", () => { test("accepts every committed Go-generated vector", () => { expect(vectors.length).toBeGreaterThan(0); for (const vector of vectors) { expect(verifyPassword(vector.password, vector.phc)).toBe(true); } }); test("rejects a one-byte password change", () => { for (const vector of vectors) { expect(verifyPassword(`${vector.password}!`, vector.phc)).toBe(false); } }); test("rejects malformed and oversized PHC parameters before Argon2 allocation", () => { const password = vectors[0].password; const digest = vectors[0].phc.split("$")[5]; const salt = vectors[0].phc.split("$")[4]; const cases = [ `$argon2id$v=19$m=262145,t=1,p=1$${salt}$${digest}`, `$argon2id$v=19$m=65536,t=11,p=1$${salt}$${digest}`, `$argon2id$v=19$m=65536,t=3,p=5$${salt}$${digest}`, `$argon2id$v=19$m=65536,t=3,p=1$${salt}$${"A".repeat(88)}`, `$argon2id$v=19$m=65536,t=3,p=1$${salt}=$${digest}`, ]; for (const phc of cases) { expect(() => verifyPassword(password, phc)).not.toThrow(); expect(verifyPassword(password, phc)).toBe(false); } }); });