import { describe, expect, it } from "vitest"; import { bytesToArrayBuffer, bytesToBase64Url, utf8ToBytes, } from "../../src/crypto/encoding"; import { parseAuthenticatorData, parseClientData, } from "../../src/webauthn/parser"; describe("WebAuthn parsers", () => { it("parses collected client data without normalizing security fields", () => { const json = utf8ToBytes( JSON.stringify({ type: "webauthn.get", challenge: "YWJj", origin: "https://example.test", crossOrigin: false, }), ); expect(parseClientData(bytesToBase64Url(json))).toMatchObject({ type: "webauthn.get", origin: "https://example.test", }); }); it("extracts flags and the unsigned signature counter", async () => { const rpHash = new Uint8Array( await crypto.subtle.digest( "SHA-256", bytesToArrayBuffer(utf8ToBytes("example.test")), ), ); const bytes = Uint8Array.from([...rpHash, 0x05, 0x00, 0x00, 0x00, 0x09]); const parsed = parseAuthenticatorData(bytes); expect(parsed.flags).toMatchObject({ userPresent: true, userVerified: true, }); expect(parsed.signCount).toBe(9); }); it("rejects inconsistent backup flags and undeclared trailing data", () => { expect(() => parseAuthenticatorData( Uint8Array.from([...new Uint8Array(32), 0x10, 0, 0, 0, 0]), ), ).toThrow(/backup/iu); expect(() => parseAuthenticatorData( Uint8Array.from([...new Uint8Array(32), 0x01, 0, 0, 0, 0, 0]), ), ).toThrow(/not described/iu); }); });