feat: release OTP and Passkey Tools 0.1.0

This commit is contained in:
2026-08-19 12:19:35 +02:00
commit f1cb2d7151
67 changed files with 12802 additions and 0 deletions
+75
View File
@@ -0,0 +1,75 @@
import { describe, expect, it } from "vitest";
import { hexToBytes, utf8ToBytes } from "../../src/crypto/encoding";
import { hashOcraPassword, ocra, parseOcraSuite } from "../../src/otp/ocra";
describe("OCRA", () => {
it("parses complete suites and rejects reordered inputs", () => {
expect(
parseOcraSuite("OCRA-1:HOTP-SHA512-8:C-QN08-PSHA1-S064-T1M"),
).toMatchObject({
algorithm: "SHA-512",
digits: 8,
counter: true,
questionFormat: "numeric",
passwordAlgorithm: "SHA-1",
sessionLength: 64,
timeStepSeconds: 60,
});
expect(() => parseOcraSuite("OCRA-1:HOTP-SHA1-6:QN08-T1M-PSHA1")).toThrow(
/order/iu,
);
});
it("matches RFC 6287 one-way challenge vectors", async () => {
const secret = utf8ToBytes("12345678901234567890");
const expected = [
"237653",
"243178",
"653583",
"740991",
"608993",
"388898",
"816933",
"224598",
"750600",
"294470",
];
for (let index = 0; index < expected.length; index += 1) {
await expect(
ocra({
suite: "OCRA-1:HOTP-SHA1-6:QN08",
secret,
question: String(index).repeat(8),
}),
).resolves.toBe(expected[index]);
}
});
it("matches RFC 6287 counter/PIN and timestamp vectors", async () => {
const secret32 = utf8ToBytes("12345678901234567890123456789012");
const pinHash = await hashOcraPassword("1234", "SHA-1");
expect([...pinHash]).toEqual([
...hexToBytes("7110eda4d09e062aa5e4a390b0a572ac0d2c0220"),
]);
await expect(
ocra({
suite: "OCRA-1:HOTP-SHA256-8:C-QN08-PSHA1",
secret: secret32,
counter: 0n,
question: "12345678",
passwordHash: pinHash,
}),
).resolves.toBe("65347737");
const secret64 = utf8ToBytes(
"1234567890123456789012345678901234567890123456789012345678901234",
);
await expect(
ocra({
suite: "OCRA-1:HOTP-SHA512-8:QN08-T1M",
secret: secret64,
question: "00000000",
timeStep: 0x132d0b6n,
}),
).resolves.toBe("95209754");
});
});