123import { describe, expect, test } from "bun:test";4import { codeAt, DIGITS, isSecret, newSecret, stepFor } from "../src/server/totp.ts";5678const SECRET = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ";9const VECTORS: [at: number, code: string, step: number][] = [10 [59, "287082", 1],11 [1111111109, "081804", 37037036],12 [1234567890, "005924", 41152263],13 [2000000000, "279037", 66666666],14];1516describe("TOTP codes (§24)", () => {17 test("matches the RFC's vectors, at the step they belong to", () => {18 for (const [at, code, step] of VECTORS) {19 expect(stepFor(SECRET, code, at)).toBe(step);20 expect(codeAt(SECRET, at)).toBe(code);21 }22 });2324 test("accepts a step either side of now, and nothing further", () => {25 const [at, code] = VECTORS[0] as [number, string, number];26 expect(stepFor(SECRET, code, at + 30)).toBe(1);27 expect(stepFor(SECRET, code, at - 30)).toBe(1);28 expect(stepFor(SECRET, code, at + 60)).toBeNull();29 expect(stepFor(SECRET, code, at - 61)).toBeNull();30 });3132 test("a code is only right for its own secret and its own shape", () => {33 const other = "JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP";34 expect(stepFor(other, "287082", 59)).toBeNull();35 expect(stepFor(SECRET, "28708", 59)).toBeNull();36 expect(stepFor(SECRET, "2870821", 59)).toBeNull();37 expect(stepFor(SECRET, "abcdef", 59)).toBeNull();38 expect(stepFor(SECRET, "", 59)).toBeNull();39 expect(stepFor("not base32!", "287082", 59)).toBeNull();40 });4142 test("a fresh secret is base32 of the length authenticators use", () => {43 const secret = newSecret();44 expect(isSecret(secret)).toBe(true);45 expect(secret).toHaveLength(32);46 expect(secret).toMatch(/^[A-Z2-7]+$/);47 expect(secret).not.toBe(newSecret());48 expect(codeAt(secret, 1111111109)).toHaveLength(DIGITS);49 });5051 test("a secret too short to be worth having is refused, as is anything that is not base32", () => {52 53 expect(isSecret("ABCD")).toBe(false);54 expect(isSecret("OOOO")).toBe(false);55 expect(isSecret("")).toBe(false);56 expect(isSecret("JBSWY3DPEHPK3PXP")).toBe(false);57 expect(isSecret("not base32!")).toBe(false);58 expect(isSecret(SECRET)).toBe(true);59 });60});