Skip to main content

webauthn_kit/
lib.rs

1//! # webauthn-kit
2//!
3//! Standalone `WebAuthn` / FIDO2 passkey verification kit: real CTAP2/COSE
4//! cryptographic verification with zero framework coupling. This is not a
5//! wrapper around `navigator.credentials` server emulation — it implements
6//! the server side of the protocol directly on `ring`.
7//!
8//! ## What it does
9//!
10//! - Full CTAP2 authenticator data parsing (rpIdHash, flags, signCount,
11//!   attested credential data) — [`protocol`]
12//! - `verify_registration` / `verify_authentication` ceremonies — [`protocol`]
13//! - COSE public key parsing and signature verification for **ES256**
14//!   (ECDSA P-256 + SHA-256) and **RS256** (RSA PKCS#1 v1.5 + SHA-256) via
15//!   `ring` — [`crypto`]
16//! - Challenge generation from the OS CSPRNG, single-use consumption
17//!   (replay protection), freshness/expiry enforcement, and the sign-count
18//!   clone-detection state machine — [`challenge`]
19//! - Storage-free credential record + browser wire DTOs — [`credential`]
20//!
21//! ## Quick start
22//!
23//! ```
24//! use webauthn_kit::{
25//!     check_sign_count, verify_authentication, verify_registration, AuthenticationParams,
26//!     ChallengeStore, WebauthnConfig,
27//! };
28//!
29//! let config = WebauthnConfig {
30//!     rp_id: "example.com".into(),
31//!     rp_name: "Example".into(),
32//!     rp_origins: vec!["https://example.com".into()],
33//!     allowed_algorithms: vec![-7, -257],
34//!     challenge_timeout_secs: 300,
35//! };
36//!
37//! // 1. Issue a challenge (single-use; store the (id, bytes) pair).
38//! let store = ChallengeStore::new();
39//! let (challenge_b64, create_options) =
40//!     store.generate_registration_challenge(&config, "alice", "Alice", &[]);
41//! // ... send `create_options` to the browser, receive RegistrationResponse ...
42//!
43//! // 2. Verify a registration response (see tests/vectors.rs for a full
44//! //    roundtrip with a synthetic keypair).
45//! // let result = verify_registration(&challenge, &client_data_json_b64, &attestation_object_b64, "", &config.rp_id, &config.rp_origins)?;
46//!
47//! // 3. Verify an assertion; persist result.new_sign_count afterwards.
48//! // let result = verify_authentication(&AuthenticationParams { ... })?;
49//! ```
50//!
51//! ## Storage contract (what this crate does NOT do)
52//!
53//! This crate is deliberately storage-free. The integrator owns:
54//!
55//! - **Credential persistence**: after [`verify_registration`] succeeds,
56//!   store a [`credential::WebauthnCredential`] (credential ID, COSE public
57//!   key, sign count, timestamps). After each successful
58//!   [`verify_authentication`], atomically persist
59//!   [`credential::AuthenticationResult::new_sign_count`].
60//! - **Challenge durability**: [`ChallengeStore`] holds pending challenges
61//!   in memory; they are lost on restart (users simply retry). Keep the
62//!   consume-then-verify ordering so restarts can never enable replays.
63//! - **User mapping and session issuance** (cookies, JWTs, ...).
64//! - **UI / HTTP layer**: the `credential` DTOs serialize with the `serde`
65//!   feature but no HTTP types are included.
66//!
67//! ## Security notes
68//!
69//! - `#![forbid(unsafe_code)]`; all cryptography via `ring`.
70//! - Every parser accepts hostile input and returns `Err` instead of
71//!   panicking (property/fuzz-tested in `tests/fuzz.rs`).
72//! - Challenge bytes are 32 bytes from the OS CSPRNG; consumption is
73//!   single-use and freshness-bounded.
74//! - **Attestation is parsed, not verified**: the attestation object is
75//!   decoded to extract authenticator data, but attestation statement
76//!   signatures are not checked. Supported attestation formats: any `fmt`
77//!   value is tolerated ("none", "packed", "fido-u2f", ...) and recorded
78//!   informationally — registration proves key possession, not device
79//!   provenance. If you need verified device provenance, add an attestation
80//!   verifier on top.
81//! - Supported algorithms: ES256 (COSE -7) and RS256 (COSE -257) only.
82//!   Other algorithms (including OKP/Ed25519) are rejected with
83//!   [`WebauthnError::UnsupportedAlgorithm`].
84//! - Sign-count policy: a stored counter of 0 disables the check
85//!   (authenticators without counters); otherwise a strictly decreasing
86//!   counter is rejected. Equal counters are allowed by design.
87//! - The UV (user verification) flag is reported, never required; enforce
88//!   your own policy per ceremony.
89
90#![forbid(unsafe_code)]
91#![deny(missing_docs)]
92
93pub mod challenge;
94pub mod config;
95pub mod credential;
96pub mod crypto;
97pub mod error;
98pub mod protocol;
99
100pub use challenge::{check_sign_count, ChallengeStore};
101pub use config::WebauthnConfig;
102pub use credential::{
103    AllowCredential, AuthenticationOptions, AuthenticationResponse, AuthenticationResult,
104    AuthenticatorSelection, ExcludeCredential, PubKeyCredParam, RegistrationOptions,
105    RegistrationResponse, RegistrationResult, RelyingParty, WebauthnCredential, WebauthnUser,
106};
107pub use crypto::{
108    alg_to_name, base64_decode_urlsafe, base64_encode_urlsafe, cbor_bytes, cbor_map_entries,
109    generate_challenge_bytes, parse_cose_key, verify_cose_signature, CosePublicKey, COSE_ALG_ES256,
110    COSE_ALG_RS256, COSE_KTY_EC2, COSE_KTY_OKP, COSE_KTY_RSA,
111};
112pub use error::WebauthnError;
113pub use protocol::{verify_authentication, verify_registration, AuthenticationParams};