webauthn_rp

WebAuthn Level 3 RP library.
git clone https://git.philomathiclife.com/repos/webauthn_rp
Log | Files | Refs | README

lib.rs (59160B)


      1 //! [![git]](https://git.philomathiclife.com/webauthn_rp/log.html) [![crates-io]](https://crates.io/crates/webauthn_rp) [![docs-rs]](crate)
      2 //!
      3 //! [git]: https://git.philomathiclife.com/git_badge.svg
      4 //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
      5 //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
      6 //!
      7 //! `webauthn_rp` is a library for _server-side_
      8 //! [Web Authentication (WebAuthn)](https://www.w3.org/TR/webauthn-3/#sctn-rp-operations) Relying Party
      9 //! (RP) operations.
     10 //!
     11 //! The purpose of a server-side RP library is to be modular so that any client can be used with it as a backend
     12 //! _including_ native applications—WebAuthn technically only covers web applications; however it's relatively easy
     13 //! to adapt to native applications as well. It achieves this by not assuming how data is sent to/from the client;
     14 //! having said that, there are pre-defined serialization formats for "common" deployments which can be used when
     15 //! [`serde`](#serde) is enabled.
     16 //!
     17 //! ## Cargo "features"
     18 //!
     19 //! [`custom`](#custom) or both [`bin`](#bin) and [`serde`](#serde) must be enabled; otherwise a [`compile_error`]
     20 //!  will occur.
     21 //!
     22 //! ### `bin`
     23 //!
     24 //! Enables binary (de)serialization via [`Encode`] and [`Decode`]. Since registered credentials will almost always
     25 //! have to be saved to persistent storage, _some_ form of (de)serialization is necessary. In the event `bin` is
     26 //! unsuitable or only partially suitable (e.g., human-readable output is desired), one will need to enable
     27 //! [`custom`](#custom) to allow construction of certain types (e.g., [`AuthenticatedCredential`]).
     28 //!
     29 //! If possible and desired, one may wish to save the data "directly" to avoid any potential temporary allocations.
     30 //! For example [`StaticState::encode`] will return a [`Vec`] containing hundreds (and possibly thousands in the
     31 //! extreme case) of bytes if the underlying public key is an RSA key. This additional allocation and copy of data
     32 //! is obviously avoided if [`StaticState`] is stored as a
     33 //! [composite type](https://www.postgresql.org/docs/current/rowtypes.html) or its fields are stored in separate
     34 //! columns when written to a relational database (RDB).
     35 //!
     36 //! ### `custom`
     37 //!
     38 //! Exposes functions (e.g., [`AuthenticatedCredential::new`]) that allows one to construct instances of types that
     39 //! cannot be constructed when [`bin`](#bin) or [`serde`](#serde) is not enabled.
     40 //!
     41 //! ### `serde`
     42 //!
     43 //! This feature _strictly_ adheres to the JSON-motivated definitions. You _will_ encounter clients that send data
     44 //! that cannot be deserialized using this feature. For many [`serde_relaxed`](#serde_relaxed) should be used
     45 //! instead.
     46 //!
     47 //! Enables (de)serialization of data sent to/from the client via [`serde`](https://docs.rs/serde/latest/serde/)
     48 //! based on the JSON-motivated definitions (e.g.,
     49 //! [`RegistrationResponseJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-registrationresponsejson)). Since
     50 //! data has to be sent to/from the client, _some_ form of (de)serialization is necessary. In the event `serde`
     51 //! is unsuitable or only partially suitable, one will need to enable [`custom`](#custom) to allow construction
     52 //! of certain types (e.g., [`Registration`]).
     53 //!
     54 //! Code is _strongly_ encouraged to rely on the [`Deserialize`] implementations as much as possible to reduce the
     55 //! chances of improperly deserializing the client data.
     56 //!
     57 //! Note that clients are free to send data in whatever form works best, so there is no requirement the
     58 //! JSON-motivated definitions are used even when JSON is sent. This is especially relevant since the JSON-motivated
     59 //! definitions were only added in [WebAuthn Level 3](https://www.w3.org/TR/webauthn-3/); thus many deployments only
     60 //! partially conform. Some specific deviations that may require partial customization of deserialization are the
     61 //! following:
     62 //!
     63 //! * [`ArrayBuffer`](https://webidl.spec.whatwg.org/#idl-ArrayBuffer)s encoded using something other than
     64 //!   base64url.
     65 //! * `ArrayBuffer`s that are encoded multiple times (including the use of different encodings each time).
     66 //! * Missing fields (e.g.,
     67 //!   [`transports`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-transports)).
     68 //! * Different field names (e.g., `extensions` instead of
     69 //!   [`clientExtensionResults`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-clientextensionresults)).
     70 //!
     71 //! ### `serde_relaxed`
     72 //!
     73 //! Automatically enables [`serde`](#serde) in addition to "relaxed" [`Deserialize`] implementations
     74 //! (e.g., [`RegistrationRelaxed`]). Roughly "relaxed" translates to unknown fields being ignored and only
     75 //! the fields necessary for construction of the type are required. Case still matters, duplicate fields are still
     76 //! forbidden, and interrelated data validation is still performed when applicable. This can be useful when one
     77 //! wants to accommodate non-conforming clients or clients that implement older versions of the spec.
     78 //!
     79 //! ### `serializable_server_state`
     80 //!
     81 //! Automatically enables [`bin`](#bin) in addition to [`Encode`] and [`Decode`] implementations for
     82 //! [`RegistrationServerState`] and [`AuthenticationServerState`]. Less accurate [`SystemTime`] is used instead of
     83 //! [`Instant`] for timeout enforcement. This should be enabled if you don't desire to use in-memory collections to
     84 //! store the instances of those types.
     85 //!
     86 //! Note even when written to persistent storage, an application should still periodically remove expired ceremonies.
     87 //! If one is using a relational database (RDB); then one can achieve this by storing [`ServerState::sent_challenge`],
     88 //! the `Vec` returned from [`Encode::encode`], and [`ServerState::expiration`] and periodically remove all rows
     89 //! whose expiration exceeds the current date and time.
     90 //!
     91 //! ## Registration and authentication
     92 //!
     93 //! Both [registration](https://www.w3.org/TR/webauthn-3/#registration-ceremony) and
     94 //! [authentication](https://www.w3.org/TR/webauthn-3/#authentication-ceremony) ceremonies rely on "challenges", and
     95 //! these challenges are inherently temporary. For this reason the data associated with challenge completion can
     96 //! often be stored in memory without concern for out-of-memory (OOM) conditions. There are several benefits to
     97 //! storing such data in memory:
     98 //!
     99 //! * No data manipulation
    100 //!     * By leveraging move semantics, the data sent to the client cannot be mutated once the ceremony begins.
    101 //! * Improved timeout enforcement
    102 //!     * By ensuring the same machine that started the ceremony is also used to finish the ceremony, deviation of
    103 //!       system clocks is not a concern. Additionally, allowing serialization requires the use of some form of
    104 //!       cross-platform "timestamp" (e.g., [Unix time](https://en.wikipedia.org/wiki/Unix_time)) which differ in
    105 //!       implementation (e.g., platforms implement leap seconds in different ways) and are often not monotonically
    106 //!       increasing. If data resides in memory, a monotonic [`Instant`] can be used instead.
    107 //!
    108 //! It is for those reasons data like [`RegistrationServerState`] are not serializable by default and require the
    109 //! use of in-memory collections (e.g., [`FixedCapHashSet`]). To better ensure OOM is not a concern, RPs should set
    110 //! reasonable timeouts. Since ceremonies can only be completed by moving data (e.g.,
    111 //! [`RegistrationServerState::verify`]), ceremony completion is guaranteed to free up the memory used—
    112 //! `RegistrationServerState` instances are only 48 bytes on `x86_64-unknown-linux-gnu` platforms. To avoid issues
    113 //! related to incomplete ceremonies, RPs can periodically iterate the collection for expired ceremonies and remove
    114 //! such data. Other techniques can be employed as well to mitigate OOM, but they are application specific and
    115 //! out-of-scope. If this is undesirable, one can enable [`serializable_server_state`](#serializable_server_state)
    116 //! so that `RegistrationServerState` and [`AuthenticationServerState`] implement [`Encode`] and [`Decode`]. Another
    117 //! reason one may need to store this information persistently is for load-balancing purposes where the server that
    118 //! started the ceremony is not guaranteed to be the server that finishes the ceremony.
    119 //!
    120 //! ## Supported signature algorithms
    121 //!
    122 //! The only supported signature algorithms are the following:
    123 //!
    124 //! * Ed25519 as defined in [RFC 8032 § 5.1](https://www.rfc-editor.org/rfc/rfc8032#section-5.1). This corresponds
    125 //!   to [`CoseAlgorithmIdentifier::Eddsa`].
    126 //! * ECDSA as defined in [SEC 1 Version 2.0 § 4.1](https://www.secg.org/sec1-v2.pdf#subsection.4.1) using SHA-256
    127 //!   as the hash function and NIST P-256 as defined in
    128 //!   [NIST SP 800-186 § 3.2.1.3](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-186.pdf#%5B%7B%22num%22%3A229%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C70%2C275%2C0%5D)
    129 //!   for the underlying elliptic curve. This corresponds to [`CoseAlgorithmIdentifier::Es256`].
    130 //! * ECDSA as defined in SEC 1 Version 2.0 § 4.1 using SHA-384 as the hash function and NIST P-384 as defined in
    131 //!   [NIST SP 800-186 § 3.2.1.4](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-186.pdf#%5B%7B%22num%22%3A232%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C70%2C264%2C0%5D)
    132 //!   for the underlying elliptic curve. This corresponds to [`CoseAlgorithmIdentifier::Es384`].
    133 //! * RSASSA-PKCS1-v1_5 as defined in [RFC 8017 § 8.2](https://www.rfc-editor.org/rfc/rfc8017#section-8.2) using
    134 //!   SHA-256 as the hash function. This corresponds to [`CoseAlgorithmIdentifier::Rs256`].
    135 //!
    136 //! ## Correctness of code
    137 //!
    138 //! This library more strictly adheres to the spec than many other similar libraries including but not limited to
    139 //! the following ways:
    140 //!
    141 //! * [CTAP2 canonical CBOR encoding form](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#ctap2-canonical-cbor-encoding-form).
    142 //! * `Deserialize` implementations requiring _exact_ conformance (e.g., not allowing unknown data).
    143 //! * More thorough interrelated data validation (e.g., all places a Credential ID exists must match).
    144 //! * Implement a lot of recommended (i.e., SHOULD) criteria (e.g.,
    145 //!   [User display names conforming to the Nickname Profile as defined in RFC 8266](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialentity-name)).
    146 //!
    147 //! Unfortunately like almost all software, this library has not been formally verified; however great care is
    148 //! employed in the following ways:
    149 //!
    150 //! * Leverage move semantics to prevent mutation of data once in a static state.
    151 //! * Ensure a great many invariants via types.
    152 //! * Reduce code duplication.
    153 //! * Reduce variable mutation allowing for simpler algebraic reasoning.
    154 //! * `panic`-free code[^note] (i.e., define true/total functions).
    155 //! * Ensure arithmetic "side effects" don't occur (e.g., overflow).
    156 //! * Aggressive use of compiler and [Clippy](https://doc.rust-lang.org/stable/clippy/lints.html) lints.
    157 //! * Unit tests for common cases, edge cases, and error cases.
    158 //!
    159 //! ## Cryptographic libraries
    160 //!
    161 //! This library does not rely on _any_ sensitive data (e.g., private keys) as only signature verification is
    162 //! ever performed. This means that the only thing that matters with the libraries used is their algorithmic
    163 //! correctness and not other normally essential aspects like susceptibility to side-channel attacks. While I
    164 //! personally believe the libraries that are used are at least as "secure" as alternatives even when dealing with
    165 //! sensitive data, one only needs to audit the correctness of the libraries to be confident in their use. In fact
    166 //! [`curve25519_dalek`](https://docs.rs/curve25519-dalek/latest/curve25519_dalek/#backends) has been formally
    167 //! verified when the [`fiat`](https://github.com/mit-plv/fiat-crypto) backend is used making it _objectively_
    168 //! better than many other libraries whose correctness has not been proven. Two additional benefits of the library
    169 //! choices are simpler APIs making it more likely their use is correct and better cross-platform compatibility.
    170 //!
    171 //! [^note]: `panic`s related to memory allocations or stack overflow are possible since such issues are not
    172 //!          formally guarded against.
    173 #![cfg_attr(docsrs, feature(doc_cfg))]
    174 #![deny(
    175     unknown_lints,
    176     future_incompatible,
    177     let_underscore,
    178     missing_docs,
    179     nonstandard_style,
    180     refining_impl_trait,
    181     rust_2018_compatibility,
    182     rust_2018_idioms,
    183     rust_2021_compatibility,
    184     rust_2024_compatibility,
    185     unsafe_code,
    186     unused,
    187     warnings,
    188     clippy::all,
    189     clippy::cargo,
    190     clippy::complexity,
    191     clippy::correctness,
    192     clippy::nursery,
    193     clippy::pedantic,
    194     clippy::perf,
    195     clippy::restriction,
    196     clippy::style,
    197     clippy::suspicious
    198 )]
    199 #![expect(
    200     clippy::arbitrary_source_item_ordering,
    201     clippy::blanket_clippy_restriction_lints,
    202     clippy::exhaustive_enums,
    203     clippy::exhaustive_structs,
    204     clippy::implicit_return,
    205     clippy::min_ident_chars,
    206     clippy::missing_trait_methods,
    207     clippy::multiple_crate_versions,
    208     clippy::pub_with_shorthand,
    209     clippy::pub_use,
    210     clippy::ref_patterns,
    211     clippy::self_named_module_files,
    212     clippy::single_call_fn,
    213     clippy::single_char_lifetime_names,
    214     clippy::unseparated_literal_suffix,
    215     reason = "noisy, opinionated, and likely doesn't prevent bugs or improve APIs"
    216 )]
    217 #[cfg(not(any(feature = "custom", all(feature = "bin", feature = "serde"))))]
    218 compile_error!("'custom' must be enabled or both 'bin' and 'serde' must be enabled");
    219 #[cfg(feature = "serializable_server_state")]
    220 use crate::request::{
    221     auth::ser_server_state::{
    222         DecodeAuthenticationServerStateErr, EncodeAuthenticationServerStateErr,
    223     },
    224     register::ser_server_state::DecodeRegistrationServerStateErr,
    225 };
    226 #[cfg(any(feature = "bin", feature = "custom"))]
    227 use crate::response::error::CredentialIdErr;
    228 #[cfg(feature = "serde_relaxed")]
    229 use crate::response::ser_relaxed::SerdeJsonErr;
    230 #[cfg(feature = "bin")]
    231 use crate::{
    232     request::register::bin::{DecodeNicknameErr, DecodeUsernameErr},
    233     response::{
    234         bin::DecodeAuthTransportsErr,
    235         register::bin::{DecodeDynamicStateErr, DecodeStaticStateErr},
    236     },
    237 };
    238 #[cfg(doc)]
    239 use crate::{
    240     request::{
    241         AsciiDomain, DomainOrigin, FixedCapHashSet, Port, PublicKeyCredentialDescriptor, RpId,
    242         Scheme, ServerState, Url,
    243         auth::{AllowedCredential, AllowedCredentials},
    244         register::{CoseAlgorithmIdentifier, Nickname, Username},
    245     },
    246     response::{
    247         CollectedClientData, Flag,
    248         auth::{self, AuthenticatorAssertion},
    249         register::{
    250             self, Aaguid, Attestation, AttestationObject, AttestedCredentialData,
    251             AuthenticatorExtensionOutput, ClientExtensionsOutputs, CompressedPubKey,
    252             CredentialPropertiesOutput,
    253         },
    254     },
    255 };
    256 use crate::{
    257     request::{
    258         auth::error::{RequestOptionsErr, SecondFactorErr},
    259         error::{AsciiDomainErr, DomainOriginParseErr, PortParseErr, SchemeParseErr, UrlErr},
    260         register::{
    261             ResidentKeyRequirement, UserHandle,
    262             error::{CreationOptionsErr, NicknameErr, UserHandleErr, UsernameErr},
    263         },
    264     },
    265     response::{
    266         AuthTransports, CredentialId,
    267         auth::error::{AuthCeremonyErr, AuthenticatorDataErr as AuthAuthDataErr},
    268         error::CollectedClientDataErr,
    269         register::{
    270             CredentialProtectionPolicy, DynamicState, Metadata, StaticState, UncompressedPubKey,
    271             error::{
    272                 AaguidErr, AttestationObjectErr, AuthenticatorDataErr as RegAuthDataErr,
    273                 RegCeremonyErr,
    274             },
    275         },
    276     },
    277 };
    278 #[cfg(all(doc, feature = "bin"))]
    279 use bin::{Decode, Encode};
    280 #[cfg(doc)]
    281 use core::str::FromStr;
    282 use core::{
    283     convert,
    284     error::Error,
    285     fmt::{self, Display, Formatter},
    286 };
    287 #[cfg(all(doc, feature = "serde_relaxed"))]
    288 use response::register::ser_relaxed::RegistrationRelaxed;
    289 #[cfg(all(doc, feature = "serde"))]
    290 use serde::Deserialize;
    291 #[cfg(all(doc, feature = "serde_relaxed"))]
    292 use serde_json::de::{Deserializer, StreamDeserializer};
    293 #[cfg(feature = "serializable_server_state")]
    294 use std::time::SystemTimeError;
    295 #[cfg(doc)]
    296 use std::time::{Instant, SystemTime};
    297 /// Contains functionality to (de)serialize data to a data store.
    298 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))]
    299 #[cfg(feature = "bin")]
    300 pub mod bin;
    301 /// Functionality for starting ceremonies.
    302 ///
    303 /// # What kind of credential should I create?
    304 ///
    305 /// Without partitioning the possibilities _too_ much, the following are possible authentication flows:
    306 ///
    307 /// | Label | Username | Password | Client-side credential | Authenticator-side user verification | Recommended |
    308 /// |-------|----------|----------|------------------------|--------------------------------------|:-----------:|
    309 /// | 1     | Yes      | Yes      | Required               | Yes                                  |          ❌ |
    310 /// | 2     | Yes      | Yes      | Required               | No                                   |          ❌ |
    311 /// | 3     | Yes      | Yes      | Optional               | Yes                                  |          ❌ |
    312 /// | <a name="label4">4</a>     | Yes      | Yes      | Optional               | No                                   |          ✅ |
    313 /// | 5     | Yes      | No       | Required               | Yes                                  |          ❌ |
    314 /// | 6     | Yes      | No       | Required               | No                                   |          ❌ |
    315 /// | <a name="label7">7</a>     | Yes      | No       | Optional               | Yes                                  |          ❔ |
    316 /// | 8     | Yes      | No       | Optional               | No                                   |          ❌ |
    317 /// | 9     | No       | Yes      | Required               | Yes                                  |          ❌ |
    318 /// | 10    | No       | Yes      | Required               | No                                   |          ❌ |
    319 /// | 11    | No       | Yes      | Optional               | Yes                                  |          ❌ |
    320 /// | 12    | No       | Yes      | Optional               | No                                   |          ❌ |
    321 /// | <a name="label13">13</a>    | No       | No       | Required               | Yes                                  |          ✅ |
    322 /// | 14    | No       | No       | Required               | No                                   |          ❌ |
    323 /// | 15    | No       | No       | Optional               | Yes                                  |          ❌ |
    324 /// | 16    | No       | No       | Optional               | No                                   |          ❌ |
    325 ///
    326 /// * All `Label`s with both `Password` and `Authenticator-side user verification` set to `Yes` are not recommended
    327 ///   since the verification done on the authenticator is likely the same "factor" as a password; thus it does not
    328 ///   add benefit but only serves as an annoyance to users.
    329 /// * All `Label`s with `Username` or `Password` set to `Yes` and `Client-side credential` set to `Required` are not
    330 ///   recommended since you may preclude authenticators that are storage constrained (e.g., security keys).
    331 /// * All `Label`s with `Username` set to `No` and `Client-side credential` set to `Optional` are not possible since
    332 ///   RPs would not have a way to identify the set of encrypted credentials to pass to the unknown user.
    333 /// * All `Label`s with `Password` and `Authenticator-side user verification` set to `No` are not recommended since
    334 ///   those are single-factor authentication schemes; thus anyone possessing the credential without also passing
    335 ///   some form of user verification (e.g., password) would authenticate.
    336 /// * [`Label 7`](#label7) is possible for RPs that are comfortable passing an encrypted credential to a potential user
    337 ///   without having that user first pass another form of authentication. For many RPs passing such information even
    338 ///   if encrypted is not desirable though.
    339 /// * [`Label 4`](#label4) is ideal as a single-factor flow incorporated within a wider multi-factor authentication (MFA)
    340 ///   setup. The easiest way to register such a credential is with
    341 ///   [`PublicKeyCredentialCreationOptions::second_factor`].
    342 /// * [`Label 13`](#label13) is ideal for passkey setups as it allows for pleasant UX where a user does not have to type a
    343 ///   username nor password while still being secured with MFA with one of the factors being based on public-key
    344 ///   cryptography which for many is the most secure form of single-factor authentication. The easiest way to register
    345 ///   such a credential is with [`PublicKeyCredentialCreationOptions::passkey`].
    346 ///
    347 /// Two other reasons one may prefer to construct client-side credentials is richer support for extensions (e.g.,
    348 /// [`largeBlobKey`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-largeBlobKey-extension)
    349 /// for CTAP 2.2 authenticators) and the ability to use both discoverable and nondiscoverable requests (i.e.,
    350 /// [`PublicKeyCredentialRequestOptions::allow_credentials`] is empty and not empty respectively). The former is not
    351 /// relevant for this library—at least currently—since the only extensions supported are applicable for both
    352 /// client-side and server-side credentials. The latter can be important especially if an RP wants the ability to
    353 /// seamlessly transition from a username and password scheme to a userless and passwordless one in the future.
    354 ///
    355 /// Note the table is purely informative. While helper functions
    356 /// (e.g., [`PublicKeyCredentialCreationOptions::passkey`]) only exist for [`Label 4`](#label4) and
    357 /// [`Label 13`](#label13), one can create any credential since all fields in [`PublicKeyCredentialCreationOptions`]
    358 /// and [`PublicKeyCredentialRequestOptions`] are accessible.
    359 pub mod request;
    360 /// Functionality for completing ceremonies.
    361 ///
    362 /// Read [`request`] for more information about what credentials one should create.
    363 pub mod response;
    364 #[doc(inline)]
    365 pub use crate::{
    366     request::{
    367         auth::{
    368             AuthenticationClientState, AuthenticationServerState, PublicKeyCredentialRequestOptions,
    369         },
    370         register::{
    371             PublicKeyCredentialCreationOptions, RegistrationClientState, RegistrationServerState,
    372         },
    373     },
    374     response::{
    375         auth::{
    376             Authentication, PasskeyAuthentication, PasskeyAuthentication16, PasskeyAuthentication64,
    377         },
    378         register::Registration,
    379     },
    380 };
    381 /// Error returned in [`RegCeremonyErr::Credential`] and [`AuthCeremonyErr::Credential`] as well as
    382 /// from [`AuthenticatedCredential::new`].
    383 #[derive(Clone, Copy, Debug)]
    384 pub enum CredentialErr {
    385     /// Variant when [`CredentialProtectionPolicy::UserVerificationRequired`], but
    386     /// [`DynamicState::user_verified`] is `false`.
    387     CredProtectUserVerificationRequiredWithoutUserVerified,
    388     /// Variant when [`AuthenticatorExtensionOutput::hmac_secret`] is `Some(true)`, but
    389     /// [`DynamicState::user_verified`] is `false`.
    390     HmacSecretWithoutUserVerified,
    391     /// Variant when [`AuthenticatorExtensionOutput::hmac_secret`] is `Some(true)`, but
    392     /// [`ClientExtensionsOutputs::prf`] is not `Some(AuthenticationExtensionsPRFOutputs { enabled: true })`.
    393     HmacSecretWithoutPrf,
    394     /// Variant when [`ClientExtensionsOutputs::prf`] is
    395     /// `Some(AuthenticationExtensionsPRFOutputs { enabled: true })`, but
    396     /// [`AuthenticatorExtensionOutput::hmac_secret`] is not `Some(true)`.
    397     PrfWithoutHmacSecret,
    398     /// Variant when [`ResidentKeyRequirement::Required`] was sent, but
    399     /// [`CredentialPropertiesOutput::rk`] is `Some(false)`.
    400     ResidentKeyRequiredServerCredentialCreated,
    401 }
    402 impl Display for CredentialErr {
    403     #[inline]
    404     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    405         f.write_str(match *self {
    406             Self::CredProtectUserVerificationRequiredWithoutUserVerified => {
    407                 "credProtect requires user verification, but the user is not verified"
    408             }
    409             Self::HmacSecretWithoutUserVerified => {
    410                 "hmac-secret is enabled, but the user is not verified"
    411             }
    412             Self::HmacSecretWithoutPrf => "hmac-secret was enabled but prf was not",
    413             Self::PrfWithoutHmacSecret => "prf was enabled, but hmac-secret was not",
    414             Self::ResidentKeyRequiredServerCredentialCreated => {
    415                 "server-side credential was created, but a client-side credential is required"
    416             }
    417         })
    418     }
    419 }
    420 impl Error for CredentialErr {}
    421 /// Checks if the `static_state` and `dynamic_state` are valid for a credential.
    422 ///
    423 /// # Errors
    424 ///
    425 /// Errors iff `static_state` or `dynamic_state` are invalid.
    426 fn verify_static_and_dynamic_state<T>(
    427     static_state: &StaticState<T>,
    428     dynamic_state: DynamicState,
    429 ) -> Result<(), CredentialErr> {
    430     if dynamic_state.user_verified {
    431         Ok(())
    432     } else if matches!(
    433         static_state.extensions.cred_protect,
    434         CredentialProtectionPolicy::UserVerificationRequired
    435     ) {
    436         Err(CredentialErr::CredProtectUserVerificationRequiredWithoutUserVerified)
    437     } else if static_state.extensions.hmac_secret.unwrap_or_default() {
    438         Err(CredentialErr::HmacSecretWithoutUserVerified)
    439     } else {
    440         Ok(())
    441     }
    442 }
    443 /// Registered credential that needs to be saved server-side to perform future
    444 /// [authentication ceremonies](https://www.w3.org/TR/webauthn-3/#authentication-ceremony) with
    445 /// [`AuthenticatedCredential`].
    446 ///
    447 /// When saving `RegisteredCredential` to persistent storage, one will almost always want to save the contained data
    448 /// separately. The reasons for this are the following:
    449 ///
    450 /// * [`CredentialId`]
    451 ///     * MUST be globally unique, and it will likely be easier to enforce such uniqueness when it's separate.
    452 ///     * Fetching the [`AuthenticatedCredential`] by [`Authentication::raw_id`] when completing the
    453 ///       authentication ceremony via [`AuthenticationServerState::verify`] will likely be easier than alternatives.
    454 /// * [`AuthTransports`]
    455 ///     * Fetching [`CredentialId`]s and associated `AuthTransports` by [`UserHandle`] will likely make credential
    456 ///       registration easier since one should set [`PublicKeyCredentialCreationOptions::exclude_credentials`] to
    457 ///       the [`PublicKeyCredentialDescriptor`]s belonging to a `UserHandle` in order to avoid accidentally
    458 ///       overwriting an existing credential on the authenticator.
    459 ///     * Fetching `CredentialId`s and associated `AuthTransports` by `UserHandle` will likely make starting
    460 ///       authentication ceremonies easier for non-discoverable requests (i.e., setting
    461 ///       [`PublicKeyCredentialRequestOptions::allow_credentials`] to a non-empty [`AllowedCredentials`]).
    462 /// * [`UserHandle`]
    463 ///     * Fetching the [`AuthenticatedCredential`] by [`Authentication::raw_id`] must also coincide with
    464 ///       verifying the associated `UserHandle` matches [`AuthenticatorAssertion::user_handle`] when `Some`.
    465 ///     * Fetching [`CredentialId`]s and associated [`AuthTransports`] by `UserHandle` will likely make credential
    466 ///       registration easier since one should set [`PublicKeyCredentialCreationOptions::exclude_credentials`] to
    467 ///       the [`PublicKeyCredentialDescriptor`]s belonging to a `UserHandle` in order to avoid accidentally
    468 ///       overwriting an existing credential on the authenticator.
    469 ///     * Fetching `CredentialId`s and associated `AuthTransports` by `UserHandle` will likely make starting
    470 ///       authentication ceremonies easier for non-discoverable requests (i.e., setting
    471 ///       [`PublicKeyCredentialRequestOptions::allow_credentials`] to a non-empty [`AllowedCredentials`]).
    472 /// * [`DynamicState`]
    473 ///     * `DynamicState` is the only part that is ever updated after a successful authentication ceremony
    474 ///       via [`AuthenticationServerState::verify`]. It being separate allows for smaller and quicker updates.
    475 /// * [`Metadata`]
    476 ///     * Informative data that is never used during authentication ceremonies; consequently, one may wish to
    477 ///       not even save this information.
    478 /// * [`StaticState`]
    479 ///     * All other data exists as part of `StaticState`.
    480 ///
    481 /// It is for those reasons that `RegisteredCredential` does not implement [`Encode`] or [`Decode`]; instead its parts
    482 /// do.
    483 ///
    484 /// Note that [`RpId`] and user information other than the `UserHandle` are not stored in `RegisteredCredential`.
    485 /// RPs that wish to store such information must do so on their own. Since user information is likely the same
    486 /// for a given `UserHandle` and `RpId` is likely static, it makes little sense to store such information
    487 /// automatically. Types like [`Username`] implement `Encode` and `Decode` to assist such a thing.
    488 ///
    489 /// When registering a credential, [`AttestedCredentialData::aaguid`], [`AttestedCredentialData::credential_id`],
    490 /// and [`AttestedCredentialData::credential_public_key`] will be the sources for [`Metadata::aaguid`],
    491 /// [`Self::id`], and [`StaticState::credential_public_key`] respectively. Additionally, there must be some way for
    492 /// the RP to know what `UserHandle` the [`Registration`] is associated with (e.g., a session cookie); thus the
    493 /// source of [`Self::user_id`] is the `UserHandle` passed to [`RegistrationServerState::verify`].
    494 ///
    495 /// The only way to create this is via `RegistrationServerState::verify`.
    496 #[derive(Debug)]
    497 pub struct RegisteredCredential<'reg, 'user> {
    498     /// The credential ID.
    499     ///
    500     /// For client-side credentials, this is a unique identifier; but for server-side
    501     /// credentials, this _is_ the credential (i.e., the encrypted private key and necessary information).
    502     id: CredentialId<&'reg [u8]>,
    503     /// Hints for how the client might communicate with the authenticator containing the credential.
    504     transports: AuthTransports,
    505     /// The identifier for the user.
    506     ///
    507     /// Unlike [`Self::id`] which is globally unique for an RP, this is unique up to "user" (i.e.,
    508     /// multiple [`CredentialId`]s will often exist for the same `UserHandle`).
    509     user_id: UserHandle<&'user [u8]>,
    510     /// Immutable state returned during registration.
    511     static_state: StaticState<UncompressedPubKey<'reg>>,
    512     /// State that can change during authentication ceremonies.
    513     dynamic_state: DynamicState,
    514     /// Metadata.
    515     metadata: Metadata<'reg>,
    516 }
    517 impl<'reg, 'user> RegisteredCredential<'reg, 'user> {
    518     /// The credential ID.
    519     ///
    520     /// For client-side credentials, this is a unique identifier; but for server-side
    521     /// credentials, this _is_ the credential (i.e., the encrypted private key and necessary information).
    522     #[inline]
    523     #[must_use]
    524     pub const fn id(&self) -> CredentialId<&'reg [u8]> {
    525         self.id
    526     }
    527     /// Hints for how the client might communicate with the authenticator containing the credential.
    528     #[inline]
    529     #[must_use]
    530     pub const fn transports(&self) -> AuthTransports {
    531         self.transports
    532     }
    533     /// The identifier for the user.
    534     ///
    535     /// Unlike [`Self::id`] which is globally unique for an RP, this is unique up to "user" (i.e.,
    536     /// multiple [`CredentialId`]s will often exist for the same `UserHandle`).
    537     #[inline]
    538     #[must_use]
    539     pub const fn user_id(&self) -> UserHandle<&'user [u8]> {
    540         self.user_id
    541     }
    542     /// Immutable state returned during registration.
    543     #[inline]
    544     #[must_use]
    545     pub const fn static_state(&self) -> StaticState<UncompressedPubKey<'reg>> {
    546         self.static_state
    547     }
    548     /// State that can change during authentication ceremonies.
    549     #[inline]
    550     #[must_use]
    551     pub const fn dynamic_state(&self) -> DynamicState {
    552         self.dynamic_state
    553     }
    554     /// Metadata.
    555     #[inline]
    556     #[must_use]
    557     pub const fn metadata(&self) -> Metadata<'reg> {
    558         self.metadata
    559     }
    560     /// Constructs a `RegisteredCredential` based on the passed arguments.
    561     ///
    562     /// # Errors
    563     ///
    564     /// Errors iff the passed arguments are invalid. Read [`CredentialErr`]
    565     /// for more information.
    566     #[inline]
    567     fn new<'a: 'reg, 'b: 'user>(
    568         id: CredentialId<&'a [u8]>,
    569         transports: AuthTransports,
    570         user_id: UserHandle<&'b [u8]>,
    571         static_state: StaticState<UncompressedPubKey<'a>>,
    572         dynamic_state: DynamicState,
    573         metadata: Metadata<'a>,
    574     ) -> Result<Self, CredentialErr> {
    575         verify_static_and_dynamic_state(&static_state, dynamic_state).and_then(|()| {
    576             // `verify_static_and_dynamic_state` already ensures that
    577             // `hmac-secret` is not `Some(true)` when `!dynamic_state.user_verified`;
    578             // thus we only need to check that one is not enabled without the other.
    579             if static_state.extensions.hmac_secret.unwrap_or_default() {
    580                 if metadata
    581                     .client_extension_results
    582                     .prf
    583                     .is_some_and(|prf| prf.enabled)
    584                 {
    585                     Ok(())
    586                 } else {
    587                     Err(CredentialErr::HmacSecretWithoutPrf)
    588                 }
    589             } else if metadata
    590                 .client_extension_results
    591                 .prf
    592                 .is_some_and(|prf| prf.enabled)
    593             {
    594                 Err(CredentialErr::PrfWithoutHmacSecret)
    595             } else {
    596                 Ok(())
    597             }
    598             .and_then(|()| {
    599                 if !matches!(metadata.resident_key, ResidentKeyRequirement::Required)
    600                     || metadata
    601                         .client_extension_results
    602                         .cred_props
    603                         .as_ref()
    604                         .is_none_or(|props| props.rk.is_none_or(convert::identity))
    605                 {
    606                     Ok(Self {
    607                         id,
    608                         transports,
    609                         user_id,
    610                         static_state,
    611                         dynamic_state,
    612                         metadata,
    613                     })
    614                 } else {
    615                     Err(CredentialErr::ResidentKeyRequiredServerCredentialCreated)
    616                 }
    617             })
    618         })
    619     }
    620     /// Returns the contained data consuming `self`.
    621     #[expect(
    622         clippy::type_complexity,
    623         reason = "type aliases with bounds are even more problematic at least until lazy_type_alias is stable"
    624     )]
    625     #[inline]
    626     #[must_use]
    627     pub const fn into_parts(
    628         self,
    629     ) -> (
    630         CredentialId<&'reg [u8]>,
    631         AuthTransports,
    632         UserHandle<&'user [u8]>,
    633         StaticState<UncompressedPubKey<'reg>>,
    634         DynamicState,
    635         Metadata<'reg>,
    636     ) {
    637         (
    638             self.id,
    639             self.transports,
    640             self.user_id,
    641             self.static_state,
    642             self.dynamic_state,
    643             self.metadata,
    644         )
    645     }
    646     /// Returns the contained data.
    647     #[expect(
    648         clippy::type_complexity,
    649         reason = "type aliases with bounds are even more problematic at least until lazy_type_alias is stable"
    650     )]
    651     #[inline]
    652     #[must_use]
    653     pub const fn as_parts(
    654         &self,
    655     ) -> (
    656         CredentialId<&'reg [u8]>,
    657         AuthTransports,
    658         UserHandle<&'user [u8]>,
    659         StaticState<UncompressedPubKey<'reg>>,
    660         DynamicState,
    661         Metadata<'reg>,
    662     ) {
    663         (
    664             self.id,
    665             self.transports,
    666             self.user_id,
    667             self.static_state,
    668             self.dynamic_state,
    669             self.metadata,
    670         )
    671     }
    672 }
    673 /// Credential used in authentication ceremonies.
    674 ///
    675 /// Similar to [`RegisteredCredential`] except designed to only contain the necessary data to complete
    676 /// authentication ceremonies. In particular there is no [`AuthTransports`] or [`Metadata`],
    677 /// [`StaticState::credential_public_key`] is [`CompressedPubKey`] that can own or borrow its data, [`Self::id`] is
    678 /// based on the [`CredentialId`] passed to [`Self::new`] which itself must be from [`Authentication::raw_id`], and
    679 /// [`Self::user_id`] is based on the [`UserHandle`] passed to [`Self::new`] which itself must be the value in
    680 /// persistent storage associated with the `CredentialId`. When [`AuthenticatorAssertion::user_handle`] is `Some`,
    681 /// this can be used for `Self::user_id` so long as it matches the value in persistent storage. Note it MUST be
    682 /// `Some` when using discoverable requests (i.e., [`PublicKeyCredentialRequestOptions::allow_credentials`] is
    683 /// empty); and when using non-discoverable requests (i.e., `PublicKeyCredentialRequestOptions::allow_credentials`
    684 /// is non-empty), one should already have the user handle (e.g., in a session cookie) which can also be used.
    685 ///
    686 /// Note `PublicKey` should be `CompressedPubKey` for this to be useful.
    687 ///
    688 /// The only way to create this is via `Self::new`.
    689 #[derive(Debug)]
    690 pub struct AuthenticatedCredential<'cred, 'user, PublicKey> {
    691     /// The credential ID.
    692     ///
    693     /// For client-side credentials, this is a unique identifier; but for server-side
    694     /// credentials, this _is_ the credential (i.e., the encrypted private key and necessary information).
    695     id: CredentialId<&'cred [u8]>,
    696     /// The identifier for the user.
    697     ///
    698     /// Unlike [`Self::id`] which is globally unique for an RP, this is unique up to "user" (i.e.,
    699     /// multiple [`CredentialId`]s will often exist for the same `UserHandle`).
    700     user_id: UserHandle<&'user [u8]>,
    701     /// Immutable state returned during registration.
    702     static_state: StaticState<PublicKey>,
    703     /// State that can change during authentication ceremonies.
    704     dynamic_state: DynamicState,
    705 }
    706 impl<'cred, 'user, PublicKey> AuthenticatedCredential<'cred, 'user, PublicKey> {
    707     /// The credential ID.
    708     ///
    709     /// For client-side credentials, this is a unique identifier; but for server-side
    710     /// credentials, this _is_ the credential (i.e., the encrypted private key and necessary information).
    711     #[inline]
    712     #[must_use]
    713     pub const fn id(&self) -> CredentialId<&'cred [u8]> {
    714         self.id
    715     }
    716     /// The identifier for the user.
    717     ///
    718     /// Unlike [`Self::id`] which is globally unique for an RP, this is unique up to "user" (i.e.,
    719     /// multiple [`CredentialId`]s will often exist for the same `UserHandle`).
    720     #[inline]
    721     #[must_use]
    722     pub const fn user_id(&self) -> UserHandle<&'user [u8]> {
    723         self.user_id
    724     }
    725     /// Immutable state returned during registration.
    726     #[inline]
    727     #[must_use]
    728     pub const fn static_state(&self) -> &StaticState<PublicKey> {
    729         &self.static_state
    730     }
    731     /// State that can change during authentication ceremonies.
    732     #[inline]
    733     #[must_use]
    734     pub const fn dynamic_state(&self) -> DynamicState {
    735         self.dynamic_state
    736     }
    737     /// Constructs an `AuthenticatedCredential` based on the passed arguments.
    738     ///
    739     /// # Errors
    740     ///
    741     /// Errors iff the passed arguments are invalid. Read [`CredentialErr`]
    742     /// for more information.
    743     #[cfg_attr(docsrs, doc(cfg(any(feature = "bin", feature = "custom"))))]
    744     #[cfg(any(feature = "bin", feature = "custom"))]
    745     #[inline]
    746     pub fn new<'a: 'cred, 'b: 'user>(
    747         id: CredentialId<&'a [u8]>,
    748         user_id: UserHandle<&'b [u8]>,
    749         static_state: StaticState<PublicKey>,
    750         dynamic_state: DynamicState,
    751     ) -> Result<Self, CredentialErr> {
    752         verify_static_and_dynamic_state(&static_state, dynamic_state).map(|()| Self {
    753             id,
    754             user_id,
    755             static_state,
    756             dynamic_state,
    757         })
    758     }
    759     /// Returns the contained data consuming `self`.
    760     #[expect(
    761         clippy::type_complexity,
    762         reason = "type aliases with bounds are even more problematic at least until lazy_type_alias is stable"
    763     )]
    764     #[inline]
    765     #[must_use]
    766     pub fn into_parts(
    767         self,
    768     ) -> (
    769         CredentialId<&'cred [u8]>,
    770         UserHandle<&'user [u8]>,
    771         StaticState<PublicKey>,
    772         DynamicState,
    773     ) {
    774         (self.id, self.user_id, self.static_state, self.dynamic_state)
    775     }
    776     /// Returns the contained data.
    777     #[expect(
    778         clippy::type_complexity,
    779         reason = "type aliases with bounds are even more problematic at least until lazy_type_alias is stable"
    780     )]
    781     #[inline]
    782     #[must_use]
    783     pub const fn as_parts(
    784         &self,
    785     ) -> (
    786         CredentialId<&'cred [u8]>,
    787         UserHandle<&'user [u8]>,
    788         &StaticState<PublicKey>,
    789         DynamicState,
    790     ) {
    791         (
    792             self.id,
    793             self.user_id,
    794             self.static_state(),
    795             self.dynamic_state,
    796         )
    797     }
    798 }
    799 /// Convenience aggregate error that rolls up all errors into one.
    800 #[derive(Debug)]
    801 pub enum AggErr {
    802     /// Variant when [`AsciiDomain::try_from`] errors.
    803     AsciiDomain(AsciiDomainErr),
    804     /// Variant when [`Url::from_str`] errors.
    805     Url(UrlErr),
    806     /// Variant when [`Scheme::try_from`] errors.
    807     Scheme(SchemeParseErr),
    808     /// Variant when [`DomainOrigin::try_from`] errors.
    809     DomainOrigin(DomainOriginParseErr),
    810     /// Variant when [`Port::from_str`] errors.
    811     Port(PortParseErr),
    812     /// Variant when [`PublicKeyCredentialRequestOptions::start_ceremony`] errors.
    813     RequestOptions(RequestOptionsErr),
    814     /// Variant when [`PublicKeyCredentialRequestOptions::second_factor`] errors.
    815     SecondFactor(SecondFactorErr),
    816     /// Variant when [`PublicKeyCredentialCreationOptions::start_ceremony`] errors.
    817     CreationOptions(CreationOptionsErr),
    818     /// Variant when [`Nickname::try_from`] errors.
    819     Nickname(NicknameErr),
    820     /// Variant when [`UserHandle::rand`] or [`UserHandle::decode`] error.
    821     UserHandle(UserHandleErr),
    822     /// Variant when [`Username::try_from`] errors.
    823     Username(UsernameErr),
    824     /// Variant when [`RegistrationServerState::verify`] errors.
    825     RegCeremony(RegCeremonyErr),
    826     /// Variant when [`AuthenticationServerState::verify`] errors.
    827     AuthCeremony(AuthCeremonyErr),
    828     /// Variant when [`AttestationObject::try_from`] errors.
    829     AttestationObject(AttestationObjectErr),
    830     /// Variant when [`register::AuthenticatorData::try_from`] errors.
    831     RegAuthenticatorData(RegAuthDataErr),
    832     /// Variant when [`auth::AuthenticatorData::try_from`] errors.
    833     AuthAuthenticatorData(AuthAuthDataErr),
    834     /// Variant when [`CollectedClientData::from_client_data_json`] errors.
    835     CollectedClientData(CollectedClientDataErr),
    836     /// Variant when [`CollectedClientData::from_client_data_json_relaxed`] errors or any of the [`Deserialize`]
    837     /// implementations error when relying on [`Deserializer`] or [`StreamDeserializer`].
    838     #[cfg_attr(docsrs, doc(cfg(feature = "serde_relaxed")))]
    839     #[cfg(feature = "serde_relaxed")]
    840     SerdeJson(SerdeJsonErr),
    841     /// Variant when [`Aaguid::try_from`] errors.
    842     Aaguid(AaguidErr),
    843     /// Variant when [`AuthTransports::decode`] errors.
    844     #[cfg_attr(docsrs, doc(cfg(feature = "bin")))]
    845     #[cfg(feature = "bin")]
    846     DecodeAuthTransports(DecodeAuthTransportsErr),
    847     /// Variant when [`StaticState::decode`] errors.
    848     #[cfg_attr(docsrs, doc(cfg(feature = "bin")))]
    849     #[cfg(feature = "bin")]
    850     DecodeStaticState(DecodeStaticStateErr),
    851     /// Variant when [`DynamicState::decode`] errors.
    852     #[cfg_attr(docsrs, doc(cfg(feature = "bin")))]
    853     #[cfg(feature = "bin")]
    854     DecodeDynamicState(DecodeDynamicStateErr),
    855     /// Variant when [`Nickname::decode`] errors.
    856     #[cfg_attr(docsrs, doc(cfg(feature = "bin")))]
    857     #[cfg(feature = "bin")]
    858     DecodeNickname(DecodeNicknameErr),
    859     /// Variant when [`Username::decode`] errors.
    860     #[cfg_attr(docsrs, doc(cfg(feature = "bin")))]
    861     #[cfg(feature = "bin")]
    862     DecodeUsername(DecodeUsernameErr),
    863     /// Variant when [`RegistrationServerState::decode`] errors.
    864     #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))]
    865     #[cfg(feature = "serializable_server_state")]
    866     DecodeRegistrationServerState(DecodeRegistrationServerStateErr),
    867     /// Variant when [`AuthenticationServerState::decode`] errors.
    868     #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))]
    869     #[cfg(feature = "serializable_server_state")]
    870     DecodeAuthenticationServerState(DecodeAuthenticationServerStateErr),
    871     /// Variant when [`RegistrationServerState::encode`] errors.
    872     #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))]
    873     #[cfg(feature = "serializable_server_state")]
    874     EncodeRegistrationServerState(SystemTimeError),
    875     /// Variant when [`AuthenticationServerState::encode`] errors.
    876     #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))]
    877     #[cfg(feature = "serializable_server_state")]
    878     EncodeAuthenticationServerState(EncodeAuthenticationServerStateErr),
    879     /// Variant when [`AuthenticatedCredential::new`] errors.
    880     #[cfg_attr(docsrs, doc(cfg(any(feature = "bin", feature = "custom"))))]
    881     #[cfg(any(feature = "bin", feature = "custom"))]
    882     Credential(CredentialErr),
    883     /// Variant when [`CredentialId::try_from`] or [`CredentialId::decode`] errors.
    884     #[cfg_attr(docsrs, doc(cfg(any(feature = "bin", feature = "custom"))))]
    885     #[cfg(any(feature = "bin", feature = "custom"))]
    886     CredentialId(CredentialIdErr),
    887 }
    888 impl From<AsciiDomainErr> for AggErr {
    889     #[inline]
    890     fn from(value: AsciiDomainErr) -> Self {
    891         Self::AsciiDomain(value)
    892     }
    893 }
    894 impl From<UrlErr> for AggErr {
    895     #[inline]
    896     fn from(value: UrlErr) -> Self {
    897         Self::Url(value)
    898     }
    899 }
    900 impl From<SchemeParseErr> for AggErr {
    901     #[inline]
    902     fn from(value: SchemeParseErr) -> Self {
    903         Self::Scheme(value)
    904     }
    905 }
    906 impl From<DomainOriginParseErr> for AggErr {
    907     #[inline]
    908     fn from(value: DomainOriginParseErr) -> Self {
    909         Self::DomainOrigin(value)
    910     }
    911 }
    912 impl From<PortParseErr> for AggErr {
    913     #[inline]
    914     fn from(value: PortParseErr) -> Self {
    915         Self::Port(value)
    916     }
    917 }
    918 impl From<RequestOptionsErr> for AggErr {
    919     #[inline]
    920     fn from(value: RequestOptionsErr) -> Self {
    921         Self::RequestOptions(value)
    922     }
    923 }
    924 impl From<SecondFactorErr> for AggErr {
    925     #[inline]
    926     fn from(value: SecondFactorErr) -> Self {
    927         Self::SecondFactor(value)
    928     }
    929 }
    930 impl From<CreationOptionsErr> for AggErr {
    931     #[inline]
    932     fn from(value: CreationOptionsErr) -> Self {
    933         Self::CreationOptions(value)
    934     }
    935 }
    936 impl From<NicknameErr> for AggErr {
    937     #[inline]
    938     fn from(value: NicknameErr) -> Self {
    939         Self::Nickname(value)
    940     }
    941 }
    942 impl From<UserHandleErr> for AggErr {
    943     #[inline]
    944     fn from(value: UserHandleErr) -> Self {
    945         Self::UserHandle(value)
    946     }
    947 }
    948 impl From<UsernameErr> for AggErr {
    949     #[inline]
    950     fn from(value: UsernameErr) -> Self {
    951         Self::Username(value)
    952     }
    953 }
    954 impl From<RegCeremonyErr> for AggErr {
    955     #[inline]
    956     fn from(value: RegCeremonyErr) -> Self {
    957         Self::RegCeremony(value)
    958     }
    959 }
    960 impl From<AuthCeremonyErr> for AggErr {
    961     #[inline]
    962     fn from(value: AuthCeremonyErr) -> Self {
    963         Self::AuthCeremony(value)
    964     }
    965 }
    966 impl From<AttestationObjectErr> for AggErr {
    967     #[inline]
    968     fn from(value: AttestationObjectErr) -> Self {
    969         Self::AttestationObject(value)
    970     }
    971 }
    972 impl From<RegAuthDataErr> for AggErr {
    973     #[inline]
    974     fn from(value: RegAuthDataErr) -> Self {
    975         Self::RegAuthenticatorData(value)
    976     }
    977 }
    978 impl From<AuthAuthDataErr> for AggErr {
    979     #[inline]
    980     fn from(value: AuthAuthDataErr) -> Self {
    981         Self::AuthAuthenticatorData(value)
    982     }
    983 }
    984 impl From<CollectedClientDataErr> for AggErr {
    985     #[inline]
    986     fn from(value: CollectedClientDataErr) -> Self {
    987         Self::CollectedClientData(value)
    988     }
    989 }
    990 #[cfg_attr(docsrs, doc(cfg(feature = "serde_relaxed")))]
    991 #[cfg(feature = "serde_relaxed")]
    992 impl From<SerdeJsonErr> for AggErr {
    993     #[inline]
    994     fn from(value: SerdeJsonErr) -> Self {
    995         Self::SerdeJson(value)
    996     }
    997 }
    998 impl From<AaguidErr> for AggErr {
    999     #[inline]
   1000     fn from(value: AaguidErr) -> Self {
   1001         Self::Aaguid(value)
   1002     }
   1003 }
   1004 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))]
   1005 #[cfg(feature = "bin")]
   1006 impl From<DecodeAuthTransportsErr> for AggErr {
   1007     #[inline]
   1008     fn from(value: DecodeAuthTransportsErr) -> Self {
   1009         Self::DecodeAuthTransports(value)
   1010     }
   1011 }
   1012 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))]
   1013 #[cfg(feature = "bin")]
   1014 impl From<DecodeStaticStateErr> for AggErr {
   1015     #[inline]
   1016     fn from(value: DecodeStaticStateErr) -> Self {
   1017         Self::DecodeStaticState(value)
   1018     }
   1019 }
   1020 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))]
   1021 #[cfg(feature = "bin")]
   1022 impl From<DecodeDynamicStateErr> for AggErr {
   1023     #[inline]
   1024     fn from(value: DecodeDynamicStateErr) -> Self {
   1025         Self::DecodeDynamicState(value)
   1026     }
   1027 }
   1028 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))]
   1029 #[cfg(feature = "bin")]
   1030 impl From<DecodeNicknameErr> for AggErr {
   1031     #[inline]
   1032     fn from(value: DecodeNicknameErr) -> Self {
   1033         Self::DecodeNickname(value)
   1034     }
   1035 }
   1036 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))]
   1037 #[cfg(feature = "bin")]
   1038 impl From<DecodeUsernameErr> for AggErr {
   1039     #[inline]
   1040     fn from(value: DecodeUsernameErr) -> Self {
   1041         Self::DecodeUsername(value)
   1042     }
   1043 }
   1044 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))]
   1045 #[cfg(feature = "serializable_server_state")]
   1046 impl From<DecodeRegistrationServerStateErr> for AggErr {
   1047     #[inline]
   1048     fn from(value: DecodeRegistrationServerStateErr) -> Self {
   1049         Self::DecodeRegistrationServerState(value)
   1050     }
   1051 }
   1052 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))]
   1053 #[cfg(feature = "serializable_server_state")]
   1054 impl From<DecodeAuthenticationServerStateErr> for AggErr {
   1055     #[inline]
   1056     fn from(value: DecodeAuthenticationServerStateErr) -> Self {
   1057         Self::DecodeAuthenticationServerState(value)
   1058     }
   1059 }
   1060 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))]
   1061 #[cfg(feature = "serializable_server_state")]
   1062 impl From<SystemTimeError> for AggErr {
   1063     #[inline]
   1064     fn from(value: SystemTimeError) -> Self {
   1065         Self::EncodeRegistrationServerState(value)
   1066     }
   1067 }
   1068 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))]
   1069 #[cfg(feature = "serializable_server_state")]
   1070 impl From<EncodeAuthenticationServerStateErr> for AggErr {
   1071     #[inline]
   1072     fn from(value: EncodeAuthenticationServerStateErr) -> Self {
   1073         Self::EncodeAuthenticationServerState(value)
   1074     }
   1075 }
   1076 #[cfg_attr(docsrs, doc(cfg(any(feature = "bin", feature = "custom"))))]
   1077 #[cfg(any(feature = "bin", feature = "custom"))]
   1078 impl From<CredentialErr> for AggErr {
   1079     #[inline]
   1080     fn from(value: CredentialErr) -> Self {
   1081         Self::Credential(value)
   1082     }
   1083 }
   1084 #[cfg_attr(docsrs, doc(cfg(any(feature = "bin", feature = "custom"))))]
   1085 #[cfg(any(feature = "bin", feature = "custom"))]
   1086 impl From<CredentialIdErr> for AggErr {
   1087     #[inline]
   1088     fn from(value: CredentialIdErr) -> Self {
   1089         Self::CredentialId(value)
   1090     }
   1091 }
   1092 impl Display for AggErr {
   1093     #[inline]
   1094     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
   1095         match *self {
   1096             Self::AsciiDomain(err) => err.fmt(f),
   1097             Self::Url(err) => err.fmt(f),
   1098             Self::Scheme(err) => err.fmt(f),
   1099             Self::DomainOrigin(ref err) => err.fmt(f),
   1100             Self::Port(ref err) => err.fmt(f),
   1101             Self::RequestOptions(err) => err.fmt(f),
   1102             Self::SecondFactor(err) => err.fmt(f),
   1103             Self::CreationOptions(err) => err.fmt(f),
   1104             Self::Nickname(err) => err.fmt(f),
   1105             Self::UserHandle(err) => err.fmt(f),
   1106             Self::Username(err) => err.fmt(f),
   1107             Self::RegCeremony(ref err) => err.fmt(f),
   1108             Self::AuthCeremony(ref err) => err.fmt(f),
   1109             Self::AttestationObject(err) => err.fmt(f),
   1110             Self::RegAuthenticatorData(err) => err.fmt(f),
   1111             Self::AuthAuthenticatorData(err) => err.fmt(f),
   1112             Self::CollectedClientData(ref err) => err.fmt(f),
   1113             #[cfg(feature = "serde_relaxed")]
   1114             Self::SerdeJson(ref err) => err.fmt(f),
   1115             Self::Aaguid(err) => err.fmt(f),
   1116             #[cfg(feature = "bin")]
   1117             Self::DecodeAuthTransports(err) => err.fmt(f),
   1118             #[cfg(feature = "bin")]
   1119             Self::DecodeStaticState(err) => err.fmt(f),
   1120             #[cfg(feature = "bin")]
   1121             Self::DecodeDynamicState(err) => err.fmt(f),
   1122             #[cfg(feature = "bin")]
   1123             Self::DecodeNickname(err) => err.fmt(f),
   1124             #[cfg(feature = "bin")]
   1125             Self::DecodeUsername(err) => err.fmt(f),
   1126             #[cfg(feature = "serializable_server_state")]
   1127             Self::DecodeRegistrationServerState(err) => err.fmt(f),
   1128             #[cfg(feature = "serializable_server_state")]
   1129             Self::DecodeAuthenticationServerState(err) => err.fmt(f),
   1130             #[cfg(feature = "serializable_server_state")]
   1131             Self::EncodeRegistrationServerState(ref err) => err.fmt(f),
   1132             #[cfg(feature = "serializable_server_state")]
   1133             Self::EncodeAuthenticationServerState(ref err) => err.fmt(f),
   1134             #[cfg(any(feature = "bin", feature = "custom"))]
   1135             Self::Credential(err) => err.fmt(f),
   1136             #[cfg(any(feature = "bin", feature = "custom"))]
   1137             Self::CredentialId(err) => err.fmt(f),
   1138         }
   1139     }
   1140 }
   1141 impl Error for AggErr {}
   1142 /// Calculates the number of bytes needed to encode an input of length `n` bytes into base64url.
   1143 ///
   1144 /// `Some` is returned iff the encoded length does not exceed [`isize::MAX`].
   1145 #[expect(
   1146     clippy::arithmetic_side_effects,
   1147     clippy::as_conversions,
   1148     clippy::cast_possible_wrap,
   1149     clippy::cast_sign_loss,
   1150     clippy::integer_division,
   1151     clippy::integer_division_remainder_used,
   1152     reason = "proof and comment justifies their correctness"
   1153 )]
   1154 const fn base64url_nopad_len(n: usize) -> Option<usize> {
   1155     // 256^n is the number of distinct values of the input. Let the base64 encoding in a URL safe
   1156     // way without padding of the input be O. There are 64 possible values each byte in O can be; thus we must find
   1157     // the minimum nonnegative integer m such that:
   1158     // 64^m = (2^6)^m = 2^(6m) >= 256^n = (2^8)^n = 2^(8n)
   1159     // <==>
   1160     // lg(2^(6m)) = 6m >= lg(2^(8n)) = 8n   lg is defined on all positive reals which 2^(6m) and 2^(8n) are
   1161     // <==>
   1162     // m >= 8n/6 = 4n/3
   1163     // Clearly that corresponds to m = ⌈4n/3⌉; thus:
   1164 
   1165     let (quot, rem) = (n / 3, n % 3);
   1166     // Actual construction of the encoded output requires the allocation to take no more than `isize::MAX`
   1167     // bytes; thus we must detect overflow of it and not `usize::MAX`.
   1168     // isize::MAX = usize::MAX / 2 >= usize::MAX / 3; thus this `as` conversion is lossless.
   1169     match (quot as isize).checked_mul(4) {
   1170         // If multiplying by 4 caused overflow, then multiplying by 4 and adding by 3 would also.
   1171         None => None,
   1172         // This won't overflow since this maxes at `isize::MAX` since
   1173         // `n` <= ⌊3*isize::MAX/4⌋; thus `quot` <= ⌊isize::MAX/4⌋.
   1174         // `n` can be partitioned into 4 possibilities:
   1175         // (1) n ≡ 0 (mod 4) = 4quot + 0
   1176         // (2) n ≡ 1 (mod 4) = 4quot + 1
   1177         // (3) n ≡ 2 (mod 4) = 4quot + 2
   1178         // (4) n ≡ 3 (mod 4) = 4quot + 3
   1179         // For (1), rem is 0; thus 4quot + 0 = `n` which is fine.
   1180         // For (2), rem is 1; thus 4quot + 2 = n - 1 + 2 = n + 1 <= ⌊3*isize::MAX/4⌋ + 1 <= isize::MAX for
   1181         // isize::MAX > 0. Clearly `isize::MAX > 0`; otherwise we couldn't allocate anything.
   1182         // For (3), rem is 2; thus 4quot + 3 = n - 2 + 3 = n + 1 <= ⌊3*isize::MAX/4⌋ + 1 <= isize::MAX for
   1183         // isize::MAX > 0. Clearly `isize::MAX > 0`; otherwise we couldn't allocate anything.
   1184         // For (4), rem is 3; thus 4quot + 4 = n - 3 + 4 = n + 1 <= ⌊3*isize::MAX/4⌋ + 1 <= isize::MAX for
   1185         // isize::MAX > 0. Clearly `isize::MAX > 0`; otherwise we couldn't allocate anything.
   1186         //
   1187         // `val >= 0`; thus we can cast it to `usize` via `as` in a lossless way.
   1188         // Thus this is free from overflow, underflow, and a lossy conversion.
   1189         Some(val) => Some(val as usize + (4 * rem).div_ceil(3)),
   1190     }
   1191 }
   1192 /// Calculates the number of bytes a base64url-encoded input of length `n` bytes will be decoded into.
   1193 ///
   1194 /// `Some` is returned iff `n` is a valid input length.
   1195 ///
   1196 /// Note `n` must not only be a valid length mathematically but also represent a possible allocation of that
   1197 /// many bytes. Since only allocations <= [`isize::MAX`] are possible, this will always return `None` when
   1198 /// `n > isize::MAX`.
   1199 #[cfg(feature = "serde")]
   1200 #[expect(
   1201     clippy::arithmetic_side_effects,
   1202     clippy::as_conversions,
   1203     clippy::integer_division_remainder_used,
   1204     reason = "proof and comment justifies their correctness"
   1205 )]
   1206 const fn base64url_nopad_decode_len(n: usize) -> Option<usize> {
   1207     // 64^n is the number of distinct values of the input. Let the decoded output be O.
   1208     // There are 256 possible values each byte in O can be; thus we must find
   1209     // the maximum nonnegative integer m such that:
   1210     // 256^m = (2^8)^m = 2^(8m) <= 64^n = (2^6)^n = 2^(6n)
   1211     // <==>
   1212     // lg(2^(8m)) = 8m <= lg(2^(6n)) = 6n   lg is defined on all positive reals which 2^(8m) and 2^(6n) are
   1213     // <==>
   1214     // m <= 6n/8 = 3n/4
   1215     // Clearly that corresponds to m = ⌊3n/4⌋.
   1216     //
   1217     // There are three partitions for m:
   1218     // (1) m ≡ 0 (mod 3) = 3i
   1219     // (2) m ≡ 1 (mod 3) = 3i + 1
   1220     // (3) m ≡ 2 (mod 3) = 3i + 2
   1221     //
   1222     // From `crate::base64url_nopad_len`, we know that the encoded length, n, of an input of length m is n = ⌈4m/3⌉.
   1223     // The encoded length of (1) is thus n = ⌈4(3i)/3⌉ = ⌈4i⌉ = 4i ≡ 0 (mod 4).
   1224     // The encoded length of (2) is thus n = ⌈4(3i + 1)/3⌉ = ⌈4i + 4/3⌉ = 4i + 2 ≡ 2 (mod 4).
   1225     // The encoded length of (3) is thus n = ⌈4(3i + 2)/3⌉ = ⌈4i + 8/3⌉ = 4i + 3 ≡ 3 (mod 4).
   1226     //
   1227     // Thus if n ≡ 1 (mod 4), it is never a valid length.
   1228     //
   1229     // Let n be the length of a possible encoded output of an input of length m.
   1230     // We know from above that n ≢ 1 (mod 4), this leaves three possibilities:
   1231     // (1) n ≡ 0 (mod 4) = 4i
   1232     // (2) n ≡ 2 (mod 4) = 4i + 2
   1233     // (3) n ≡ 3 (mod 4) = 4i + 3
   1234     //
   1235     // For (1) an input of length 3i is the inverse since ⌈4(3i)/3⌉ = 4i.
   1236     // For (2) an input of length 3i + 1 is the inverse since ⌈4(3i + 1)/3⌉ = ⌈4i + 4/3⌉ = 4i + 2.
   1237     // For (3) an input of length 3i + 2 is the inverse since ⌈4(3i + 2)/3⌉ = ⌈4i + 8/3⌉ = 4i + 3.
   1238     //
   1239     // Consequently n is a valid length of an encoded output iff n ≢ 1 (mod 4).
   1240 
   1241     // `isize::MAX >= 0 >= usize::MIN`; thus this conversion is lossless.
   1242     if n % 4 == 1 || n > isize::MAX as usize {
   1243         None
   1244     } else {
   1245         let (quot, rem) = (n >> 2u8, n % 4);
   1246         // 4quot + rem = n
   1247         // rem <= 3
   1248         // 3rem <= 9
   1249         // 3rem/4 <= 2
   1250         // 3quot + 3rem/4 <= 4quot + rem
   1251         // Thus no operation causes overflow or underflow.
   1252         Some((3 * quot) + ((3 * rem) >> 2u8))
   1253     }
   1254 }