webauthn_rp

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

lib.rs (68084B)


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