webauthn_rp

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

lib.rs (74255B)


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