nox-webauthn #
WebAuthn relying-party verification for OCaml: passkey and security-key registrations and assertions, checked step by step against W3C Web Authentication Level 3.
A relying party asks a browser to create a credential (registration) or to
sign a challenge with one (authentication), and receives byte strings back:
clientDataJSON and attestationObject, or clientDataJSON,
authenticatorData and signature. Webauthn.Registration.verify runs the
steps of section 7.1 over the first pair and returns the credential record to
store; Webauthn.Assertion.verify runs those of section 7.2 over the second
and returns the record updated with the new signature counter. A refusal is a
Webauthn.Error.t naming the ceremony and the step that failed.
The none and packed attestation formats are verified, packed through
the certificate requirements of section 8.2.1 and a chain to the relying
party's own trust anchors. tpm, android-key, apple and fido-u2f
statements are refused by name. Credential public keys are COSE_Keys of the
cose package (ES256, ES384, ES512, EdDSA, PS256, RS256).
Verification is pure: the relying party, the challenge it issued, the stored credential and the current time are arguments, so the library reads no clock, no network and no database.
Installation #
$ opam install nox-webauthn
Usage #
let rp =
Webauthn.Relying_party.v ~id:"example.org"
~origins:[ "https://example.org" ] ()
(* After navigator.credentials.create: [challenge] is the one this
server issued, the two strings are the base64url-decoded response and
[now] is the current time. *)
let register ~now ~challenge ~client_data_json attestation_object =
match
Webauthn.Registration.verify ~rp
~algorithms:[ Cose.Algorithm.ES256; Cose.Algorithm.RS256 ]
~challenge ~now ~client_data_json
attestation_object
with
| Ok r -> Ok (Webauthn.Registration.credential r)
| Error e -> Error (Fmt.str "%a" Webauthn.Error.pp e)
(* After navigator.credentials.get, with the record stored above. *)
let authenticate ~challenge ~credential ~client_data_json
~authenticator_data signature =
Webauthn.Assertion.verify ~user_verification:Webauthn.Required ~rp
~challenge ~credential ~client_data_json ~authenticator_data signature
|> Result.map Webauthn.Assertion.credential
Storing the record, refusing a credential id already registered (section 7.1 step 26) and finding the record an assertion names (section 7.2 steps 5 and 6) are the caller's.
Related work #
- webauthn (robur, BSD-2)
verifies the
noneandfido-u2fattestations with P-256 keys and leaves counters and user verification to the caller. - webauthn-rs (Rust) and go-webauthn (Go) cover every attestation format and FIDO metadata; this package's packed attestation tests replay some of their fixtures.
References #
- Web Authentication Level 3, sections 5.8.1, 6.1, 6.5, 7.1, 7.2, 8.2, 8.7 and the test vectors of section 16
- RFC 8812, COSE and JOSE registrations for WebAuthn algorithms
- RFC 9052, COSE_Key
Licence #
ISC. See LICENSE.md. The test fixtures keep their own licences;
see test/fixtures/README.md.