71 lines
2.5 KiB
TypeScript
71 lines
2.5 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { describe, it } from "node:test";
|
|
import {
|
|
VERIFICATION_CODE_LENGTH,
|
|
hashPhoneNumber,
|
|
hashVerificationCode,
|
|
mintVerificationCode,
|
|
verificationCodeMatches,
|
|
} from "../../src/util/smsVerificationCode.js";
|
|
|
|
const SECRET = "test-sms-code-secret";
|
|
|
|
describe("mintVerificationCode", () => {
|
|
it("always produces six digits, leading zeros included", () => {
|
|
for (let i = 0; i < 2000; i += 1) {
|
|
const code = mintVerificationCode();
|
|
assert.equal(code.length, VERIFICATION_CODE_LENGTH);
|
|
assert.match(code, /^\d{6}$/);
|
|
}
|
|
});
|
|
|
|
it("does not return the same code every call", () => {
|
|
const seen = new Set<string>();
|
|
for (let i = 0; i < 200; i += 1) seen.add(mintVerificationCode());
|
|
assert.ok(seen.size > 100, `expected varied codes, got ${seen.size}`);
|
|
});
|
|
});
|
|
|
|
describe("hashVerificationCode", () => {
|
|
it("is stable for one secret and different across secrets", () => {
|
|
assert.equal(
|
|
hashVerificationCode("483920", SECRET),
|
|
hashVerificationCode("483920", SECRET)
|
|
);
|
|
assert.notEqual(
|
|
hashVerificationCode("483920", SECRET),
|
|
hashVerificationCode("483920", "other-secret")
|
|
);
|
|
});
|
|
|
|
it("never contains the plaintext code", () => {
|
|
assert.equal(hashVerificationCode("483920", SECRET).includes("483920"), false);
|
|
});
|
|
});
|
|
|
|
describe("hashPhoneNumber", () => {
|
|
it("is stable per number and does not contain the number", () => {
|
|
const hash = hashPhoneNumber("+15555550123", SECRET);
|
|
assert.equal(hash, hashPhoneNumber("+15555550123", SECRET));
|
|
assert.notEqual(hash, hashPhoneNumber("+15555550124", SECRET));
|
|
assert.equal(hash.includes("5555550123"), false);
|
|
});
|
|
});
|
|
|
|
describe("verificationCodeMatches", () => {
|
|
it("accepts the matching code and rejects every near miss", () => {
|
|
const stored = hashVerificationCode("483920", SECRET);
|
|
assert.equal(verificationCodeMatches("483920", stored, SECRET), true);
|
|
assert.equal(verificationCodeMatches("483921", stored, SECRET), false);
|
|
assert.equal(verificationCodeMatches("48392", stored, SECRET), false);
|
|
assert.equal(verificationCodeMatches("", stored, SECRET), false);
|
|
assert.equal(verificationCodeMatches("483920", stored, "wrong"), false);
|
|
});
|
|
|
|
it("compares full hashes, so a shared prefix is not a partial match", () => {
|
|
const stored = hashVerificationCode("111111", SECRET);
|
|
const truncated = stored.slice(0, 10);
|
|
assert.equal(verificationCodeMatches("111111", truncated, SECRET), false);
|
|
});
|
|
});
|