webauthn_rp

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

auth.rs (69810B)


      1 #[cfg(test)]
      2 mod tests;
      3 #[cfg(doc)]
      4 use super::{
      5     super::response::{
      6         CollectedClientData, Flag,
      7         auth::AuthenticatorData,
      8         register::{
      9             AuthenticatorExtensionOutputStaticState, ClientExtensionsOutputsStaticState,
     10             DynamicState, StaticState,
     11         },
     12     },
     13     AsciiDomain, AsciiDomainStatic, DomainOrigin, Url,
     14     register::{self, PublicKeyCredentialCreationOptions},
     15 };
     16 use super::{
     17     super::{
     18         AuthenticatedCredential,
     19         response::{
     20             AuthenticatorAttachment,
     21             auth::{
     22                 Authentication, AuthenticatorExtensionOutput, DiscoverableAuthentication,
     23                 HmacSecret, NonDiscoverableAuthentication,
     24                 error::{AuthCeremonyErr, ExtensionErr, OneOrTwo},
     25             },
     26             register::{CompressedPubKey, CredentialProtectionPolicy},
     27         },
     28     },
     29     Backup, BackupReq, Ceremony, CeremonyOptions, Challenge, CredentialId,
     30     CredentialMediationRequirement, Credentials, ExtensionReq, FIVE_MINUTES, Hints, Origin,
     31     PrfInput, PublicKeyCredentialDescriptor, RpId, SentChallenge, TimedCeremony,
     32     UserVerificationRequirement,
     33     auth::error::{InvalidTimeout, NonDiscoverableCredentialRequestOptionsErr},
     34 };
     35 use core::{
     36     borrow::Borrow,
     37     cmp::Ordering,
     38     hash::{Hash, Hasher},
     39     num::{NonZeroU32, NonZeroU64},
     40     time::Duration,
     41 };
     42 #[cfg(any(doc, not(feature = "serializable_server_state")))]
     43 use std::time::Instant;
     44 #[cfg(any(doc, feature = "serializable_server_state"))]
     45 use std::time::SystemTime;
     46 /// Contains error types.
     47 pub mod error;
     48 /// Contains functionality to serialize data to a client.
     49 #[cfg(feature = "serde")]
     50 pub mod ser;
     51 /// Contains functionality to (de)serialize [`DiscoverableAuthenticationServerState`] and
     52 /// [`NonDiscoverableAuthenticationServerState`] to a data store.
     53 #[cfg(feature = "serializable_server_state")]
     54 pub mod ser_server_state;
     55 /// Controls how [signature counter](https://www.w3.org/TR/webauthn-3/#signature-counter) is enforced.
     56 ///
     57 /// Note that if the previous signature counter is positive and the new counter is not strictly greater, then the
     58 /// authenticator is likely a clone (i.e., there are at least two copies of the private key).
     59 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
     60 pub enum SignatureCounterEnforcement {
     61     /// Fail the authentication ceremony if the counter is less than or equal to the previous value when the
     62     /// previous value is positive.
     63     #[default]
     64     Fail,
     65     /// When the counter is less than the previous value, don't fail and update the value.
     66     ///
     67     /// Note in the special case that the new signature counter is 0, [`DynamicState::sign_count`] _won't_
     68     /// be updated since that would allow an attacker to permanently disable the counter.
     69     Update,
     70     /// When the counter is less than the previous value, don't fail but don't update the value.
     71     Ignore,
     72 }
     73 impl SignatureCounterEnforcement {
     74     /// Validates the signature counter based on `self`.
     75     const fn validate(self, prev: u32, cur: u32) -> Result<u32, AuthCeremonyErr> {
     76         if prev == 0 || cur > prev {
     77             Ok(cur)
     78         } else {
     79             match self {
     80                 Self::Fail => Err(AuthCeremonyErr::SignatureCounter),
     81                 // When the new counter is `0`, we use the previous counter to avoid an attacker from
     82                 // being able to permanently disable it.
     83                 Self::Update => Ok(if cur == 0 { prev } else { cur }),
     84                 Self::Ignore => Ok(prev),
     85             }
     86         }
     87     }
     88 }
     89 /// Owned version of [`PrfInput`].
     90 ///
     91 /// When relying on [`NonDiscoverableCredentialRequestOptions`], it's recommended to use credential-specific PRF
     92 /// inputs that are continuously rolled over. One uses this type for such a thing.
     93 #[derive(Clone, Debug, Eq, PartialEq)]
     94 pub struct PrfInputOwned {
     95     /// [`first`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-first).
     96     pub first: Vec<u8>,
     97     /// [`second`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-second).
     98     pub second: Option<Vec<u8>>,
     99     /// Note this is only applicable for authenticators that implement the
    100     /// [`prf`](https://www.w3.org/TR/webauthn-3/#prf-extension) extension on top of the
    101     /// [`hmac-secret`](https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#sctn-hmac-secret-extension)
    102     /// extension since the data is encrypted and is part of the [`AuthenticatorData`].
    103     pub ext_req: ExtensionReq,
    104 }
    105 /// The [defined extensions](https://www.w3.org/TR/webauthn-3/#sctn-defined-extensions) to send to the client.
    106 #[derive(Clone, Copy, Debug)]
    107 pub struct Extension<'prf_first, 'prf_second> {
    108     /// [`prf`](https://www.w3.org/TR/webauthn-3/#prf-extension).
    109     ///
    110     /// If both [`CredentialSpecificExtension::prf`] and this are [`Some`], then `CredentialSpecificExtension::prf`
    111     /// takes priority.
    112     ///
    113     /// Note `ExtensionReq` is only applicable for authenticators that implement the
    114     /// [`prf`](https://www.w3.org/TR/webauthn-3/#prf-extension) extension on top of the
    115     /// [`hmac-secret`](https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#sctn-hmac-secret-extension)
    116     /// extension since the data is encrypted and is part of the [`AuthenticatorData`].
    117     pub prf: Option<(PrfInput<'prf_first, 'prf_second>, ExtensionReq)>,
    118 }
    119 impl<'prf_first, 'prf_second> Extension<'prf_first, 'prf_second> {
    120     /// Returns an `Extension` with [`Self::prf`] set to `None`.
    121     #[inline]
    122     #[must_use]
    123     pub const fn none() -> Self {
    124         Self { prf: None }
    125     }
    126     /// Returns an `Extension` with [`Self::prf`] set to `None`.
    127     #[expect(single_use_lifetimes, reason = "false positive")]
    128     #[inline]
    129     #[must_use]
    130     pub const fn with_prf<'a: 'prf_first, 'b: 'prf_second>(
    131         input: PrfInput<'a, 'b>,
    132         req: ExtensionReq,
    133     ) -> Self {
    134         Self {
    135             prf: Some((input, req)),
    136         }
    137     }
    138 }
    139 impl Default for Extension<'_, '_> {
    140     /// Same as [`Self::none`].
    141     #[inline]
    142     fn default() -> Self {
    143         Self::none()
    144     }
    145 }
    146 /// The [defined extensions](https://www.w3.org/TR/webauthn-3/#sctn-defined-extensions) to send to the client that
    147 /// are credential-specific which among other things implies a non-discoverable request.
    148 #[derive(Clone, Debug, Default)]
    149 pub struct CredentialSpecificExtension {
    150     /// [`prf`](https://www.w3.org/TR/webauthn-3/#prf-extension).
    151     ///
    152     /// If both [`Extension::prf`] and this are [`Some`], then this take priority.
    153     pub prf: Option<PrfInputOwned>,
    154 }
    155 /// Registered credential used in
    156 /// [`allowCredentials`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-allowcredentials).
    157 #[derive(Clone, Debug)]
    158 pub struct AllowedCredential {
    159     /// The registered credential.
    160     pub credential: PublicKeyCredentialDescriptor<Box<[u8]>>,
    161     /// Credential-specific extensions.
    162     pub extension: CredentialSpecificExtension,
    163 }
    164 impl From<PublicKeyCredentialDescriptor<Box<[u8]>>> for AllowedCredential {
    165     #[inline]
    166     fn from(credential: PublicKeyCredentialDescriptor<Box<[u8]>>) -> Self {
    167         Self {
    168             credential,
    169             extension: CredentialSpecificExtension::default(),
    170         }
    171     }
    172 }
    173 impl From<AllowedCredential> for PublicKeyCredentialDescriptor<Box<[u8]>> {
    174     #[inline]
    175     fn from(credential: AllowedCredential) -> Self {
    176         credential.credential
    177     }
    178 }
    179 /// Queue of unique [`AllowedCredential`]s.
    180 #[derive(Clone, Debug, Default)]
    181 pub struct AllowedCredentials {
    182     /// Allowed credentials.
    183     creds: Vec<AllowedCredential>,
    184     /// Number of `AllowedCredential`s that have PRF inputs.
    185     ///
    186     /// Useful to help serialization.
    187     prf_count: usize,
    188 }
    189 impl Credentials for AllowedCredentials {
    190     type Credential = AllowedCredential;
    191     /// # Examples
    192     ///
    193     /// ```
    194     /// # use webauthn_rp::request::{auth::AllowedCredentials, Credentials};
    195     /// assert!(AllowedCredentials::with_capacity(1).as_ref().is_empty());
    196     /// ```
    197     #[inline]
    198     fn with_capacity(capacity: usize) -> Self {
    199         Self {
    200             creds: Vec::with_capacity(capacity),
    201             prf_count: 0,
    202         }
    203     }
    204     /// # Examples
    205     ///
    206     /// ```
    207     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    208     /// # use webauthn_rp::{bin::Decode, response::bin::DecodeAuthTransportsErr};
    209     /// # use webauthn_rp::{
    210     /// #     request::{auth::AllowedCredentials, PublicKeyCredentialDescriptor, Credentials},
    211     /// #     response::{AuthTransports, CredentialId},
    212     /// # };
    213     /// /// Retrieves the `AuthTransports` associated with the unique `cred_id`
    214     /// /// from the database.
    215     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    216     /// fn get_transports(cred_id: CredentialId<&[u8]>) -> Result<AuthTransports, DecodeAuthTransportsErr> {
    217     ///     // ⋮
    218     /// #     AuthTransports::decode(32)
    219     /// }
    220     /// let mut creds = AllowedCredentials::with_capacity(1);
    221     /// assert!(creds.as_ref().is_empty());
    222     /// // `CredentialId::try_from` only exists when `custom` is enabled; and even then, it is
    223     /// // likely never needed since the `CredentialId` was originally sent from the client and is likely
    224     /// // stored in a database which would be fetched by `UserHandle` or `Authentication::raw_id`.
    225     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    226     /// let id = CredentialId::try_from(vec![0; 16].into_boxed_slice())?;
    227     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    228     /// let transports = get_transports((&id).into())?;
    229     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    230     /// assert!(creds.push(PublicKeyCredentialDescriptor { id, transports }.into()));
    231     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    232     /// let id_copy = CredentialId::try_from(vec![0; 16].into_boxed_slice())?;
    233     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    234     /// let transports_2 = AuthTransports::NONE;
    235     /// // Duplicate `CredentialId`s don't get added.
    236     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    237     /// assert!(!creds.push(
    238     ///     PublicKeyCredentialDescriptor {
    239     ///         id: id_copy,
    240     ///         transports: transports_2
    241     ///     }
    242     ///     .into()
    243     /// ));
    244     /// # Ok::<_, webauthn_rp::AggErr>(())
    245     /// ```
    246     #[expect(
    247         clippy::arithmetic_side_effects,
    248         reason = "comment explains how overflow is not possible"
    249     )]
    250     #[inline]
    251     fn push(&mut self, cred: Self::Credential) -> bool {
    252         self.creds
    253             .iter()
    254             .try_fold((), |(), c| {
    255                 if c.credential.id == cred.credential.id {
    256                     Err(())
    257                 } else {
    258                     Ok(())
    259                 }
    260             })
    261             .is_ok_and(|()| {
    262                 // This can't overflow since `self.creds.push` would `panic` since
    263                 // `self.prf_count <= self.creds.len()`.
    264                 self.prf_count += usize::from(cred.extension.prf.is_some());
    265                 self.creds.push(cred);
    266                 true
    267             })
    268     }
    269     #[inline]
    270     fn len(&self) -> usize {
    271         self.creds.len()
    272     }
    273 }
    274 impl AsRef<[AllowedCredential]> for AllowedCredentials {
    275     #[inline]
    276     fn as_ref(&self) -> &[AllowedCredential] {
    277         self.creds.as_slice()
    278     }
    279 }
    280 impl From<&AllowedCredentials> for Box<[CredInfo]> {
    281     #[inline]
    282     fn from(value: &AllowedCredentials) -> Self {
    283         let len = value.creds.len();
    284         value
    285             .creds
    286             .iter()
    287             .fold(Vec::with_capacity(len), |mut creds, cred| {
    288                 creds.push(CredInfo {
    289                     id: cred.credential.id.clone(),
    290                     ext: (&cred.extension).into(),
    291                 });
    292                 creds
    293             })
    294             .into_boxed_slice()
    295     }
    296 }
    297 impl From<AllowedCredentials> for Vec<PublicKeyCredentialDescriptor<Box<[u8]>>> {
    298     #[inline]
    299     fn from(value: AllowedCredentials) -> Self {
    300         let mut creds = Self::with_capacity(value.creds.len());
    301         value.creds.into_iter().fold((), |(), cred| {
    302             creds.push(cred.credential);
    303         });
    304         creds
    305     }
    306 }
    307 impl From<Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>> for AllowedCredentials {
    308     #[inline]
    309     fn from(value: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>) -> Self {
    310         let mut creds = Self::with_capacity(value.len());
    311         value.into_iter().fold((), |(), credential| {
    312             _ = creds.push(AllowedCredential {
    313                 credential,
    314                 extension: CredentialSpecificExtension { prf: None },
    315             });
    316         });
    317         creds
    318     }
    319 }
    320 /// [`CredentialUiMode`](https://www.w3.org/TR/credential-management-1/#enumdef-credentialuimode)
    321 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    322 pub enum CredentialUiMode {
    323     /// [`immediate`](https://www.w3.org/TR/credential-management-1/#dom-credentialuimode-immediate).
    324     Immediate,
    325 }
    326 /// The [`CredentialRequestOptions`](https://www.w3.org/TR/credential-management-1/#dictdef-credentialrequestoptions)
    327 /// to send to the client when authenticating a discoverable credential.
    328 ///
    329 /// Upon saving the [`DiscoverableAuthenticationServerState`] returned from [`Self::start_ceremony`], one MUST send
    330 /// [`DiscoverableAuthenticationClientState`] to the client ASAP. After receiving the newly created
    331 /// [`DiscoverableAuthentication`], it is validated using [`DiscoverableAuthenticationServerState::verify`].
    332 #[derive(Debug)]
    333 pub struct DiscoverableCredentialRequestOptions<'rp_id, 'prf_first, 'prf_second> {
    334     /// [`mediation`](https://www.w3.org/TR/credential-management-1/#dom-credentialrequestoptions-mediation).
    335     ///
    336     /// Note if this is [`CredentialMediationRequirement::Conditional`], user agents are instructed to not
    337     /// enforce any timeout; as result, one may want to set [`PublicKeyCredentialRequestOptions::timeout`] to
    338     /// [`NonZeroU32::MAX`].
    339     pub mediation: CredentialMediationRequirement,
    340     /// [`uiMode`](https://www.w3.org/TR/credential-management-1/#dom-credentialrequestoptions-uimode).
    341     pub ui_mode: Option<CredentialUiMode>,
    342     /// `public-key` [credential type](https://www.w3.org/TR/credential-management-1/#sctn-cred-type-registry).
    343     pub public_key: PublicKeyCredentialRequestOptions<'rp_id, 'prf_first, 'prf_second>,
    344 }
    345 impl<'rp_id, 'prf_first, 'prf_second>
    346     DiscoverableCredentialRequestOptions<'rp_id, 'prf_first, 'prf_second>
    347 {
    348     /// Creates a `DiscoverableCredentialRequestOptions` containing [`CredentialMediationRequirement::default`] and
    349     /// [`PublicKeyCredentialRequestOptions::passkey`].
    350     ///
    351     /// # Examples
    352     ///
    353     /// ```
    354     /// # use webauthn_rp::request::{auth::DiscoverableCredentialRequestOptions, AsciiDomain, RpId, UserVerificationRequirement};
    355     /// assert!(matches!(
    356     ///     DiscoverableCredentialRequestOptions::passkey(&RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?)).public_key.user_verification,
    357     ///     UserVerificationRequirement::Required
    358     /// ));
    359     /// # Ok::<_, webauthn_rp::AggErr>(())
    360     /// ```
    361     #[expect(single_use_lifetimes, reason = "false positive")]
    362     #[inline]
    363     #[must_use]
    364     pub fn passkey<'a: 'rp_id>(rp_id: &'a RpId) -> Self {
    365         Self {
    366             mediation: CredentialMediationRequirement::default(),
    367             ui_mode: None,
    368             public_key: PublicKeyCredentialRequestOptions::passkey(rp_id),
    369         }
    370     }
    371     /// Begins the [authentication ceremony](https://www.w3.org/TR/webauthn-3/#authentication-ceremony) consuming
    372     /// `self`. Note that the expiration [`Instant`]/[`SystemTime`] is saved, so
    373     /// `DiscoverableAuthenticationClientState` MUST be sent ASAP. In order to complete authentication, the returned
    374     /// `DiscoverableAuthenticationServerState` MUST be saved so that it can later be used to verify the credential
    375     /// assertion with [`DiscoverableAuthenticationServerState::verify`].
    376     ///
    377     /// # Errors
    378     ///
    379     /// Errors iff `self` contains incompatible configuration.
    380     #[inline]
    381     pub fn start_ceremony(
    382         self,
    383     ) -> Result<
    384         (
    385             DiscoverableAuthenticationServerState,
    386             DiscoverableAuthenticationClientState<'rp_id, 'prf_first, 'prf_second>,
    387         ),
    388         InvalidTimeout,
    389     > {
    390         #[cfg(not(feature = "serializable_server_state"))]
    391         let res = Instant::now();
    392         #[cfg(feature = "serializable_server_state")]
    393         let res = SystemTime::now();
    394         res.checked_add(Duration::from_millis(
    395             NonZeroU64::from(self.public_key.timeout).get(),
    396         ))
    397         .ok_or(InvalidTimeout)
    398         .map(|expiration| {
    399             (
    400                 DiscoverableAuthenticationServerState(AuthenticationServerState {
    401                     challenge: SentChallenge(self.public_key.challenge.0),
    402                     user_verification: self.public_key.user_verification,
    403                     extensions: self.public_key.extensions.into(),
    404                     expiration,
    405                 }),
    406                 DiscoverableAuthenticationClientState(self),
    407             )
    408         })
    409     }
    410     /// Same as [`Self::start_ceremony`] except the raw challenge is returned instead of
    411     /// [`DiscoverableAuthenticationClientState`].
    412     ///
    413     /// Note this is useful when one configures the authentication ceremony client-side and only needs the
    414     /// server-generated challenge. It's of course essential that `self` is configured exactly the same as
    415     /// how it is configured client-side. See
    416     /// [`challengeURL`](https://github.com/w3c/webauthn/wiki/Explainer:-WebAuthn-challengeURL) for more
    417     /// information.
    418     ///
    419     /// # Errors
    420     ///
    421     /// Read [`Self::start_ceremony`].
    422     #[inline]
    423     pub fn start_ceremony_challenge_only(
    424         self,
    425     ) -> Result<(DiscoverableAuthenticationServerState, [u8; 16]), InvalidTimeout> {
    426         self.start_ceremony()
    427             .map(|(server, client)| (server, client.0.public_key.challenge.into_array()))
    428     }
    429 }
    430 /// The [`CredentialRequestOptions`](https://www.w3.org/TR/credential-management-1/#dictdef-credentialrequestoptions)
    431 /// to send to the client when authenticating non-discoverable credentials.
    432 ///
    433 /// Upon saving the [`NonDiscoverableAuthenticationServerState`] returned from [`Self::start_ceremony`], one MUST send
    434 /// [`NonDiscoverableAuthenticationClientState`] to the client ASAP. After receiving the newly created
    435 /// [`NonDiscoverableAuthentication`], it is validated using [`NonDiscoverableAuthenticationServerState::verify`].
    436 #[derive(Debug)]
    437 pub struct NonDiscoverableCredentialRequestOptions<'rp_id, 'prf_first, 'prf_second> {
    438     /// [`mediation`](https://www.w3.org/TR/credential-management-1/#dom-credentialrequestoptions-mediation).
    439     pub mediation: CredentialMediationRequirement,
    440     /// [`PublicKeyCredentialRequestOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrequestoptions).
    441     pub options: PublicKeyCredentialRequestOptions<'rp_id, 'prf_first, 'prf_second>,
    442     /// [`allowCredentials`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-allowcredentials).
    443     pub allow_credentials: AllowedCredentials,
    444 }
    445 impl<'rp_id, 'prf_first, 'prf_second>
    446     NonDiscoverableCredentialRequestOptions<'rp_id, 'prf_first, 'prf_second>
    447 {
    448     /// Creates a `NonDiscoverableCredentialRequestOptions` containing
    449     /// [`CredentialMediationRequirement::default`],
    450     /// [`PublicKeyCredentialRequestOptions::second_factor`], and the passed [`AllowedCredentials`].
    451     ///
    452     /// # Examples
    453     ///
    454     /// ```
    455     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    456     /// # use webauthn_rp::{bin::Decode, response::bin::DecodeAuthTransportsErr};
    457     /// # use webauthn_rp::{
    458     /// #     request::{
    459     /// #         auth::{AllowedCredentials, NonDiscoverableCredentialRequestOptions},
    460     /// #         AsciiDomain, RpId, PublicKeyCredentialDescriptor, Credentials
    461     /// #     },
    462     /// #     response::{AuthTransports, CredentialId},
    463     /// # };
    464     /// /// Retrieves the `AuthTransports` associated with the unique `cred_id`
    465     /// /// from the database.
    466     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    467     /// fn get_transports(cred_id: CredentialId<&[u8]>) -> Result<AuthTransports, DecodeAuthTransportsErr> {
    468     ///     // ⋮
    469     /// #     AuthTransports::decode(32)
    470     /// }
    471     /// let mut creds = AllowedCredentials::with_capacity(1);
    472     /// assert!(creds.as_ref().is_empty());
    473     /// // `CredentialId::try_from` only exists when `custom` is enabled; and even then, it is
    474     /// // likely never needed since the `CredentialId` was originally sent from the client and is likely
    475     /// // stored in a database which would be fetched by `UserHandle` or `Authentication::raw_id`.
    476     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    477     /// let id = CredentialId::try_from(vec![0; 16].into_boxed_slice())?;
    478     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    479     /// let transports = get_transports((&id).into())?;
    480     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    481     /// assert!(creds.push(PublicKeyCredentialDescriptor { id, transports }.into()));
    482     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    483     /// assert_eq!(
    484     ///     NonDiscoverableCredentialRequestOptions::second_factor(&RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?), creds)
    485     ///         .allow_credentials
    486     ///         .len(),
    487     ///     1
    488     /// );
    489     /// # Ok::<_, webauthn_rp::AggErr>(())
    490     /// ```
    491     #[expect(single_use_lifetimes, reason = "false positive")]
    492     #[inline]
    493     #[must_use]
    494     pub fn second_factor<'a: 'rp_id>(
    495         rp_id: &'a RpId,
    496         allow_credentials: AllowedCredentials,
    497     ) -> Self {
    498         Self {
    499             mediation: CredentialMediationRequirement::default(),
    500             options: PublicKeyCredentialRequestOptions::second_factor(rp_id),
    501             allow_credentials,
    502         }
    503     }
    504     /// Begins the [authentication ceremony](https://www.w3.org/TR/webauthn-3/#authentication-ceremony) consuming
    505     /// `self`. Note that the expiration [`Instant`]/[`SystemTime`] is saved, so `NonDiscoverableAuthenticationClientState`
    506     /// MUST be sent ASAP. In order to complete authentication, the returned `NonDiscoverableAuthenticationServerState`
    507     /// MUST be saved so that it can later be used to verify the credential assertion with
    508     /// [`NonDiscoverableAuthenticationServerState::verify`].
    509     ///
    510     /// # Errors
    511     ///
    512     /// Errors iff `self` contains incompatible configuration.
    513     #[inline]
    514     pub fn start_ceremony(
    515         self,
    516     ) -> Result<
    517         (
    518             NonDiscoverableAuthenticationServerState,
    519             NonDiscoverableAuthenticationClientState<'rp_id, 'prf_first, 'prf_second>,
    520         ),
    521         NonDiscoverableCredentialRequestOptionsErr,
    522     > {
    523         if self.allow_credentials.is_empty() {
    524             Err(NonDiscoverableCredentialRequestOptionsErr::EmptyAllowedCredentials)
    525         } else if matches!(self.mediation, CredentialMediationRequirement::Conditional) {
    526             Err(NonDiscoverableCredentialRequestOptionsErr::ConditionalMediationRequested)
    527         } else {
    528             #[cfg(not(feature = "serializable_server_state"))]
    529             let res = Instant::now();
    530             #[cfg(feature = "serializable_server_state")]
    531             let res = SystemTime::now();
    532             res.checked_add(Duration::from_millis(
    533                 NonZeroU64::from(self.options.timeout).get(),
    534             ))
    535             .ok_or(NonDiscoverableCredentialRequestOptionsErr::InvalidTimeout)
    536             .map(|expiration| {
    537                 (
    538                     NonDiscoverableAuthenticationServerState {
    539                         state: AuthenticationServerState {
    540                             challenge: SentChallenge(self.options.challenge.0),
    541                             user_verification: self.options.user_verification,
    542                             extensions: self.options.extensions.into(),
    543                             expiration,
    544                         },
    545                         allow_credentials: Box::from(&self.allow_credentials),
    546                     },
    547                     NonDiscoverableAuthenticationClientState(self),
    548                 )
    549             })
    550         }
    551     }
    552 }
    553 /// The [`PublicKeyCredentialRequestOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrequestoptions)
    554 /// to send to the client when authenticating a credential.
    555 ///
    556 /// This does _not_ contain [`allowCredentials`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-allowcredentials).
    557 #[derive(Debug)]
    558 pub struct PublicKeyCredentialRequestOptions<'rp_id, 'prf_first, 'prf_second> {
    559     /// [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-challenge).
    560     pub challenge: Challenge,
    561     /// [`timeout`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-timeout).
    562     ///
    563     /// Note we require a positive value despite the spec allowing an optional nonnegative value. This jives
    564     /// with the fact that in-memory storage is required when `serializable_server_state` is not enabled
    565     /// when authenticating credentials as no timeout would make out-of-memory (OOM) conditions more likely.
    566     pub timeout: NonZeroU32,
    567     /// [`rpId`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-rpid).
    568     ///
    569     /// This MUST be the same as the [`PublicKeyCredentialCreationOptions::rp_id`] used when the credential was registered.
    570     pub rp_id: &'rp_id RpId,
    571     /// [`userVerification`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-userverification).
    572     pub user_verification: UserVerificationRequirement,
    573     /// [`hints`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-hints).
    574     pub hints: Hints,
    575     /// [`extensions`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-extensions).
    576     pub extensions: Extension<'prf_first, 'prf_second>,
    577 }
    578 impl<'rp_id> PublicKeyCredentialRequestOptions<'rp_id, '_, '_> {
    579     /// Creates a `PublicKeyCredentialRequestOptions` with [`Self::user_verification`] set to
    580     /// [`UserVerificationRequirement::Required`] and [`Self::timeout`] set to [`FIVE_MINUTES`].
    581     ///
    582     /// Note `rp_id` _must_ be the same as the [`PublicKeyCredentialCreationOptions::rp_id`] when the
    583     /// credential was registered.
    584     ///
    585     /// # Examples
    586     ///
    587     /// ```
    588     /// # use webauthn_rp::request::{auth::PublicKeyCredentialRequestOptions, AsciiDomain, RpId, UserVerificationRequirement};
    589     /// assert!(matches!(
    590     ///     PublicKeyCredentialRequestOptions::passkey(&RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?)).user_verification,
    591     ///     UserVerificationRequirement::Required
    592     /// ));
    593     /// # Ok::<_, webauthn_rp::AggErr>(())
    594     /// ```
    595     #[expect(single_use_lifetimes, reason = "false positive")]
    596     #[inline]
    597     #[must_use]
    598     pub fn passkey<'a: 'rp_id>(rp_id: &'a RpId) -> Self {
    599         Self {
    600             challenge: Challenge::new(),
    601             timeout: FIVE_MINUTES,
    602             rp_id,
    603             user_verification: UserVerificationRequirement::Required,
    604             hints: Hints::EMPTY,
    605             extensions: Extension::default(),
    606         }
    607     }
    608     /// Creates a `PublicKeyCredentialRequestOptions` with [`Self::user_verification`] set to
    609     /// [`UserVerificationRequirement::Discouraged`] and [`Self::timeout`] set to [`FIVE_MINUTES`].
    610     ///
    611     /// Note `rp_id` _must_ be the same as the [`PublicKeyCredentialCreationOptions::rp_id`] when the
    612     /// credentials were registered.
    613     ///
    614     /// # Examples
    615     ///
    616     /// ```
    617     /// # use webauthn_rp::request::{auth::PublicKeyCredentialRequestOptions, AsciiDomain, RpId, UserVerificationRequirement};
    618     /// assert!(matches!(
    619     ///     PublicKeyCredentialRequestOptions::second_factor(&RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?)).user_verification,
    620     ///     UserVerificationRequirement::Discouraged
    621     /// ));
    622     /// # Ok::<_, webauthn_rp::AggErr>(())
    623     /// ```
    624     #[expect(single_use_lifetimes, reason = "false positive")]
    625     #[inline]
    626     #[must_use]
    627     pub fn second_factor<'a: 'rp_id>(rp_id: &'a RpId) -> Self {
    628         let mut opts = Self::passkey(rp_id);
    629         opts.user_verification = UserVerificationRequirement::Discouraged;
    630         opts
    631     }
    632 }
    633 /// Container of a [`DiscoverableCredentialRequestOptions`] that has been used to start the authentication ceremony.
    634 /// This gets sent to the client ASAP.
    635 #[derive(Debug)]
    636 pub struct DiscoverableAuthenticationClientState<'rp_id, 'prf_first, 'prf_second>(
    637     DiscoverableCredentialRequestOptions<'rp_id, 'prf_first, 'prf_second>,
    638 );
    639 impl<'rp_id, 'prf_first, 'prf_second>
    640     DiscoverableAuthenticationClientState<'rp_id, 'prf_first, 'prf_second>
    641 {
    642     /// Returns the `DiscoverableCredentialRequestOptions` that was used to start an authentication ceremony.
    643     #[inline]
    644     #[must_use]
    645     pub const fn options(
    646         &self,
    647     ) -> &DiscoverableCredentialRequestOptions<'rp_id, 'prf_first, 'prf_second> {
    648         &self.0
    649     }
    650 }
    651 /// Container of a [`NonDiscoverableCredentialRequestOptions`] that has been used to start the authentication
    652 /// ceremony. This gets sent to the client ASAP.
    653 #[derive(Debug)]
    654 pub struct NonDiscoverableAuthenticationClientState<'rp_id, 'prf_first, 'prf_second>(
    655     NonDiscoverableCredentialRequestOptions<'rp_id, 'prf_first, 'prf_second>,
    656 );
    657 impl<'rp_id, 'prf_first, 'prf_second>
    658     NonDiscoverableAuthenticationClientState<'rp_id, 'prf_first, 'prf_second>
    659 {
    660     /// Returns the `NonDiscoverableCredentialRequestOptions` that was used to start an authentication ceremony.
    661     #[inline]
    662     #[must_use]
    663     pub const fn options(
    664         &self,
    665     ) -> &NonDiscoverableCredentialRequestOptions<'rp_id, 'prf_first, 'prf_second> {
    666         &self.0
    667     }
    668 }
    669 /// The possible combinations of an [`AuthenticatedCredential`]'s [`StaticState`]'s
    670 /// `extensions.hmac_secret` and `client_extension_results.prf`.
    671 ///
    672 /// Note we ensure in `crate::verify_static_and_dynamic_state` that `hmac_secret` does not exist when
    673 /// `prf` does not exist, `hmac_secret` does not exist or is `false` when `prf` is `false`, or
    674 /// `hmac_secret` does not exist or is `true` when `prf` is `true`.
    675 #[derive(Clone, Copy)]
    676 enum CredPrf {
    677     /// No `prf` or `hmac_secret`.
    678     None,
    679     /// `prf.enabled` is `false` but there is no `hmac_secret`.
    680     FalseNoHmac,
    681     /// `prf.enabled` and `hmac_secret` are `false`.
    682     FalseFalseHmac,
    683     /// `prf.enabled` is `true` but there is no `hmac_secret`.
    684     TrueNoHmac,
    685     /// `prf.enabled` and `hmac_secret` are `true`.
    686     TrueTrueHmac,
    687 }
    688 /// `PrfInput` and `PrfInputOwned` without the actual data sent to reduce memory usage when storing
    689 /// [`DiscoverableAuthenticationServerState`] in an in-memory collection.
    690 #[derive(Clone, Copy, Debug)]
    691 enum ServerPrfInfo {
    692     /// No `PrfInput`.
    693     None,
    694     /// `PrfInput::second` was `None`.
    695     One(ExtensionReq),
    696     /// `PrfInput::second` was `Some`.
    697     Two(ExtensionReq),
    698 }
    699 impl ServerPrfInfo {
    700     /// Validates `val` based on the passed arguments.
    701     ///
    702     /// It's not possible to request the PRF extension without sending `UserVerificationRequirement::Required`;
    703     /// thus `user_verified` will always be `true` when sending PRF; otherwise ceremony validation will error.
    704     /// However when we _don't_ send the PRF extension _and_ we don't error on an unsolicited response, it's
    705     /// possible to receive an `HmacSecret` without the user having been verified; thus we only ensure
    706     /// `user_verified` is true when we don't error on unsolicted responses _and_ we didn't send the PRF extension.
    707     const fn validate(
    708         self,
    709         user_verified: bool,
    710         cred_prf: CredPrf,
    711         hmac: HmacSecret,
    712         err_unsolicited: bool,
    713     ) -> Result<(), ExtensionErr> {
    714         match hmac {
    715             HmacSecret::None => match self {
    716                 Self::None => Ok(()),
    717                 Self::One(req) | Self::Two(req) => {
    718                     if matches!(req, ExtensionReq::Allow)
    719                         || !matches!(cred_prf, CredPrf::TrueTrueHmac)
    720                     {
    721                         Ok(())
    722                     } else {
    723                         Err(ExtensionErr::MissingHmacSecret)
    724                     }
    725                 }
    726             },
    727             HmacSecret::One => match self {
    728                 Self::None => {
    729                     if err_unsolicited {
    730                         Err(ExtensionErr::ForbiddenHmacSecret)
    731                     } else if matches!(cred_prf, CredPrf::None | CredPrf::TrueTrueHmac) {
    732                         if user_verified {
    733                             Ok(())
    734                         } else {
    735                             Err(ExtensionErr::UserNotVerifiedHmacSecret)
    736                         }
    737                     } else {
    738                         Err(ExtensionErr::HmacSecretForNonHmacSecretCredential)
    739                     }
    740                 }
    741                 Self::One(_) => {
    742                     if matches!(cred_prf, CredPrf::None | CredPrf::TrueTrueHmac) {
    743                         Ok(())
    744                     } else {
    745                         Err(ExtensionErr::HmacSecretForNonHmacSecretCredential)
    746                     }
    747                 }
    748                 Self::Two(_) => Err(ExtensionErr::InvalidHmacSecretValue(
    749                     OneOrTwo::Two,
    750                     OneOrTwo::One,
    751                 )),
    752             },
    753             HmacSecret::Two => match self {
    754                 Self::None => {
    755                     if err_unsolicited {
    756                         Err(ExtensionErr::ForbiddenHmacSecret)
    757                     } else if matches!(cred_prf, CredPrf::None | CredPrf::TrueTrueHmac) {
    758                         if user_verified {
    759                             Ok(())
    760                         } else {
    761                             Err(ExtensionErr::UserNotVerifiedHmacSecret)
    762                         }
    763                     } else {
    764                         Err(ExtensionErr::HmacSecretForNonHmacSecretCredential)
    765                     }
    766                 }
    767                 Self::One(_) => Err(ExtensionErr::InvalidHmacSecretValue(
    768                     OneOrTwo::One,
    769                     OneOrTwo::Two,
    770                 )),
    771                 Self::Two(_) => {
    772                     if matches!(cred_prf, CredPrf::None | CredPrf::TrueTrueHmac) {
    773                         Ok(())
    774                     } else {
    775                         Err(ExtensionErr::HmacSecretForNonHmacSecretCredential)
    776                     }
    777                 }
    778             },
    779         }
    780     }
    781 }
    782 #[cfg(test)]
    783 impl PartialEq for ServerPrfInfo {
    784     fn eq(&self, other: &Self) -> bool {
    785         match *self {
    786             Self::None => matches!(*other, Self::None),
    787             Self::One(req) => matches!(*other, Self::One(req2) if req == req2),
    788             Self::Two(req) => matches!(*other, Self::Two(req2) if req == req2),
    789         }
    790     }
    791 }
    792 impl From<Option<(PrfInput<'_, '_>, ExtensionReq)>> for ServerPrfInfo {
    793     fn from(value: Option<(PrfInput<'_, '_>, ExtensionReq)>) -> Self {
    794         value.map_or(Self::None, |val| {
    795             val.0
    796                 .second
    797                 .map_or_else(|| Self::One(val.1), |_| Self::Two(val.1))
    798         })
    799     }
    800 }
    801 impl From<&PrfInputOwned> for ServerPrfInfo {
    802     fn from(value: &PrfInputOwned) -> Self {
    803         value
    804             .second
    805             .as_ref()
    806             .map_or_else(|| Self::One(value.ext_req), |_| Self::Two(value.ext_req))
    807     }
    808 }
    809 /// `Extension` without the actual data sent to reduce memory usage when storing [`AuthenticationServerState`]
    810 /// in an in-memory collection.
    811 #[derive(Clone, Copy, Debug)]
    812 struct ServerExtensionInfo {
    813     /// `Extension::prf`.
    814     prf: ServerPrfInfo,
    815 }
    816 impl From<Extension<'_, '_>> for ServerExtensionInfo {
    817     fn from(value: Extension<'_, '_>) -> Self {
    818         Self {
    819             prf: value.prf.into(),
    820         }
    821     }
    822 }
    823 #[cfg(test)]
    824 impl PartialEq for ServerExtensionInfo {
    825     fn eq(&self, other: &Self) -> bool {
    826         self.prf == other.prf
    827     }
    828 }
    829 /// `CredentialSpecificExtension` without the actual data sent to reduce memory usage when storing [`AuthenticationServerState`]
    830 /// in an in-memory collection.
    831 #[derive(Clone, Copy, Debug)]
    832 struct ServerCredSpecificExtensionInfo {
    833     /// `CredentialSpecificExtension::prf`.
    834     prf: ServerPrfInfo,
    835 }
    836 #[cfg(test)]
    837 impl PartialEq for ServerCredSpecificExtensionInfo {
    838     fn eq(&self, other: &Self) -> bool {
    839         self.prf == other.prf
    840     }
    841 }
    842 impl From<&CredentialSpecificExtension> for ServerCredSpecificExtensionInfo {
    843     fn from(value: &CredentialSpecificExtension) -> Self {
    844         Self {
    845             prf: value
    846                 .prf
    847                 .as_ref()
    848                 .map_or(ServerPrfInfo::None, ServerPrfInfo::from),
    849         }
    850     }
    851 }
    852 impl ServerExtensionInfo {
    853     /// Validates the extensions.
    854     ///
    855     /// Note that this MUST only be called internally by `auth::validate_extensions`.
    856     const fn validate_extensions(
    857         self,
    858         user_verified: bool,
    859         auth_ext: AuthenticatorExtensionOutput,
    860         error_unsolicited: bool,
    861         cred_prf: CredPrf,
    862     ) -> Result<(), ExtensionErr> {
    863         ServerPrfInfo::validate(
    864             self.prf,
    865             user_verified,
    866             cred_prf,
    867             auth_ext.hmac_secret,
    868             error_unsolicited,
    869         )
    870     }
    871 }
    872 /// Validates the extensions.
    873 fn validate_extensions(
    874     ext: ServerExtensionInfo,
    875     user_verified: bool,
    876     cred_ext: Option<ServerCredSpecificExtensionInfo>,
    877     auth_ext: AuthenticatorExtensionOutput,
    878     error_unsolicited: bool,
    879     cred_prf: CredPrf,
    880 ) -> Result<(), ExtensionErr> {
    881     cred_ext.map_or_else(
    882         || {
    883             // No credental-specific extensions, so we can simply focus on `ext`.
    884             ext.validate_extensions(user_verified, auth_ext, error_unsolicited, cred_prf)
    885         },
    886         |c_ext| {
    887             // Must carefully process each extension based on overlap and which gets priority over the other.
    888             if matches!(c_ext.prf, ServerPrfInfo::None) {
    889                 ext.prf.validate(
    890                     user_verified,
    891                     cred_prf,
    892                     auth_ext.hmac_secret,
    893                     error_unsolicited,
    894                 )
    895             } else {
    896                 c_ext.prf.validate(
    897                     user_verified,
    898                     cred_prf,
    899                     auth_ext.hmac_secret,
    900                     error_unsolicited,
    901                 )
    902             }
    903         },
    904     )
    905 }
    906 /// [`AllowedCredential`] with less data to reduce memory usage when storing [`AuthenticationServerState`]
    907 /// in an in-memory collection.
    908 #[derive(Debug)]
    909 struct CredInfo {
    910     /// The Credential ID.
    911     id: CredentialId<Box<[u8]>>,
    912     /// Any credential-specific extensions.
    913     ext: ServerCredSpecificExtensionInfo,
    914 }
    915 #[cfg(test)]
    916 impl PartialEq for CredInfo {
    917     fn eq(&self, other: &Self) -> bool {
    918         self.id == other.id && self.ext == other.ext
    919     }
    920 }
    921 /// Controls how to handle a change in [`DynamicState::authenticator_attachment`].
    922 ///
    923 /// Note when `DynamicState::authenticator_attachment` is [`AuthenticatorAttachment::None`], then it will
    924 /// be updated regardless. Similarly when [`Authentication::authenticator_attachment`] is
    925 /// `AuthenticatorAttachment::None`, it will never update `DynamicState::authenticator_attachment`.
    926 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    927 pub enum AuthenticatorAttachmentEnforcement {
    928     /// Fail the authentication ceremony if [`AuthenticatorAttachment`] is not the same.
    929     ///
    930     /// The contained `bool` represents if `AuthenticatorAttachment` must be sent.
    931     Fail(bool),
    932     /// Update [`DynamicState::authenticator_attachment`] to the sent [`AuthenticatorAttachment`].
    933     ///
    934     /// The contained `bool` represents if `AuthenticatorAttachment` must be sent.
    935     Update(bool),
    936     /// Do not update [`DynamicState::authenticator_attachment`] to the sent [`AuthenticatorAttachment`].
    937     ///
    938     /// The contained `bool` represents if `AuthenticatorAttachment` must be sent.
    939     Ignore(bool),
    940 }
    941 impl AuthenticatorAttachmentEnforcement {
    942     /// Validates `cur` based on `self` and `prev`.
    943     const fn validate(
    944         self,
    945         prev: AuthenticatorAttachment,
    946         cur: AuthenticatorAttachment,
    947     ) -> Result<AuthenticatorAttachment, AuthCeremonyErr> {
    948         match cur {
    949             AuthenticatorAttachment::None => match self {
    950                 Self::Fail(require) | Self::Update(require) | Self::Ignore(require) => {
    951                     if require {
    952                         Err(AuthCeremonyErr::MissingAuthenticatorAttachment)
    953                     } else {
    954                         // We don't overwrite the previous one with [`AuthenticatorAttachment::None`].
    955                         Ok(prev)
    956                     }
    957                 }
    958             },
    959             AuthenticatorAttachment::Platform => match self {
    960                 Self::Fail(_) => {
    961                     if matches!(prev, AuthenticatorAttachment::CrossPlatform) {
    962                         Err(AuthCeremonyErr::AuthenticatorAttachmentMismatch)
    963                     } else {
    964                         // We don't fail when we previously had [`AuthenticatorAttachment::None`].
    965                         Ok(cur)
    966                     }
    967                 }
    968                 Self::Update(_) => Ok(cur),
    969                 Self::Ignore(_) => {
    970                     if matches!(prev, AuthenticatorAttachment::None) {
    971                         // We overwrite the previous one when it is [`AuthenticatorAttachment::None`].
    972                         Ok(cur)
    973                     } else {
    974                         Ok(prev)
    975                     }
    976                 }
    977             },
    978             AuthenticatorAttachment::CrossPlatform => match self {
    979                 Self::Fail(_) => {
    980                     if matches!(prev, AuthenticatorAttachment::Platform) {
    981                         Err(AuthCeremonyErr::AuthenticatorAttachmentMismatch)
    982                     } else {
    983                         // We don't fail when we previously had [`AuthenticatorAttachment::None`].
    984                         Ok(cur)
    985                     }
    986                 }
    987                 Self::Update(_) => Ok(cur),
    988                 Self::Ignore(_) => {
    989                     if matches!(prev, AuthenticatorAttachment::None) {
    990                         // We overwrite the previous one when it is [`AuthenticatorAttachment::None`].
    991                         Ok(cur)
    992                     } else {
    993                         Ok(prev)
    994                     }
    995                 }
    996             },
    997         }
    998     }
    999 }
   1000 impl Default for AuthenticatorAttachmentEnforcement {
   1001     /// Returns [`Self::Ignore`] containing `false`.
   1002     #[inline]
   1003     fn default() -> Self {
   1004         Self::Ignore(false)
   1005     }
   1006 }
   1007 /// Backup state requirements for the credential.
   1008 ///
   1009 /// Note the backup eligibility of a credential is not allowed to change. This is only used to allow one to
   1010 /// require the existence or lack of existence of a backup.
   1011 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
   1012 pub enum BackupStateReq {
   1013     /// No requirements on the backup state.
   1014     ///
   1015     /// The backup eligibility of the credential must be the same as required by the spec, but the existence
   1016     /// of a backup doesn't matter.
   1017     #[default]
   1018     None,
   1019     /// Credential must be backed up.
   1020     Exists,
   1021     /// Credential must not be backed up.
   1022     DoesntExist,
   1023 }
   1024 /// Additional verification options to perform in [`DiscoverableAuthenticationServerState::verify`] and
   1025 /// [`NonDiscoverableAuthenticationServerState::verify`].
   1026 #[derive(Clone, Copy, Debug)]
   1027 pub struct AuthenticationVerificationOptions<'origins, 'top_origins, O, T> {
   1028     /// Origins to use for [origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin).
   1029     ///
   1030     /// When this is empty, the origin that will be used will be based on the [`RpId`] passed to
   1031     /// [`DiscoverableAuthenticationServerState::verify`] or [`NonDiscoverableAuthenticationServerState::verify`].
   1032     /// If [`RpId::Domain`] or [`RpId::StaticDomain`], then the [`DomainOrigin`] returned from passing
   1033     /// [`AsciiDomain::as_ref`] and [`AsciiDomainStatic::as_str`] to [`DomainOrigin::new`] respectively will be
   1034     /// used; otherwise the [`Url`] in [`RpId::Url`] will be used.
   1035     pub allowed_origins: &'origins [O],
   1036     /// [Top-level origins](https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-top-level-origin)
   1037     /// to use for [origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin).
   1038     ///
   1039     /// When this is `Some`, [`CollectedClientData::cross_origin`] is allowed to be `true`. When the contained
   1040     /// `slice` is empty, [`CollectedClientData::top_origin`] must be `None`. When this is `None`,
   1041     /// `CollectedClientData::cross_origin` must be `false` and `CollectedClientData::top_origin` must be `None`.
   1042     pub allowed_top_origins: Option<&'top_origins [T]>,
   1043     /// The required [`Backup`] state of the credential.
   1044     pub backup_state_requirement: BackupStateReq,
   1045     /// Error when unsolicited extensions are sent back iff `true`.
   1046     pub error_on_unsolicited_extensions: bool,
   1047     /// Dictates what happens when [`Authentication::authenticator_attachment`] is not the same as
   1048     /// [`DynamicState::authenticator_attachment`].
   1049     pub auth_attachment_enforcement: AuthenticatorAttachmentEnforcement,
   1050     /// [`DynamicState::user_verified`] will be set to `true` iff [`Flag::user_verified`] when `true`.
   1051     pub update_uv: bool,
   1052     /// Dictates what happens when [`AuthenticatorData::sign_count`] is not updated to a strictly greater value.
   1053     pub sig_counter_enforcement: SignatureCounterEnforcement,
   1054     /// [`CollectedClientData::from_client_data_json_relaxed`] is used to extract [`CollectedClientData`] iff `true`.
   1055     #[cfg(feature = "serde_relaxed")]
   1056     pub client_data_json_relaxed: bool,
   1057 }
   1058 impl<O, T> AuthenticationVerificationOptions<'_, '_, O, T> {
   1059     /// Returns `Self` such that [`Self::allowed_origins`] is empty, [`Self::allowed_top_origins`] is `None`,
   1060     /// [`Self::backup_state_requirement`] is [`BackupStateReq::None`], [`Self::error_on_unsolicited_extensions`] is
   1061     /// `true`, [`Self::auth_attachment_enforcement`] is [`AuthenticatorAttachmentEnforcement::default`],
   1062     /// [`Self::update_uv`] is `false`, [`Self::sig_counter_enforcement`] is
   1063     /// [`SignatureCounterEnforcement::default`], and [`Self::client_data_json_relaxed`] is `true`.
   1064     ///
   1065     /// Note `O` and `T` should implement `PartialEq<Origin<'_>>` (e.g., `&str`).
   1066     #[inline]
   1067     #[must_use]
   1068     pub const fn new() -> Self {
   1069         Self {
   1070             allowed_origins: [].as_slice(),
   1071             allowed_top_origins: None,
   1072             backup_state_requirement: BackupStateReq::None,
   1073             error_on_unsolicited_extensions: true,
   1074             auth_attachment_enforcement: AuthenticatorAttachmentEnforcement::Ignore(false),
   1075             update_uv: false,
   1076             sig_counter_enforcement: SignatureCounterEnforcement::Fail,
   1077             #[cfg(feature = "serde_relaxed")]
   1078             client_data_json_relaxed: true,
   1079         }
   1080     }
   1081 }
   1082 impl<O, T> Default for AuthenticationVerificationOptions<'_, '_, O, T> {
   1083     /// Same as [`Self::new`].
   1084     #[inline]
   1085     fn default() -> Self {
   1086         Self::new()
   1087     }
   1088 }
   1089 // This is essentially the `DiscoverableCredentialRequestOptions` used to create it; however to reduce
   1090 // memory usage, we remove all unnecessary data making an instance of this 48 bytes in size
   1091 // `x86_64-unknown-linux-gnu` platforms.
   1092 //
   1093 /// State needed to be saved when beginning the authentication ceremony.
   1094 ///
   1095 /// Saves the necessary information associated with the [`DiscoverableCredentialRequestOptions`] used to create it
   1096 /// via [`DiscoverableCredentialRequestOptions::start_ceremony`] so that authentication of a credential can be
   1097 /// performed with [`Self::verify`].
   1098 ///
   1099 /// `DiscoverableAuthenticationServerState` implements [`Borrow`] of [`SentChallenge`]; thus to obtain the correct
   1100 /// `DiscoverableAuthenticationServerState` associated with a [`DiscoverableAuthentication`], one should use its
   1101 /// corresponding [`DiscoverableAuthentication::challenge`].
   1102 #[derive(Debug)]
   1103 pub struct DiscoverableAuthenticationServerState(AuthenticationServerState);
   1104 impl DiscoverableAuthenticationServerState {
   1105     /// Verifies `response` is valid based on `self` consuming `self` and updating `cred`. Returns `true`
   1106     /// iff `cred` was mutated.
   1107     ///
   1108     /// `rp_id` MUST be the same as the [`PublicKeyCredentialRequestOptions::rp_id`] used when starting the
   1109     /// ceremony.
   1110     ///
   1111     /// It is _essential_ to save [`AuthenticatedCredential::dynamic_state`] overwriting the original value iff `Ok(true)`
   1112     /// is returned.
   1113     ///
   1114     /// # Errors
   1115     ///
   1116     /// Errors iff `response` is not valid according to the
   1117     /// [authentication ceremony](https://www.w3.org/TR/webauthn-3/#sctn-verifying-assertion) or violates any
   1118     /// of the settings in `options`.
   1119     #[inline]
   1120     pub fn verify<
   1121         'a,
   1122         const USER_LEN: usize,
   1123         O: PartialEq<Origin<'a>>,
   1124         T: PartialEq<Origin<'a>>,
   1125         MlDsa87Key: AsRef<[u8]>,
   1126         MlDsa65Key: AsRef<[u8]>,
   1127         MlDsa44Key: AsRef<[u8]>,
   1128         EdKey: AsRef<[u8]>,
   1129         P256Key: AsRef<[u8]>,
   1130         P384Key: AsRef<[u8]>,
   1131         RsaKey: AsRef<[u8]>,
   1132     >(
   1133         self,
   1134         rp_id: &RpId,
   1135         response: &'a DiscoverableAuthentication<USER_LEN>,
   1136         cred: &mut AuthenticatedCredential<
   1137             '_,
   1138             '_,
   1139             USER_LEN,
   1140             CompressedPubKey<MlDsa87Key, MlDsa65Key, MlDsa44Key, EdKey, P256Key, P384Key, RsaKey>,
   1141         >,
   1142         options: &AuthenticationVerificationOptions<'_, '_, O, T>,
   1143     ) -> Result<bool, AuthCeremonyErr> {
   1144         // Step 6 item 2.
   1145         if cred.user_id == response.response.user_handle() {
   1146             // Step 6 item 2.
   1147             if cred.id.as_ref() == response.raw_id.as_ref() {
   1148                 self.0.verify(rp_id, response, cred, options, None)
   1149             } else {
   1150                 Err(AuthCeremonyErr::CredentialIdMismatch)
   1151             }
   1152         } else {
   1153             Err(AuthCeremonyErr::UserHandleMismatch)
   1154         }
   1155     }
   1156     #[cfg(all(test, feature = "custom", feature = "serializable_server_state"))]
   1157     fn is_eq(&self, other: &Self) -> bool {
   1158         self.0.is_eq(&other.0)
   1159     }
   1160 }
   1161 // This is essentially the `NonDiscoverableCredentialRequestOptions` used to create it; however to reduce
   1162 // memory usage, we remove all unnecessary data making an instance of this as small as 64 bytes in size on
   1163 // `x86_64-unknown-linux-gnu` platforms. This does not include the size of each `CredInfo` which should exist
   1164 // elsewhere on the heap but obviously contributes memory overall.
   1165 /// State needed to be saved when beginning the authentication ceremony.
   1166 ///
   1167 /// Saves the necessary information associated with the [`NonDiscoverableCredentialRequestOptions`] used to create
   1168 /// it via [`NonDiscoverableCredentialRequestOptions::start_ceremony`] so that authentication of a credential can be
   1169 /// performed with [`Self::verify`].
   1170 ///
   1171 /// `NonDiscoverableAuthenticationServerState` implements [`Borrow`] of [`SentChallenge`]; thus to obtain the
   1172 /// correct `NonDiscoverableAuthenticationServerState` associated with a [`NonDiscoverableAuthentication`], one
   1173 /// should use its corresponding [`NonDiscoverableAuthentication::challenge`].
   1174 #[derive(Debug)]
   1175 pub struct NonDiscoverableAuthenticationServerState {
   1176     /// Most server state.
   1177     state: AuthenticationServerState,
   1178     /// The set of credentials that are allowed.
   1179     allow_credentials: Box<[CredInfo]>,
   1180 }
   1181 impl NonDiscoverableAuthenticationServerState {
   1182     /// Verifies `response` is valid based on `self` consuming `self` and updating `cred`. Returns `true`
   1183     /// iff `cred` was mutated.
   1184     ///
   1185     /// `rp_id` MUST be the same as the [`PublicKeyCredentialRequestOptions::rp_id`] used when starting the
   1186     /// ceremony.
   1187     ///
   1188     /// It is _essential_ to save [`AuthenticatedCredential::dynamic_state`] overwriting the original value iff `Ok(true)`
   1189     /// is returned.
   1190     ///
   1191     /// # Errors
   1192     ///
   1193     /// Errors iff `response` is not valid according to the
   1194     /// [authentication ceremony](https://www.w3.org/TR/webauthn-3/#sctn-verifying-assertion) or violates any
   1195     /// of the settings in `options`.
   1196     #[inline]
   1197     pub fn verify<
   1198         'a,
   1199         const USER_LEN: usize,
   1200         O: PartialEq<Origin<'a>>,
   1201         T: PartialEq<Origin<'a>>,
   1202         MlDsa87Key: AsRef<[u8]>,
   1203         MlDsa65Key: AsRef<[u8]>,
   1204         MlDsa44Key: AsRef<[u8]>,
   1205         EdKey: AsRef<[u8]>,
   1206         P256Key: AsRef<[u8]>,
   1207         P384Key: AsRef<[u8]>,
   1208         RsaKey: AsRef<[u8]>,
   1209     >(
   1210         self,
   1211         rp_id: &RpId,
   1212         response: &'a NonDiscoverableAuthentication<USER_LEN>,
   1213         cred: &mut AuthenticatedCredential<
   1214             '_,
   1215             '_,
   1216             USER_LEN,
   1217             CompressedPubKey<MlDsa87Key, MlDsa65Key, MlDsa44Key, EdKey, P256Key, P384Key, RsaKey>,
   1218         >,
   1219         options: &AuthenticationVerificationOptions<'_, '_, O, T>,
   1220     ) -> Result<bool, AuthCeremonyErr> {
   1221         response
   1222             .response
   1223             .user_handle()
   1224             .as_ref()
   1225             .map_or(Ok(()), |user| {
   1226                 // Step 6 item 1.
   1227                 if *user == cred.user_id() {
   1228                     Ok(())
   1229                 } else {
   1230                     Err(AuthCeremonyErr::UserHandleMismatch)
   1231                 }
   1232             })
   1233             .and_then(|()| {
   1234                 self.allow_credentials
   1235                     .iter()
   1236                     // Step 5.
   1237                     .find(|c| c.id == response.raw_id)
   1238                     .ok_or(AuthCeremonyErr::NoMatchingAllowedCredential)
   1239                     .and_then(|c| {
   1240                         // Step 6 item 1.
   1241                         if c.id.as_ref() == cred.id.as_ref() {
   1242                             self.state
   1243                                 .verify(rp_id, response, cred, options, Some(c.ext))
   1244                         } else {
   1245                             Err(AuthCeremonyErr::CredentialIdMismatch)
   1246                         }
   1247                     })
   1248             })
   1249     }
   1250     #[cfg(all(test, feature = "custom", feature = "serializable_server_state"))]
   1251     fn is_eq(&self, other: &Self) -> bool {
   1252         self.state.is_eq(&other.state) && self.allow_credentials == other.allow_credentials
   1253     }
   1254 }
   1255 // This is essentially the `PublicKeyCredentialRequestOptions` used to create it; however to reduce
   1256 // memory usage, we remove all unnecessary data making an instance of this 48 bytes in size on
   1257 // `x86_64-unknown-linux-gnu` platforms.
   1258 /// Shared state used by [`DiscoverableAuthenticationServerState`] and [`NonDiscoverableAuthenticationServerState`].
   1259 #[derive(Debug)]
   1260 struct AuthenticationServerState {
   1261     // This is a `SentChallenge` since we need `AuthenticationServerState` to be fetchable after receiving the
   1262     // response from the client. This response must obviously be constructable; thus its challenge is a
   1263     // `SentChallenge`.
   1264     //
   1265     // This must never be mutated since we want to ensure it is actually a `Challenge` (which
   1266     // can only be constructed via `Challenge::new`). This is guaranteed to be true iff
   1267     // `serializable_server_state` is not enabled.
   1268     /// [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-challenge).
   1269     challenge: SentChallenge,
   1270     /// [`userVerification`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-userverification).
   1271     user_verification: UserVerificationRequirement,
   1272     /// [`extensions`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-extensions).
   1273     extensions: ServerExtensionInfo,
   1274     /// `Instant` the ceremony expires.
   1275     #[cfg(not(feature = "serializable_server_state"))]
   1276     expiration: Instant,
   1277     /// `SystemTime` the ceremony expires.
   1278     #[cfg(feature = "serializable_server_state")]
   1279     expiration: SystemTime,
   1280 }
   1281 impl AuthenticationServerState {
   1282     /// Verifies `response` is valid based on `self` consuming `self` and updating `cred`. Returns `true`
   1283     /// iff `cred` was mutated.
   1284     ///
   1285     /// `rp_id` MUST be the same as the [`PublicKeyCredentialRequestOptions::rp_id`] used when starting the
   1286     /// ceremony.
   1287     ///
   1288     /// It is _essential_ to save [`AuthenticatedCredential::dynamic_state`] overwriting the original value iff `Ok(true)`
   1289     /// is returned.
   1290     ///
   1291     /// # Errors
   1292     ///
   1293     /// Errors iff `response` is not valid according to the
   1294     /// [authentication ceremony](https://www.w3.org/TR/webauthn-3/#sctn-verifying-assertion) or violates any
   1295     /// of the settings in `options`.
   1296     fn verify<
   1297         'a,
   1298         const USER_LEN: usize,
   1299         const DISCOVERABLE: bool,
   1300         O: PartialEq<Origin<'a>>,
   1301         T: PartialEq<Origin<'a>>,
   1302         MlDsa87Key: AsRef<[u8]>,
   1303         MlDsa65Key: AsRef<[u8]>,
   1304         MlDsa44Key: AsRef<[u8]>,
   1305         EdKey: AsRef<[u8]>,
   1306         P256Key: AsRef<[u8]>,
   1307         P384Key: AsRef<[u8]>,
   1308         RsaKey: AsRef<[u8]>,
   1309     >(
   1310         self,
   1311         rp_id: &RpId,
   1312         response: &'a Authentication<USER_LEN, DISCOVERABLE>,
   1313         cred: &mut AuthenticatedCredential<
   1314             '_,
   1315             '_,
   1316             USER_LEN,
   1317             CompressedPubKey<MlDsa87Key, MlDsa65Key, MlDsa44Key, EdKey, P256Key, P384Key, RsaKey>,
   1318         >,
   1319         options: &AuthenticationVerificationOptions<'_, '_, O, T>,
   1320         cred_ext: Option<ServerCredSpecificExtensionInfo>,
   1321     ) -> Result<bool, AuthCeremonyErr> {
   1322         // [Authentication ceremony](https://www.w3.org/TR/webauthn-3/#sctn-verifying-assertion)
   1323         // is handled by:
   1324         //
   1325         // 1. Calling code.
   1326         // 2. Client code and the construction of `resp` (hopefully via [`Authentication::deserialize`]).
   1327         // 3. Client code and the construction of `resp` (hopefully via [`AuthenticatorAssertion::deserialize`]).
   1328         // 4. Client code and the construction of `resp` (hopefully via [`ClientExtensionsOutputs::deserialize`]).
   1329         // 5. [`NonDiscoverableAuthenticationServerState::verify`].
   1330         // 6. [`DiscoverableAuthenticationServerState::verify`] and [`NonDiscoverableAuthenticationServerState::verify`].
   1331         // 7. Informative only in that it defines variables.
   1332         // 8. [`Self::partial_validate`].
   1333         // 9. [`Self::partial_validate`].
   1334         // 10. [`Self::partial_validate`].
   1335         // 11. [`Self::partial_validate`].
   1336         // 12. [`Self::partial_validate`].
   1337         // 13. [`Self::partial_validate`].
   1338         // 14. [`Self::partial_validate`].
   1339         // 15. [`Self::partial_validate`].
   1340         // 16. [`Self::partial_validate`].
   1341         // 17. [`Self::partial_validate`].
   1342         // 18. [`Self::partial_validate`].
   1343         // 19. [`Self::partial_validate`].
   1344         // 20. [`Self::partial_validate`].
   1345         // 21. [`Self::partial_validate`].
   1346         // 22. Below.
   1347         // 23. Below.
   1348         // 24. Below.
   1349         // 25. Below.
   1350 
   1351         // Steps 8–21.
   1352         self.partial_validate(
   1353             rp_id,
   1354             response,
   1355             (&cred.static_state.credential_public_key).into(),
   1356             &CeremonyOptions {
   1357                 allowed_origins: options.allowed_origins,
   1358                 allowed_top_origins: options.allowed_top_origins,
   1359                 backup_requirement: if cred.dynamic_state.backup == Backup::NotEligible {
   1360                     BackupReq::NotEligible
   1361                 } else {
   1362                     match options.backup_state_requirement {
   1363                         BackupStateReq::None => BackupReq::Eligible,
   1364                         BackupStateReq::Exists => BackupReq::Exists,
   1365                         BackupStateReq::DoesntExist => BackupReq::EligibleNotExists,
   1366                     }
   1367                 },
   1368                 #[cfg(feature = "serde_relaxed")]
   1369                 client_data_json_relaxed: options.client_data_json_relaxed,
   1370             },
   1371         )
   1372         .map_err(AuthCeremonyErr::from)
   1373         .and_then(|auth_data| {
   1374             options
   1375                 .auth_attachment_enforcement
   1376                 .validate(
   1377                     cred.dynamic_state.authenticator_attachment,
   1378                     response.authenticator_attachment,
   1379                 )
   1380                 .and_then(|auth_attachment| {
   1381                     let flags = auth_data.flags();
   1382                     // Step 23.
   1383                     validate_extensions(
   1384                         self.extensions,
   1385                         flags.user_verified,
   1386                         cred_ext,
   1387                         auth_data.extensions(),
   1388                         options.error_on_unsolicited_extensions,
   1389                         cred.static_state.client_extension_results.prf.map_or(
   1390                             CredPrf::None,
   1391                             |prf| {
   1392                                 if prf.enabled {
   1393                                     cred.static_state
   1394                                         .extensions
   1395                                         .hmac_secret
   1396                                         .map_or(CredPrf::TrueNoHmac, |_| CredPrf::TrueTrueHmac)
   1397                                 } else {
   1398                                     cred.static_state
   1399                                         .extensions
   1400                                         .hmac_secret
   1401                                         .map_or(CredPrf::FalseNoHmac, |_| CredPrf::FalseFalseHmac)
   1402                                 }
   1403                             },
   1404                         ),
   1405                     )
   1406                     .map_err(AuthCeremonyErr::Extension)
   1407                     .and_then(|()| {
   1408                         // Step 22.
   1409                         options
   1410                             .sig_counter_enforcement
   1411                             .validate(cred.dynamic_state.sign_count, auth_data.sign_count())
   1412                             .and_then(|sig_counter| {
   1413                                 let prev_dyn_state = cred.dynamic_state;
   1414                                 // Step 24 item 2.
   1415                                 cred.dynamic_state.backup = flags.backup;
   1416                                 if options.update_uv && flags.user_verified {
   1417                                     // Step 24 item 3.
   1418                                     cred.dynamic_state.user_verified = true;
   1419                                 }
   1420                                 // Step 24 item 1.
   1421                                 cred.dynamic_state.sign_count = sig_counter;
   1422                                 cred.dynamic_state.authenticator_attachment = auth_attachment;
   1423                                 // Step 25.
   1424                                 if flags.user_verified {
   1425                                     Ok(())
   1426                                 } else {
   1427                                     match cred.static_state.extensions.cred_protect {
   1428                                         CredentialProtectionPolicy::None | CredentialProtectionPolicy::UserVerificationOptional => Ok(()),
   1429                                         CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList => {
   1430                                             if DISCOVERABLE {
   1431                                                 Err(AuthCeremonyErr::DiscoverableCredProtectCredentialIdList)
   1432                                             } else {
   1433                                                 Ok(())
   1434                                             }
   1435                                         }
   1436                                         CredentialProtectionPolicy::UserVerificationRequired => {
   1437                                             Err(AuthCeremonyErr::UserNotVerifiedCredProtectRequired)
   1438                                         }
   1439                                     }
   1440                                 }.inspect_err(|_| {
   1441                                     cred.dynamic_state = prev_dyn_state;
   1442                                 }).map(|()| {
   1443                                     prev_dyn_state != cred.dynamic_state
   1444                                 })
   1445                             })
   1446                     })
   1447                 })
   1448         })
   1449     }
   1450     #[cfg(all(test, feature = "custom", feature = "serializable_server_state"))]
   1451     fn is_eq(&self, other: &Self) -> bool {
   1452         self.challenge == other.challenge
   1453             && self.user_verification == other.user_verification
   1454             && self.extensions == other.extensions
   1455             && self.expiration == other.expiration
   1456     }
   1457 }
   1458 impl TimedCeremony for AuthenticationServerState {
   1459     #[cfg(any(doc, not(feature = "serializable_server_state")))]
   1460     #[inline]
   1461     fn expiration(&self) -> Instant {
   1462         self.expiration
   1463     }
   1464     #[cfg(all(not(doc), feature = "serializable_server_state"))]
   1465     #[inline]
   1466     fn expiration(&self) -> SystemTime {
   1467         self.expiration
   1468     }
   1469 }
   1470 impl TimedCeremony for DiscoverableAuthenticationServerState {
   1471     #[cfg(any(doc, not(feature = "serializable_server_state")))]
   1472     #[inline]
   1473     fn expiration(&self) -> Instant {
   1474         self.0.expiration()
   1475     }
   1476     #[cfg(all(not(doc), feature = "serializable_server_state"))]
   1477     #[inline]
   1478     fn expiration(&self) -> SystemTime {
   1479         self.0.expiration()
   1480     }
   1481 }
   1482 impl TimedCeremony for NonDiscoverableAuthenticationServerState {
   1483     #[cfg(any(doc, not(feature = "serializable_server_state")))]
   1484     #[inline]
   1485     fn expiration(&self) -> Instant {
   1486         self.state.expiration()
   1487     }
   1488     #[cfg(all(not(doc), feature = "serializable_server_state"))]
   1489     #[inline]
   1490     fn expiration(&self) -> SystemTime {
   1491         self.state.expiration()
   1492     }
   1493 }
   1494 impl<const USER_LEN: usize, const DISCOVERABLE: bool> Ceremony<USER_LEN, DISCOVERABLE>
   1495     for AuthenticationServerState
   1496 {
   1497     type R = Authentication<USER_LEN, DISCOVERABLE>;
   1498     fn rand_challenge(&self) -> SentChallenge {
   1499         self.challenge
   1500     }
   1501     #[cfg(not(feature = "serializable_server_state"))]
   1502     fn expiry(&self) -> Instant {
   1503         self.expiration
   1504     }
   1505     #[cfg(feature = "serializable_server_state")]
   1506     fn expiry(&self) -> SystemTime {
   1507         self.expiration
   1508     }
   1509     fn user_verification(&self) -> UserVerificationRequirement {
   1510         self.user_verification
   1511     }
   1512 }
   1513 impl Borrow<SentChallenge> for AuthenticationServerState {
   1514     #[inline]
   1515     fn borrow(&self) -> &SentChallenge {
   1516         &self.challenge
   1517     }
   1518 }
   1519 impl PartialEq for AuthenticationServerState {
   1520     #[inline]
   1521     fn eq(&self, other: &Self) -> bool {
   1522         self.challenge == other.challenge
   1523     }
   1524 }
   1525 impl PartialEq<&Self> for AuthenticationServerState {
   1526     #[inline]
   1527     fn eq(&self, other: &&Self) -> bool {
   1528         *self == **other
   1529     }
   1530 }
   1531 impl PartialEq<AuthenticationServerState> for &AuthenticationServerState {
   1532     #[inline]
   1533     fn eq(&self, other: &AuthenticationServerState) -> bool {
   1534         **self == *other
   1535     }
   1536 }
   1537 impl Eq for AuthenticationServerState {}
   1538 impl Hash for AuthenticationServerState {
   1539     #[inline]
   1540     fn hash<H: Hasher>(&self, state: &mut H) {
   1541         self.challenge.hash(state);
   1542     }
   1543 }
   1544 impl PartialOrd for AuthenticationServerState {
   1545     #[inline]
   1546     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
   1547         Some(self.cmp(other))
   1548     }
   1549 }
   1550 impl Ord for AuthenticationServerState {
   1551     #[inline]
   1552     fn cmp(&self, other: &Self) -> Ordering {
   1553         self.challenge.cmp(&other.challenge)
   1554     }
   1555 }
   1556 impl Borrow<SentChallenge> for DiscoverableAuthenticationServerState {
   1557     #[inline]
   1558     fn borrow(&self) -> &SentChallenge {
   1559         self.0.borrow()
   1560     }
   1561 }
   1562 impl PartialEq for DiscoverableAuthenticationServerState {
   1563     #[inline]
   1564     fn eq(&self, other: &Self) -> bool {
   1565         self.0 == other.0
   1566     }
   1567 }
   1568 impl PartialEq<&Self> for DiscoverableAuthenticationServerState {
   1569     #[inline]
   1570     fn eq(&self, other: &&Self) -> bool {
   1571         self.0 == other.0
   1572     }
   1573 }
   1574 impl PartialEq<DiscoverableAuthenticationServerState> for &DiscoverableAuthenticationServerState {
   1575     #[inline]
   1576     fn eq(&self, other: &DiscoverableAuthenticationServerState) -> bool {
   1577         self.0 == other.0
   1578     }
   1579 }
   1580 impl Eq for DiscoverableAuthenticationServerState {}
   1581 impl Hash for DiscoverableAuthenticationServerState {
   1582     #[inline]
   1583     fn hash<H: Hasher>(&self, state: &mut H) {
   1584         self.0.hash(state);
   1585     }
   1586 }
   1587 impl PartialOrd for DiscoverableAuthenticationServerState {
   1588     #[inline]
   1589     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
   1590         Some(self.cmp(other))
   1591     }
   1592 }
   1593 impl Ord for DiscoverableAuthenticationServerState {
   1594     #[inline]
   1595     fn cmp(&self, other: &Self) -> Ordering {
   1596         self.0.cmp(&other.0)
   1597     }
   1598 }
   1599 impl Borrow<SentChallenge> for NonDiscoverableAuthenticationServerState {
   1600     #[inline]
   1601     fn borrow(&self) -> &SentChallenge {
   1602         self.state.borrow()
   1603     }
   1604 }
   1605 impl PartialEq for NonDiscoverableAuthenticationServerState {
   1606     #[inline]
   1607     fn eq(&self, other: &Self) -> bool {
   1608         self.state == other.state
   1609     }
   1610 }
   1611 impl PartialEq<&Self> for NonDiscoverableAuthenticationServerState {
   1612     #[inline]
   1613     fn eq(&self, other: &&Self) -> bool {
   1614         self.state == other.state
   1615     }
   1616 }
   1617 impl PartialEq<NonDiscoverableAuthenticationServerState>
   1618     for &NonDiscoverableAuthenticationServerState
   1619 {
   1620     #[inline]
   1621     fn eq(&self, other: &NonDiscoverableAuthenticationServerState) -> bool {
   1622         self.state == other.state
   1623     }
   1624 }
   1625 impl Eq for NonDiscoverableAuthenticationServerState {}
   1626 impl Hash for NonDiscoverableAuthenticationServerState {
   1627     #[inline]
   1628     fn hash<H: Hasher>(&self, state: &mut H) {
   1629         self.state.hash(state);
   1630     }
   1631 }
   1632 impl PartialOrd for NonDiscoverableAuthenticationServerState {
   1633     #[inline]
   1634     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
   1635         Some(self.cmp(other))
   1636     }
   1637 }
   1638 impl Ord for NonDiscoverableAuthenticationServerState {
   1639     #[inline]
   1640     fn cmp(&self, other: &Self) -> Ordering {
   1641         self.state.cmp(&other.state)
   1642     }
   1643 }