feat: release OTP and Passkey Tools 0.1.0
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { utf8ToBytes } from "../../src/crypto/encoding";
|
||||
import {
|
||||
hotp,
|
||||
totp,
|
||||
totpCounter,
|
||||
verifyHotp,
|
||||
verifyTotp,
|
||||
} from "../../src/otp/otp";
|
||||
|
||||
describe("HOTP", () => {
|
||||
it("matches every RFC 4226 test value", async () => {
|
||||
const secret = utf8ToBytes("12345678901234567890");
|
||||
const expected = [
|
||||
"755224",
|
||||
"287082",
|
||||
"359152",
|
||||
"969429",
|
||||
"338314",
|
||||
"254676",
|
||||
"287922",
|
||||
"162583",
|
||||
"399871",
|
||||
"520489",
|
||||
];
|
||||
await expect(
|
||||
Promise.all(
|
||||
expected.map((_, counter) =>
|
||||
hotp({ secret, counter: BigInt(counter) }),
|
||||
),
|
||||
),
|
||||
).resolves.toEqual(expected);
|
||||
});
|
||||
|
||||
it("searches forward without losing leading zeroes", async () => {
|
||||
const secret = utf8ToBytes("12345678901234567890");
|
||||
const match = await verifyHotp("338314", {
|
||||
secret,
|
||||
counter: 1n,
|
||||
lookAhead: 5,
|
||||
});
|
||||
expect(match).toMatchObject({ counter: 4n, delta: 3 });
|
||||
});
|
||||
});
|
||||
|
||||
describe("TOTP", () => {
|
||||
const cases = [
|
||||
[59, "94287082", "46119246", "90693936"],
|
||||
[1_111_111_109, "07081804", "68084774", "25091201"],
|
||||
[1_111_111_111, "14050471", "67062674", "99943326"],
|
||||
[1_234_567_890, "89005924", "91819424", "93441116"],
|
||||
[2_000_000_000, "69279037", "90698825", "38618901"],
|
||||
[20_000_000_000, "65353130", "77737706", "47863826"],
|
||||
] as const;
|
||||
|
||||
it("matches RFC 6238 SHA-1, SHA-256 and SHA-512 vectors", async () => {
|
||||
const secrets = {
|
||||
"SHA-1": utf8ToBytes("12345678901234567890"),
|
||||
"SHA-256": utf8ToBytes("12345678901234567890123456789012"),
|
||||
"SHA-512": utf8ToBytes(
|
||||
"1234567890123456789012345678901234567890123456789012345678901234",
|
||||
),
|
||||
} as const;
|
||||
for (const [timestamp, sha1, sha256, sha512] of cases) {
|
||||
await expect(
|
||||
totp({
|
||||
secret: secrets["SHA-1"],
|
||||
timestamp,
|
||||
digits: 8,
|
||||
algorithm: "SHA-1",
|
||||
}),
|
||||
).resolves.toBe(sha1);
|
||||
await expect(
|
||||
totp({
|
||||
secret: secrets["SHA-256"],
|
||||
timestamp,
|
||||
digits: 8,
|
||||
algorithm: "SHA-256",
|
||||
}),
|
||||
).resolves.toBe(sha256);
|
||||
await expect(
|
||||
totp({
|
||||
secret: secrets["SHA-512"],
|
||||
timestamp,
|
||||
digits: 8,
|
||||
algorithm: "SHA-512",
|
||||
}),
|
||||
).resolves.toBe(sha512);
|
||||
}
|
||||
});
|
||||
|
||||
it("uses integer counters beyond 2038", () => {
|
||||
expect(totpCounter(20_000_000_000)).toBe(666_666_666n);
|
||||
});
|
||||
|
||||
it("diagnoses a bounded clock delta", async () => {
|
||||
const secret = utf8ToBytes("12345678901234567890");
|
||||
const code = await totp({ secret, timestamp: 1_111_111_109 });
|
||||
const match = await verifyTotp(code, {
|
||||
secret,
|
||||
timestamp: 1_111_111_109 + 60,
|
||||
window: 3,
|
||||
});
|
||||
expect(match?.delta).toBe(-2);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user