webauthn/lib.rs
1// Safety: This library handles security-critical operations. Unsafe code
2// is forbidden to eliminate entire classes of memory safety vulnerabilities.
3// All cryptographic operations are delegated to the `ring` crate which
4// manages its own unsafe code behind a safe API.
5#![forbid(unsafe_code)]
6// Security libraries must not panic on any input — a panic in a security
7// check aborts the ceremony rather than returning a typed error, potentially
8// allowing callers to misinterpret the outcome. Use ? and explicit error
9// variants everywhere; reserve .expect() for invariants guaranteed by the
10// surrounding bounds checks (e.g. try_into() on a slice whose length was
11// just verified). .unwrap() is unconditionally forbidden in library code.
12#![deny(clippy::unwrap_used)]
13
14//! # webauthn — WebAuthn relying-party library
15//!
16//! Server-side (relying party) verification for the two core WebAuthn ceremonies:
17//!
18//! - **Registration** (`navigator.credentials.create`) — the authenticator generates
19//! a public/private keypair. The relying party verifies the attestation and stores
20//! the public key and credential ID.
21//! - **Authentication** (`navigator.credentials.get`) — the authenticator signs a
22//! challenge with the stored private key. The relying party verifies the signature
23//! and sign count.
24//!
25//! ## Quick start
26//!
27//! ```rust,no_run
28//! use webauthn::{
29//! AuthenticatorAssertionResponse, AuthenticatorAttestationResponse,
30//! Challenge, RelyingParty,
31//! };
32//!
33//! // Configure the relying party once at startup.
34//! let rp = RelyingParty::new("example.com", "https://example.com", "My Service");
35//!
36//! // ── Registration ──────────────────────────────────────────────────────────
37//!
38//! // Issue a challenge, send it to the browser, receive the attestation response.
39//! let reg_challenge = Challenge::new().expect("RNG failure");
40//! # let reg_response = AuthenticatorAttestationResponse {
41//! # client_data_json: vec![],
42//! # attestation_object: vec![],
43//! # };
44//!
45//! // Verify the registration and persist the returned credential.
46//! let reg_result = rp
47//! .verify_registration(®_challenge, ®_response, b"user-id-42")
48//! .expect("registration failed");
49//! let stored_credential = reg_result.credential;
50//!
51//! // ── Authentication ────────────────────────────────────────────────────────
52//!
53//! // Issue a new challenge, send it to the browser, receive the assertion response.
54//! let auth_challenge = Challenge::new().expect("RNG failure");
55//! # let auth_response = AuthenticatorAssertionResponse {
56//! # client_data_json: vec![],
57//! # authenticator_data: vec![],
58//! # signature: vec![],
59//! # user_handle: None,
60//! # };
61//!
62//! // Verify the assertion and update the stored sign count.
63//! let auth_result = rp
64//! .verify_authentication(&stored_credential, &auth_challenge, &auth_response)
65//! .expect("authentication failed");
66//! // Persist auth_result.new_sign_count to your database.
67//! ```
68//!
69//! ## Supported algorithms
70//!
71//! | Algorithm | COSE ID | Description |
72//! |-----------|---------|-------------|
73//! | ES256 | `-7` | ECDSA P-256 with SHA-256 — recommended, most common |
74//! | ES384 | `-35` | ECDSA P-384 with SHA-384 |
75//! | EdDSA | `-8` | Ed25519 — newer FIDO2 authenticators |
76//! | RS256 | `-257` | RSA PKCS#1 v1.5 with SHA-256 — legacy YubiKey 4, Windows Hello |
77//!
78//! See the [COSE algorithm registry](https://www.iana.org/assignments/cose/cose.xhtml)
79//! for the full list of identifiers.
80//!
81//! ## Security properties
82//!
83//! - **No unsafe code** — `#![forbid(unsafe_code)]` is enforced at compile time.
84//! All cryptographic operations are delegated to [`ring`], which descends from
85//! BoringSSL and manages its own unsafe code behind a safe API boundary.
86//! - **No panics** — `#![deny(clippy::unwrap_used)]` prevents `.unwrap()` in library
87//! code. Every error path returns a typed [`WebAuthnError`] variant.
88//! - **No custom crypto** — signature verification, hashing, and random number
89//! generation are all inside `ring`'s audited boundary.
90//! - **Caller responsibilities** — credential uniqueness checks and FIDO
91//! Metadata Service integration are out of scope. Challenge single-use
92//! enforcement is opt-in via
93//! [`RelyingParty::enforce_single_use_challenges`].
94//!
95//! > **Learning project** — this library is a portfolio demonstration of a correct
96//! > WebAuthn implementation. For production use, consider
97//! > [`webauthn-rs`](https://crates.io/crates/webauthn-rs), which includes FIDO MDS
98//! > integration and a broader attestation format set.
99//!
100//! ## Features
101//!
102//! | Feature | Default | Description |
103//! |---------|---------|-------------|
104//! | `serde` | off | Derives [`serde::Serialize`] and [`serde::Deserialize`] on [`Credential`], [`PublicKey`], [`Challenge`], [`RegistrationResult`], [`AuthenticationResult`], [`AttestationType`], and [`WebAuthnError`]. `Vec<u8>` fields are encoded as compact byte sequences via [`serde_bytes`](https://docs.rs/serde_bytes) rather than arrays of integers. Enable with `features = ["serde"]` in `Cargo.toml`. |
105//!
106//! Note: `serde` and `serde_json` are unconditional dependencies used internally
107//! for `clientDataJSON` parsing. The `serde` feature only controls whether the
108//! public-facing types implement `Serialize`/`Deserialize`.
109//!
110//! ## Spec references
111//!
112//! - [W3C WebAuthn Level 2](https://www.w3.org/TR/webauthn-2/)
113//! - [FIDO Alliance specifications](https://fidoalliance.org/specifications/)
114//! - [RFC 8152 — COSE](https://www.rfc-editor.org/rfc/rfc8152)
115
116// Internal modules
117pub mod algorithm;
118pub mod attestation;
119pub mod authenticator_data;
120pub mod challenge;
121pub mod client_data;
122pub mod credential;
123pub mod crypto;
124pub mod der;
125pub mod error;
126
127mod authentication;
128mod registration;
129
130// ─── Public re-exports ────────────────────────────────────────────────────────
131
132pub use algorithm::{COSE_EDDSA, COSE_ES256, COSE_ES384, COSE_RS256};
133pub use authentication::AuthenticatorAssertionResponse;
134pub use challenge::{is_expired, is_expired_with_max_age, CHALLENGE_MAX_AGE_SECS};
135pub use credential::{
136 AttestationType, AuthenticationResult, AuthenticatorAttestationResponse, Challenge, Credential,
137 PublicKey, RegistrationResult,
138};
139pub use crypto::{generate_challenge, random_bytes, rsa_components_to_der, sha256};
140pub use error::{Result, WebAuthnError};
141pub use registration::RelyingParty;