# Attestation verification dispatcher ## Purpose Implements `verifyAttestation` (W3C WebAuthn L3 §6.5.4) and `defaultAttestationRegistry()`. The dispatcher reads `attestationObject.fmt`, looks up the corresponding verifier in the registry, computes the `clientDataHash` once, and invokes the verifier. It is the single entry point into the per-format machinery — consumers do not import individual verifiers unless they want to override behaviour for a specific format. ## Features - Built-in coverage of every IANA-registered format in W3C WebAuthn L3 §8: `packed`, `fido-u2f`, `tpm`, `android-key`, `android-safetynet`, `apple`, `none`. - `WEBAUTHN.ATTESTATION_FORMAT_UNSUPPORTED` thrown on missing registry entry — distinct from `MALFORMED_RESPONSE` so dashboards can distinguish policy denials from protocol violations. - `webauthn.attestation_format_total{fmt}` OTel counter incremented per dispatch with cardinality bounded to the seven registered formats plus `unknown`. - `clientDataHash` computed once via `@suaveplan/crypto-hash#sha256` and passed to every verifier so the per-format code does not re-hash. ## Usage: Basic Example ```ts import { defaultAttestationRegistry, parseAttestationObject, verifyAttestation, } from "@suaveplan/webauthn"; const env = parseAttestationObject(rawAttestationObject); const result = await verifyAttestation({ attestationObject: env, clientDataJSON, expectedRpIdHash, registry: defaultAttestationRegistry(), }); ``` ## Usage: Advanced Example ```ts import { defaultAttestationRegistry, registerAttestationFormat, verifyAttestation, } from "@suaveplan/webauthn"; const registry = defaultAttestationRegistry(); registerAttestationFormat(registry, "apple-appattest", async (input) => { // custom verifier for Apple's App Attest format return { trusted: true, attestationType: "anonCA", trustPath: [], }; }); const result = await verifyAttestation({ attestationObject, clientDataJSON, expectedRpIdHash, registry, }); ``` ## API Reference - `defaultAttestationRegistry(): AttestationFormatRegistry` — registry pre-populated with every built-in format. - `VerifyAttestationInput` — `{ attestationObject, clientDataJSON, expectedRpIdHash, registry }`. - `verifyAttestation(input): Promise` — dispatch + invoke + return. ## Implementation Notes - The registry is a fresh `Map` per call so consumers can mutate it independently; production callers typically build it once and cache. - The OTel counter is emitted BEFORE the dispatch so a verifier that throws still contributes to the format count — this matters for diagnosing "X happened N times, M of which failed" without needing two counters. - `clientDataHash` is the SHA-256 of the raw clientDataJSON bytes, NOT of the parsed JSON. Authenticators sign over the wire-encoded bytes, so re-encoding would break the signature check. - The dispatcher does NOT validate the client data type / origin / challenge — that is the registration ceremony's job and lives in `register`. The dispatcher is a pure attestation primitive.