webauthn_rp

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

auth.rs (50316B)


      1 #[cfg(doc)]
      2 use super::{
      3     super::response::{
      4         auth::AuthenticatorData,
      5         register::{DynamicState, StaticState},
      6         Backup, CollectedClientData, Flag,
      7     },
      8     register::{self, PublicKeyCredentialCreationOptions},
      9     AsciiDomain, DomainOrigin, Url,
     10 };
     11 use super::{
     12     super::{
     13         response::{
     14             auth::{
     15                 error::{AuthCeremonyErr, ExtensionErr, OneOrTwo},
     16                 Authentication, AuthenticatorExtensionOutput, HmacSecret,
     17             },
     18             register::CompressedPubKey,
     19             AuthenticatorAttachment,
     20         },
     21         AuthenticatedCredential,
     22     },
     23     auth::error::{RequestOptionsErr, SecondFactorErr},
     24     BackupReq, Ceremony, CeremonyOptions, Challenge, CredentialId, Credentials, ExtensionReq, Hint,
     25     Origin, PublicKeyCredentialDescriptor, RpId, SentChallenge, ServerState,
     26     UserVerificationRequirement, THREE_HUNDRED_THOUSAND,
     27 };
     28 use core::{
     29     borrow::Borrow,
     30     cmp::Ordering,
     31     hash::{Hash, Hasher},
     32     num::{NonZeroU32, NonZeroU64},
     33     time::Duration,
     34 };
     35 #[cfg(any(doc, not(feature = "serializable_server_state")))]
     36 use std::time::Instant;
     37 #[cfg(any(doc, feature = "serializable_server_state"))]
     38 use std::time::SystemTime;
     39 /// Contains error types.
     40 pub mod error;
     41 /// Contains functionality to serialize data to a client.
     42 #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
     43 #[cfg(feature = "serde")]
     44 mod ser;
     45 /// Contains functionality to (de)serialize [`AuthenticationServerState`] to a data store.
     46 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))]
     47 #[cfg(feature = "serializable_server_state")]
     48 pub mod ser_server_state;
     49 /// Controls how [signature counter](https://www.w3.org/TR/webauthn-3/#signature-counter) is enforced.
     50 ///
     51 /// Note that if the previous signature counter is positive and the new counter is not strictly greater, then the
     52 /// authenticator is likely a clone (i.e., there are at least two copies of the private key).
     53 #[derive(Clone, Copy, Debug, Default)]
     54 pub enum SignatureCounterEnforcement {
     55     /// Fail the authentication ceremony if the counter is less than or equal to the previous value when the
     56     /// previous value is positive.
     57     #[default]
     58     Fail,
     59     /// When the counter is less than the previous value, don't fail and update the value.
     60     ///
     61     /// Note in the special case that the new signature counter is 0, [`DynamicState::sign_count`] _won't_
     62     /// be updated since that would allow an attacker to permanently disable the counter.
     63     Update,
     64     /// When the counter is less than the previous value, don't fail but don't update the value.
     65     Ignore,
     66 }
     67 impl SignatureCounterEnforcement {
     68     /// Validates the signature counter based on `self`.
     69     const fn validate(self, prev: u32, cur: u32) -> Result<u32, AuthCeremonyErr> {
     70         if prev == 0 || cur > prev {
     71             Ok(cur)
     72         } else {
     73             match self {
     74                 Self::Fail => Err(AuthCeremonyErr::SignatureCounter),
     75                 // When the new counter is `0`, we use the previous counter to avoid an attacker from
     76                 // being able to permanently disable it.
     77                 Self::Update => Ok(if cur == 0 { prev } else { cur }),
     78                 Self::Ignore => Ok(prev),
     79             }
     80         }
     81     }
     82 }
     83 /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues).
     84 ///
     85 /// This is only applicable if
     86 /// [`hmac-secret`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-hmac-secret-extension)
     87 /// is `true` when registering a new credential with [`register::Extension::prf`] and
     88 /// [`PublicKeyCredentialRequestOptions::user_verification`] is [`UserVerificationRequirement::Required`].
     89 ///
     90 /// Unlike the spec, it is forbidden for
     91 /// [the decrypted outputs](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfoutputs) to be
     92 /// passed back in an effort to ensure sensitive data remains client-side. This means
     93 /// [`prf`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfoutputs) must not exist,
     94 /// be `null`, or be an
     95 /// [`AuthenticationExtensionsPRFOutputs`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfoutputs)
     96 /// such that [`results`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-results) does not exist,
     97 /// is `null`, or is an
     98 /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues) such
     99 /// that [`first`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-first) is `null` and
    100 /// [`second`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-second) does not exist or is `null`.
    101 ///
    102 /// For the owned analog, see [`PrfInputOwned`].
    103 ///
    104 /// When relying on discoverable requests
    105 /// (i.e., [`PublicKeyCredentialRequestOptions::allow_credentials`] is empty),
    106 /// one will likely use a static PRF input for _all_ credentials since rolling over PRF inputs
    107 /// is not feasible. One uses this type for such a thing. In other words, `'a` will likely
    108 /// be `'static` and [`Self::second`] will likely be `None`.
    109 #[derive(Clone, Copy, Debug)]
    110 pub struct PrfInput<'a> {
    111     /// [`first`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-first).
    112     pub first: &'a [u8],
    113     /// [`second`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-second).
    114     pub second: Option<&'a [u8]>,
    115     /// Response requirements.
    116     pub ext_info: ExtensionReq,
    117 }
    118 /// Owned version of [`PrfInput`].
    119 ///
    120 /// When relying on non-discoverable requests
    121 /// (i.e., [`PublicKeyCredentialRequestOptions::allow_credentials`] is non-empty),
    122 /// it's recommended to use credential-specific PRF inputs that are continuously rolled over.
    123 /// One uses this type for such a thing.
    124 #[derive(Debug)]
    125 pub struct PrfInputOwned {
    126     /// [`first`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-first).
    127     pub first: Vec<u8>,
    128     /// [`second`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-second).
    129     pub second: Option<Vec<u8>>,
    130     /// Response requirements.
    131     pub ext_info: ExtensionReq,
    132 }
    133 /// The [defined extensions](https://www.w3.org/TR/webauthn-3/#sctn-defined-extensions) to send to the client.
    134 #[derive(Clone, Copy, Debug, Default)]
    135 pub struct Extension<'prf> {
    136     /// [`prf`](https://www.w3.org/TR/webauthn-3/#prf-extension).
    137     ///
    138     /// If both [`CredentialSpecificExtension::prf`] and this are [`Some`], then `CredentialSpecificExtension::prf`
    139     /// takes priority.
    140     pub prf: Option<PrfInput<'prf>>,
    141 }
    142 /// The [defined extensions](https://www.w3.org/TR/webauthn-3/#sctn-defined-extensions) to send to the client that
    143 /// are credential-specific which among other things implies a non-discoverable request.
    144 #[derive(Debug, Default)]
    145 pub struct CredentialSpecificExtension {
    146     /// [`prf`](https://www.w3.org/TR/webauthn-3/#prf-extension).
    147     ///
    148     /// If both [`Extension::prf`] and this are [`Some`], then this take priority.
    149     pub prf: Option<PrfInputOwned>,
    150 }
    151 /// Registered credential used in
    152 /// [`allowCredentials`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-allowcredentials).
    153 #[derive(Debug)]
    154 pub struct AllowedCredential {
    155     /// The registered credential.
    156     pub credential: PublicKeyCredentialDescriptor<Vec<u8>>,
    157     /// Credential-specific extensions.
    158     pub extension: CredentialSpecificExtension,
    159 }
    160 impl From<PublicKeyCredentialDescriptor<Vec<u8>>> for AllowedCredential {
    161     #[inline]
    162     fn from(credential: PublicKeyCredentialDescriptor<Vec<u8>>) -> Self {
    163         Self {
    164             credential,
    165             extension: CredentialSpecificExtension::default(),
    166         }
    167     }
    168 }
    169 /// Queue of unique [`AllowedCredential`]s.
    170 #[derive(Debug, Default)]
    171 pub struct AllowedCredentials {
    172     /// Allowed credentials.
    173     creds: Vec<AllowedCredential>,
    174     /// Number of `AllowedCredential`s that have PRF inputs.
    175     ///
    176     /// Useful to help serialization.
    177     prf_count: usize,
    178 }
    179 impl Credentials for AllowedCredentials {
    180     type Credential = AllowedCredential;
    181     /// # Examples
    182     ///
    183     /// ```
    184     /// # use webauthn_rp::request::{auth::AllowedCredentials, Credentials};
    185     /// assert!(AllowedCredentials::with_capacity(1).as_ref().is_empty());
    186     /// ```
    187     #[inline]
    188     #[must_use]
    189     fn with_capacity(capacity: usize) -> Self {
    190         Self {
    191             creds: Vec::with_capacity(capacity),
    192             prf_count: 0,
    193         }
    194     }
    195     /// # Examples
    196     ///
    197     /// ```
    198     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    199     /// # use webauthn_rp::{bin::Decode, response::bin::DecodeAuthTransportsErr};
    200     /// # use webauthn_rp::{
    201     /// #     request::{auth::AllowedCredentials, PublicKeyCredentialDescriptor, Credentials},
    202     /// #     response::{AuthTransports, CredentialId},
    203     /// # };
    204     /// /// Retrieves the `AuthTransports` associated with the unique `cred_id`
    205     /// /// from the database.
    206     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    207     /// fn get_transports(cred_id: CredentialId<&[u8]>) -> Result<AuthTransports, DecodeAuthTransportsErr> {
    208     ///     // ⋮
    209     /// #     AuthTransports::decode(32)
    210     /// }
    211     /// let mut creds = AllowedCredentials::with_capacity(1);
    212     /// assert!(creds.as_ref().is_empty());
    213     /// // `CredentialId::try_from` only exists when `custom` is enabled; and even then, it is
    214     /// // likely never needed since the `CredentialId` was originally sent from the client and is likely
    215     /// // stored in a database which would be fetched by `UserHandle` or `Authentication::raw_id`.
    216     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    217     /// let id = CredentialId::try_from(vec![0; 16])?;
    218     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    219     /// let transports = get_transports((&id).into())?;
    220     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    221     /// assert!(creds.push(PublicKeyCredentialDescriptor { id, transports }.into()));
    222     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    223     /// let id_copy = CredentialId::try_from(vec![0; 16])?;
    224     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    225     /// let transports_2 = AuthTransports::NONE;
    226     /// // Duplicate `CredentialId`s don't get added.
    227     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    228     /// assert!(!creds.push(
    229     ///     PublicKeyCredentialDescriptor {
    230     ///         id: id_copy,
    231     ///         transports: transports_2
    232     ///     }
    233     ///     .into()
    234     /// ));
    235     /// # Ok::<_, webauthn_rp::AggErr>(())
    236     /// ```
    237     #[expect(
    238         clippy::arithmetic_side_effects,
    239         reason = "comment explains how overflow is not possible"
    240     )]
    241     #[inline]
    242     fn push(&mut self, cred: Self::Credential) -> bool {
    243         self.creds
    244             .iter()
    245             .try_fold((), |(), c| {
    246                 if c.credential.id == cred.credential.id {
    247                     Err(())
    248                 } else {
    249                     Ok(())
    250                 }
    251             })
    252             .is_ok_and(|()| {
    253                 // This can't overflow since `self.creds.push` would `panic` since
    254                 // `self.prf_count <= self.creds.len()`.
    255                 self.prf_count += usize::from(cred.extension.prf.is_some());
    256                 self.creds.push(cred);
    257                 true
    258             })
    259     }
    260     #[inline]
    261     fn len(&self) -> usize {
    262         self.creds.len()
    263     }
    264 }
    265 impl AsRef<[AllowedCredential]> for AllowedCredentials {
    266     #[inline]
    267     fn as_ref(&self) -> &[AllowedCredential] {
    268         self.creds.as_slice()
    269     }
    270 }
    271 impl From<&AllowedCredentials> for Vec<CredInfo> {
    272     #[inline]
    273     fn from(value: &AllowedCredentials) -> Self {
    274         let len = value.creds.len();
    275         value
    276             .creds
    277             .iter()
    278             .fold(Self::with_capacity(len), |mut creds, cred| {
    279                 creds.push(CredInfo {
    280                     id: cred.credential.id.clone(),
    281                     ext: (&cred.extension).into(),
    282                 });
    283                 creds
    284             })
    285     }
    286 }
    287 /// Helper that verifies the overlap of [`PublicKeyCredentialRequestOptions::start_ceremony`] and
    288 /// [`AuthenticationServerState::decode`].
    289 fn validate_options_helper(
    290     ext: ServerExtensionInfo,
    291     uv: UserVerificationRequirement,
    292     creds: &[CredInfo],
    293 ) -> Result<(), RequestOptionsErr> {
    294     // If PRF is set, the user has to verify themselves.
    295     ext.prf
    296         .as_ref()
    297         .map_or(Ok(()), |_| {
    298             if matches!(uv, UserVerificationRequirement::Required) {
    299                 Ok(())
    300             } else {
    301                 Err(RequestOptionsErr::PrfWithoutUserVerification)
    302             }
    303         })
    304         .and_then(|()| {
    305             creds.iter().try_fold((), |(), cred| {
    306                 // If PRF is set, the user has to verify themselves.
    307                 cred.ext.prf.as_ref().map_or(Ok(()), |_| {
    308                     if matches!(uv, UserVerificationRequirement::Required) {
    309                         Ok(())
    310                     } else {
    311                         Err(RequestOptionsErr::PrfWithoutUserVerification)
    312                     }
    313                 })
    314             })
    315         })
    316 }
    317 /// The [`PublicKeyCredentialRequestOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrequestoptions)
    318 /// to send to the client when authenticating a credential.
    319 ///
    320 /// Upon saving the [`AuthenticationServerState`] returned from [`Self::start_ceremony`], one MUST send
    321 /// [`AuthenticationClientState`] to the client ASAP. After receiving the newly created [`Authentication`], it
    322 /// is validated using [`AuthenticationServerState::verify`].
    323 #[derive(Debug)]
    324 pub struct PublicKeyCredentialRequestOptions<'rp_id, 'prf> {
    325     /// [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-challenge).
    326     pub challenge: Challenge,
    327     /// [`timeout`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-timeout).
    328     ///
    329     /// Note we require a positive value despite the spec allowing an optional nonnegative value. This jives
    330     /// with the fact that in-memory storage is required when `serializable_server_state` is not enabled
    331     /// when authenticating credentials as no timeout would make out-of-memory (OOM) conditions more likely.
    332     pub timeout: NonZeroU32,
    333     /// [`rpId`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-rpid).
    334     ///
    335     /// This MUST be the same as the [`PublicKeyCredentialCreationOptions::rp_id`] used when the credential was registered.
    336     pub rp_id: &'rp_id RpId,
    337     /// [`allowCredentials`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-allowcredentials).
    338     pub allow_credentials: AllowedCredentials,
    339     /// [`userVerification`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-userverification).
    340     pub user_verification: UserVerificationRequirement,
    341     /// [`hints`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-hints).
    342     pub hints: Hint,
    343     /// [`extensions`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-extensions).
    344     pub extensions: Extension<'prf>,
    345 }
    346 impl<'rp_id, 'prf> PublicKeyCredentialRequestOptions<'rp_id, 'prf> {
    347     /// Creates a `PublicKeyCredentialRequestOptions` with [`Self::user_verification`] set to
    348     /// [`UserVerificationRequirement::Required`] and [`Self::timeout`] set to 5 minutes,
    349     ///
    350     /// Note `rp_id` _must_ be the same as the [`PublicKeyCredentialCreationOptions::rp_id`] when the
    351     /// credential was registered.
    352     ///
    353     /// # Examples
    354     ///
    355     /// ```
    356     /// # use webauthn_rp::request::{auth::PublicKeyCredentialRequestOptions, AsciiDomain, RpId, UserVerificationRequirement};
    357     /// assert!(matches!(
    358     ///     PublicKeyCredentialRequestOptions::passkey(&RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?)).user_verification,
    359     ///     UserVerificationRequirement::Required
    360     /// ));
    361     /// # Ok::<_, webauthn_rp::AggErr>(())
    362     /// ```
    363     #[inline]
    364     #[must_use]
    365     pub fn passkey<'a: 'rp_id>(rp_id: &'a RpId) -> Self {
    366         Self {
    367             challenge: Challenge::new(),
    368             timeout: THREE_HUNDRED_THOUSAND,
    369             rp_id,
    370             allow_credentials: AllowedCredentials::new(),
    371             user_verification: UserVerificationRequirement::Required,
    372             hints: Hint::None,
    373             extensions: Extension::default(),
    374         }
    375     }
    376     /// Creates a `PublicKeyCredentialRequestOptions` with [`Self::user_verification`] set to
    377     /// [`UserVerificationRequirement::Discouraged`] and [`Self::timeout`] set to 5 minutes.
    378     ///
    379     /// Note `rp_id` _must_ be the same as the [`PublicKeyCredentialCreationOptions::rp_id`] when the
    380     /// [`AllowedCredential`]s were registered.
    381     ///
    382     /// # Errors
    383     ///
    384     /// Errors iff [`AllowedCredentials`] is empty.
    385     ///
    386     /// # Examples
    387     ///
    388     /// ```
    389     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    390     /// # use webauthn_rp::{bin::Decode, response::bin::DecodeAuthTransportsErr};
    391     /// # use webauthn_rp::{
    392     /// #     request::{
    393     /// #         auth::{AllowedCredentials, PublicKeyCredentialRequestOptions},
    394     /// #         AsciiDomain, RpId, PublicKeyCredentialDescriptor, Credentials
    395     /// #     },
    396     /// #     response::{AuthTransports, CredentialId},
    397     /// # };
    398     /// /// Retrieves the `AuthTransports` associated with the unique `cred_id`
    399     /// /// from the database.
    400     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    401     /// fn get_transports(cred_id: CredentialId<&[u8]>) -> Result<AuthTransports, DecodeAuthTransportsErr> {
    402     ///     // ⋮
    403     /// #     AuthTransports::decode(32)
    404     /// }
    405     /// let mut creds = AllowedCredentials::with_capacity(1);
    406     /// assert!(creds.as_ref().is_empty());
    407     /// // `CredentialId::try_from` only exists when `custom` is enabled; and even then, it is
    408     /// // likely never needed since the `CredentialId` was originally sent from the client and is likely
    409     /// // stored in a database which would be fetched by `UserHandle` or `Authentication::raw_id`.
    410     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    411     /// let id = CredentialId::try_from(vec![0; 16])?;
    412     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    413     /// let transports = get_transports((&id).into())?;
    414     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    415     /// assert!(creds.push(PublicKeyCredentialDescriptor { id, transports }.into()));
    416     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    417     /// assert_eq!(
    418     ///     PublicKeyCredentialRequestOptions::second_factor(&RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?), creds)?
    419     ///         .allow_credentials
    420     ///         .as_ref()
    421     ///         .len(),
    422     ///     1
    423     /// );
    424     /// # Ok::<_, webauthn_rp::AggErr>(())
    425     /// ```
    426     #[inline]
    427     pub fn second_factor<'a: 'rp_id>(
    428         rp_id: &'a RpId,
    429         creds: AllowedCredentials,
    430     ) -> Result<Self, SecondFactorErr> {
    431         if creds.as_ref().is_empty() {
    432             Err(SecondFactorErr)
    433         } else {
    434             let mut opts = Self::passkey(rp_id);
    435             opts.allow_credentials = creds;
    436             opts.user_verification = UserVerificationRequirement::Discouraged;
    437             Ok(opts)
    438         }
    439     }
    440     /// Begins the [authentication ceremony](https://www.w3.org/TR/webauthn-3/#authentication-ceremony) consuming
    441     /// `self`. Note that the expiration [`Instant`]/[`SystemTime`] is saved, so `AuthenticationClientState` MUST be
    442     /// sent ASAP. In order to complete authentication, the returned `AuthenticationServerState` MUST be saved so
    443     /// that it can later be used to verify the credential assertion with [`AuthenticationServerState::verify`].
    444     ///
    445     /// # Errors
    446     ///
    447     /// Errors iff `self` contains incompatible configuration.
    448     ///
    449     /// # Examples
    450     ///
    451     /// ```
    452     /// # #[cfg(not(feature = "serializable_server_state"))]
    453     /// # use std::time::Instant;
    454     /// # #[cfg(not(feature = "serializable_server_state"))]
    455     /// # use webauthn_rp::request::ServerState;
    456     /// # use webauthn_rp::request::{auth::PublicKeyCredentialRequestOptions, AsciiDomain, RpId};
    457     /// # #[cfg(not(feature = "serializable_server_state"))]
    458     /// assert!(
    459     ///     PublicKeyCredentialRequestOptions::passkey(&RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?))
    460     ///         .start_ceremony()?
    461     ///         .0
    462     ///         .expiration() > Instant::now()
    463     /// );
    464     /// # Ok::<_, webauthn_rp::AggErr>(())
    465     /// ```
    466     #[inline]
    467     pub fn start_ceremony(
    468         self,
    469     ) -> Result<
    470         (
    471             AuthenticationServerState,
    472             AuthenticationClientState<'rp_id, 'prf>,
    473         ),
    474         RequestOptionsErr,
    475     > {
    476         let extensions = self.extensions.into();
    477         let allow_credentials = Vec::from(&self.allow_credentials);
    478         validate_options_helper(extensions, self.user_verification, &allow_credentials).and_then(
    479             |()| {
    480                 #[cfg(not(feature = "serializable_server_state"))]
    481                 let res = Instant::now();
    482                 #[cfg(feature = "serializable_server_state")]
    483                 let res = SystemTime::now();
    484                 res.checked_add(Duration::from_millis(NonZeroU64::from(self.timeout).get()))
    485                     .ok_or(RequestOptionsErr::InvalidTimeout)
    486                     .map(|expiration| {
    487                         (
    488                             AuthenticationServerState {
    489                                 challenge: SentChallenge(self.challenge.0),
    490                                 allow_credentials,
    491                                 user_verification: self.user_verification,
    492                                 extensions,
    493                                 expiration,
    494                             },
    495                             AuthenticationClientState(self),
    496                         )
    497                     })
    498             },
    499         )
    500     }
    501 }
    502 /// Container of a [`PublicKeyCredentialRequestOptions`] that has been used to start the authentication ceremony.
    503 /// This gets sent to the client ASAP.
    504 #[derive(Debug)]
    505 pub struct AuthenticationClientState<'rp_id, 'prf>(PublicKeyCredentialRequestOptions<'rp_id, 'prf>);
    506 impl AuthenticationClientState<'_, '_> {
    507     /// Returns the `PublicKeyCredentialRequestOptions` that was used to start an authentication ceremony.
    508     ///
    509     /// # Examples
    510     ///
    511     /// ```
    512     /// # use webauthn_rp::request::{auth::PublicKeyCredentialRequestOptions, AsciiDomain, RpId};
    513     /// assert!(
    514     ///     PublicKeyCredentialRequestOptions::passkey(&RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?))
    515     ///         .start_ceremony()?
    516     ///         .1
    517     ///         .options()
    518     ///         .allow_credentials
    519     ///         .as_ref()
    520     ///         .is_empty()
    521     /// );
    522     /// # Ok::<_, webauthn_rp::AggErr>(())
    523     /// ```
    524     #[inline]
    525     #[must_use]
    526     pub const fn options(&self) -> &PublicKeyCredentialRequestOptions<'_, '_> {
    527         &self.0
    528     }
    529 }
    530 /// `PrfInput` and `PrfInputOwned` without the actual data sent to reduce memory usage when storing [`AuthenticationServerState`]
    531 /// in an in-memory collection.
    532 #[derive(Clone, Copy, Debug)]
    533 enum ServerPrfInfo {
    534     /// `PrfInput::second` was `None`.
    535     One(ExtensionReq),
    536     /// `PrfInput::second` was `Some`.
    537     Two(ExtensionReq),
    538 }
    539 impl ServerPrfInfo {
    540     /// Returns the `ExtensionReq` sent to the client.
    541     const fn ext_info(self) -> ExtensionReq {
    542         match self {
    543             Self::One(info) | Self::Two(info) => info,
    544         }
    545     }
    546     /// Validates `val` based on the passed arguments.
    547     fn validate(
    548         val: Option<Self>,
    549         prf_capable: bool,
    550         hmac: HmacSecret,
    551         err_unsolicited: bool,
    552     ) -> Result<(), ExtensionErr> {
    553         match hmac {
    554             HmacSecret::None => {
    555                 if prf_capable {
    556                     val.map_or(Ok(()), |input| {
    557                         if matches!(input.ext_info(), ExtensionReq::Allow) {
    558                             Ok(())
    559                         } else {
    560                             Err(ExtensionErr::MissingHmacSecret)
    561                         }
    562                     })
    563                 } else {
    564                     // We check if the PRF extension was requested on an incapable credential;
    565                     // if so, we error.
    566                     val.map_or(Ok(()), |_| Err(ExtensionErr::HmacSecretForPrfIncapableCred))
    567                 }
    568             }
    569             HmacSecret::One => {
    570                 if prf_capable {
    571                     val.map_or_else(
    572                         || {
    573                             if err_unsolicited {
    574                                 Err(ExtensionErr::ForbiddenHmacSecret)
    575                             } else {
    576                                 Ok(())
    577                             }
    578                         },
    579                         |input| match input {
    580                             Self::One(_) => Ok(()),
    581                             Self::Two(_) => Err(ExtensionErr::InvalidHmacSecretValue(
    582                                 OneOrTwo::Two,
    583                                 OneOrTwo::One,
    584                             )),
    585                         },
    586                     )
    587                 } else {
    588                     Err(ExtensionErr::HmacSecretForPrfIncapableCred)
    589                 }
    590             }
    591             HmacSecret::Two => {
    592                 if prf_capable {
    593                     val.map_or_else(
    594                         || {
    595                             if err_unsolicited {
    596                                 Err(ExtensionErr::ForbiddenHmacSecret)
    597                             } else {
    598                                 Ok(())
    599                             }
    600                         },
    601                         |input| match input {
    602                             Self::One(_) => Err(ExtensionErr::InvalidHmacSecretValue(
    603                                 OneOrTwo::One,
    604                                 OneOrTwo::Two,
    605                             )),
    606                             Self::Two(_) => Ok(()),
    607                         },
    608                     )
    609                 } else {
    610                     Err(ExtensionErr::HmacSecretForPrfIncapableCred)
    611                 }
    612             }
    613         }
    614     }
    615 }
    616 impl From<PrfInput<'_>> for ServerPrfInfo {
    617     fn from(value: PrfInput<'_>) -> Self {
    618         value
    619             .second
    620             .map_or_else(|| Self::One(value.ext_info), |_| Self::Two(value.ext_info))
    621     }
    622 }
    623 impl From<&PrfInputOwned> for ServerPrfInfo {
    624     fn from(value: &PrfInputOwned) -> Self {
    625         value
    626             .second
    627             .as_ref()
    628             .map_or_else(|| Self::One(value.ext_info), |_| Self::Two(value.ext_info))
    629     }
    630 }
    631 /// `Extension` without the actual data sent to reduce memory usage when storing [`AuthenticationServerState`]
    632 /// in an in-memory collection.
    633 #[derive(Clone, Copy, Debug)]
    634 struct ServerExtensionInfo {
    635     /// `Extension::prf`.
    636     prf: Option<ServerPrfInfo>,
    637 }
    638 impl From<Extension<'_>> for ServerExtensionInfo {
    639     fn from(value: Extension<'_>) -> Self {
    640         Self {
    641             prf: value.prf.map(ServerPrfInfo::from),
    642         }
    643     }
    644 }
    645 /// `CredentialSpecificExtension` without the actual data sent to reduce memory usage when storing [`AuthenticationServerState`]
    646 /// in an in-memory collection.
    647 #[derive(Clone, Copy, Debug)]
    648 struct ServerCredSpecificExtensionInfo {
    649     /// `CredentialSpecificExtension::prf`.
    650     prf: Option<ServerPrfInfo>,
    651 }
    652 impl From<&CredentialSpecificExtension> for ServerCredSpecificExtensionInfo {
    653     fn from(value: &CredentialSpecificExtension) -> Self {
    654         Self {
    655             prf: value.prf.as_ref().map(ServerPrfInfo::from),
    656         }
    657     }
    658 }
    659 impl ServerExtensionInfo {
    660     /// Validates the extensions.
    661     ///
    662     /// Note that this MUST only be called internally by `auth::validate_extensions`.
    663     fn validate_extensions(
    664         self,
    665         auth_ext: AuthenticatorExtensionOutput,
    666         error_unsolicited: bool,
    667         prf_capable: bool,
    668     ) -> Result<(), ExtensionErr> {
    669         ServerPrfInfo::validate(
    670             self.prf,
    671             prf_capable,
    672             auth_ext.hmac_secret,
    673             error_unsolicited,
    674         )
    675     }
    676 }
    677 /// Validates the extensions.
    678 fn validate_extensions(
    679     ext: ServerExtensionInfo,
    680     cred_ext: Option<ServerCredSpecificExtensionInfo>,
    681     auth_ext: AuthenticatorExtensionOutput,
    682     error_unsolicited: bool,
    683     prf_capable: bool,
    684 ) -> Result<(), ExtensionErr> {
    685     cred_ext.map_or_else(
    686         || {
    687             // No client-specific extensions, so we can simply focus on `ext`.
    688             ext.validate_extensions(auth_ext, error_unsolicited, prf_capable)
    689         },
    690         |c_ext| {
    691             // Must carefully process each extension based on overlap and which gets priority over the other.
    692             c_ext.prf.as_ref().map_or_else(
    693                 || {
    694                     ServerPrfInfo::validate(
    695                         ext.prf,
    696                         prf_capable,
    697                         auth_ext.hmac_secret,
    698                         error_unsolicited,
    699                     )
    700                 },
    701                 |_| {
    702                     ServerPrfInfo::validate(
    703                         c_ext.prf,
    704                         prf_capable,
    705                         auth_ext.hmac_secret,
    706                         error_unsolicited,
    707                     )
    708                 },
    709             )
    710         },
    711     )
    712 }
    713 /// [`AllowedCredential`] with less data to reduce memory usage when storing [`AuthenticationServerState`]
    714 /// in an in-memory collection.
    715 #[derive(Debug)]
    716 struct CredInfo {
    717     /// The Credential ID.
    718     id: CredentialId<Vec<u8>>,
    719     /// Any credential-specific extensions.
    720     ext: ServerCredSpecificExtensionInfo,
    721 }
    722 /// Controls how to handle a change in [`DynamicState::authenticator_attachment`].
    723 ///
    724 /// Note when `DynamicState::authenticator_attachment` is [`AuthenticatorAttachment::None`], then it will
    725 /// be updated regardless. Similarly when [`Authentication::authenticator_attachment`] is
    726 /// `AuthenticatorAttachment::None`, it will never update `DynamicState::authenticator_attachment`.
    727 #[derive(Clone, Copy, Debug)]
    728 pub enum AuthenticatorAttachmentEnforcement {
    729     /// Fail the authentication ceremony if [`AuthenticatorAttachment`] is not the same.
    730     ///
    731     /// The contained `bool` represents if `AuthenticatorAttachment` must be sent.
    732     Fail(bool),
    733     /// Update [`DynamicState::authenticator_attachment`] to the sent [`AuthenticatorAttachment`].
    734     ///
    735     /// The contained `bool` represents if `AuthenticatorAttachment` must be sent.
    736     Update(bool),
    737     /// Do not update [`DynamicState::authenticator_attachment`] to the sent [`AuthenticatorAttachment`].
    738     ///
    739     /// The contained `bool` represents if `AuthenticatorAttachment` must be sent.
    740     Ignore(bool),
    741 }
    742 impl AuthenticatorAttachmentEnforcement {
    743     /// Validates `cur` based on `self` and `prev`.
    744     const fn validate(
    745         self,
    746         prev: AuthenticatorAttachment,
    747         cur: AuthenticatorAttachment,
    748     ) -> Result<AuthenticatorAttachment, AuthCeremonyErr> {
    749         match cur {
    750             AuthenticatorAttachment::None => match self {
    751                 Self::Fail(require) | Self::Update(require) | Self::Ignore(require) => {
    752                     if require {
    753                         Err(AuthCeremonyErr::MissingAuthenticatorAttachment)
    754                     } else {
    755                         // We don't overwrite the previous one with [`AuthenticatorAttachment::None`].
    756                         Ok(prev)
    757                     }
    758                 }
    759             },
    760             AuthenticatorAttachment::Platform => match self {
    761                 Self::Fail(_) => {
    762                     if matches!(prev, AuthenticatorAttachment::CrossPlatform) {
    763                         Err(AuthCeremonyErr::AuthenticatorAttachmentMismatch)
    764                     } else {
    765                         // We don't fail when we previously had [`AuthenticatorAttachment::None`].
    766                         Ok(cur)
    767                     }
    768                 }
    769                 Self::Update(_) => Ok(cur),
    770                 Self::Ignore(_) => {
    771                     if matches!(prev, AuthenticatorAttachment::None) {
    772                         // We overwrite the previous one when it is [`AuthenticatorAttachment::None`].
    773                         Ok(cur)
    774                     } else {
    775                         Ok(prev)
    776                     }
    777                 }
    778             },
    779             AuthenticatorAttachment::CrossPlatform => match self {
    780                 Self::Fail(_) => {
    781                     if matches!(prev, AuthenticatorAttachment::Platform) {
    782                         Err(AuthCeremonyErr::AuthenticatorAttachmentMismatch)
    783                     } else {
    784                         // We don't fail when we previously had [`AuthenticatorAttachment::None`].
    785                         Ok(cur)
    786                     }
    787                 }
    788                 Self::Update(_) => Ok(cur),
    789                 Self::Ignore(_) => {
    790                     if matches!(prev, AuthenticatorAttachment::None) {
    791                         // We overwrite the previous one when it is [`AuthenticatorAttachment::None`].
    792                         Ok(cur)
    793                     } else {
    794                         Ok(prev)
    795                     }
    796                 }
    797             },
    798         }
    799     }
    800 }
    801 impl Default for AuthenticatorAttachmentEnforcement {
    802     /// Returns [`Self::Ignore`] containing `false`.
    803     #[inline]
    804     fn default() -> Self {
    805         Self::Ignore(false)
    806     }
    807 }
    808 /// Additional verification options to perform in [`AuthenticationServerState::verify`].
    809 #[derive(Clone, Copy, Debug)]
    810 pub struct AuthenticationVerificationOptions<'origins, 'top_origins, O, T> {
    811     /// Origins to use for [origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin).
    812     ///
    813     /// When this is empty, the origin that will be used will be based on
    814     /// the [`RpId`] passed to [`AuthenticationServerState::verify`]. If [`RpId::Domain`], then the [`DomainOrigin`] returned from
    815     /// passing [`AsciiDomain::as_ref`] to [`DomainOrigin::new`] will be used; otherwise the [`Url`] in
    816     /// [`RpId::Url`] will be used.
    817     pub allowed_origins: &'origins [O],
    818     /// [Top-level origins](https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-top-level-origin)
    819     /// to use for [origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin).
    820     ///
    821     /// When this is `Some`, [`CollectedClientData::cross_origin`] is allowed to be `true`. When the contained
    822     /// `slice` is empty, [`CollectedClientData::top_origin`] must be `None`. When this is `None`,
    823     /// `CollectedClientData::cross_origin` must be `false` and `CollectedClientData::top_origin` must be `None`.
    824     pub allowed_top_origins: Option<&'top_origins [T]>,
    825     /// The required [`Backup`] state of the credential.
    826     ///
    827     /// Note that `None` is _not_ the same as `Some(BackupReq::None)` as the latter indicates that any [`Backup`]
    828     /// is allowed. This is rarely what you want; instead, `None` indicates that [`BackupReq::from`] applied to
    829     /// [`DynamicState::backup`] will be used in [`AuthenticationServerState::verify`].
    830     pub backup_requirement: Option<BackupReq>,
    831     /// Error when unsolicited extensions are sent back iff `true`.
    832     pub error_on_unsolicited_extensions: bool,
    833     /// Dictates what happens when [`Authentication::authenticator_attachment`] is not the same as
    834     /// [`DynamicState::authenticator_attachment`].
    835     pub auth_attachment_enforcement: AuthenticatorAttachmentEnforcement,
    836     /// [`DynamicState::user_verified`] will be set to `true` iff [`Flag::user_verified`] when `true`.
    837     pub update_uv: bool,
    838     /// Dictates what happens when [`AuthenticatorData::sign_count`] is not updated to a strictly greater value.
    839     pub sig_counter_enforcement: SignatureCounterEnforcement,
    840     /// [`CollectedClientData::from_client_data_json_relaxed`] is used to extract [`CollectedClientData`] iff `true`.
    841     #[cfg_attr(docsrs, doc(cfg(feature = "serde_relaxed")))]
    842     #[cfg(feature = "serde_relaxed")]
    843     pub client_data_json_relaxed: bool,
    844 }
    845 impl<O, T> Default for AuthenticationVerificationOptions<'_, '_, O, T> {
    846     /// Returns `Self` such that [`Self::allowed_origins`] is empty, [`Self::allowed_top_origins`] is `None`,
    847     /// [`Self::backup_requirement`] is `None`, [`Self::error_on_unsolicited_extensions`] is `true`,
    848     /// [`Self::auth_attachment_enforcement`] is [`AuthenticatorAttachmentEnforcement::default`],
    849     /// [`Self::update_uv`] is `false`, [`Self::sig_counter_enforcement`] is
    850     /// [`SignatureCounterEnforcement::Fail`], and [`Self::client_data_json_relaxed`] is `true`.
    851     #[inline]
    852     fn default() -> Self {
    853         Self {
    854             allowed_origins: &[],
    855             allowed_top_origins: None,
    856             backup_requirement: None,
    857             error_on_unsolicited_extensions: true,
    858             auth_attachment_enforcement: AuthenticatorAttachmentEnforcement::default(),
    859             update_uv: false,
    860             sig_counter_enforcement: SignatureCounterEnforcement::default(),
    861             #[cfg(feature = "serde_relaxed")]
    862             client_data_json_relaxed: true,
    863         }
    864     }
    865 }
    866 // This is essentially the `PublicKeyCredentialRequestOptions` used to create it; however to reduce
    867 // memory usage, we remove all unnecessary data making an instance of this 64 bytes in size when
    868 // `Self::allow_credentials` is empty on `x86_64-unknown-linux-gnu` platforms.
    869 //
    870 // The total memory used is dependent on the number of `AllowedCredential`s and the size of each `CredentialId`.
    871 // To be exact, it is the following:
    872 // 64 + i(32 + 56n + Σj_k from k=0 to k=m-1) where i is 0 iff `AllowedCredentials` has capacity 0; otherwise 1,
    873 // n is `AllowedCredentials` capacity, j_k is the kth `CredentialId` in `AllowedCredentials` and `m` is
    874 // `AllowedCredentials::len`.
    875 /// State needed to be saved when beginning the authentication ceremony.
    876 ///
    877 /// Saves the necessary information associated with the [`PublicKeyCredentialRequestOptions`] used to create it
    878 /// via [`PublicKeyCredentialRequestOptions::start_ceremony`] so that authentication of a credential can be
    879 /// performed with [`Self::verify`].
    880 ///
    881 /// `AuthenticationServerState` implements [`Borrow`] of [`SentChallenge`]; thus to obtain the correct
    882 /// `AuthenticationServerState` associated with an [`Authentication`], one should use its corresponding
    883 /// [`Authentication::challenge`].
    884 #[derive(Debug)]
    885 pub struct AuthenticationServerState {
    886     // This is a `SentChallenge` since we need `AuthenticationServerState` to be fetchable after receiving the
    887     // response from the client. This response must obviously be constructable; thus its challenge is a
    888     // `SentChallenge`.
    889     //
    890     // This must never be mutated since we want to ensure it is actually a `Challenge` (which
    891     // can only be constructed via `Challenge::new`). This is guaranteed to be true iff
    892     // `serializable_server_state` is not enabled.
    893     /// [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-challenge).
    894     challenge: SentChallenge,
    895     /// [`allowCredentials`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-allowcredentials).
    896     allow_credentials: Vec<CredInfo>,
    897     /// [`userVerification`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-userverification).
    898     user_verification: UserVerificationRequirement,
    899     /// [`extensions`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-extensions).
    900     extensions: ServerExtensionInfo,
    901     /// `Instant` the ceremony expires.
    902     #[cfg(not(feature = "serializable_server_state"))]
    903     expiration: Instant,
    904     /// `SystemTime` the ceremony expires.
    905     #[cfg(feature = "serializable_server_state")]
    906     expiration: SystemTime,
    907 }
    908 impl AuthenticationServerState {
    909     /// Verifies `response` is valid based on `self` consuming `self` and updating `cred`. Returns `true`
    910     /// iff `cred` was mutated.
    911     ///
    912     /// `rp_id` MUST be the same as the [`PublicKeyCredentialRequestOptions::rp_id`] used when starting the
    913     /// ceremony.
    914     ///
    915     /// It is _essential_ to save [`AuthenticatedCredential::dynamic_state`] overwriting the original value iff `Ok(true)`
    916     /// is returned.
    917     ///
    918     /// # Errors
    919     ///
    920     /// Errors iff `response` is not valid according to the
    921     /// [authentication ceremony](https://www.w3.org/TR/webauthn-3/#sctn-verifying-assertion) or violates any
    922     /// of the settings in `options`.
    923     #[inline]
    924     pub fn verify<
    925         'a,
    926         'user,
    927         O: PartialEq<Origin<'a>>,
    928         T: PartialEq<Origin<'a>>,
    929         EdKey: AsRef<[u8]>,
    930         P256Key: AsRef<[u8]>,
    931         P384Key: AsRef<[u8]>,
    932         RsaKey: AsRef<[u8]>,
    933     >(
    934         self,
    935         rp_id: &RpId,
    936         response: &'a Authentication,
    937         cred: &mut AuthenticatedCredential<
    938             'a,
    939             'user,
    940             CompressedPubKey<EdKey, P256Key, P384Key, RsaKey>,
    941         >,
    942         options: &AuthenticationVerificationOptions<'_, '_, O, T>,
    943     ) -> Result<bool, AuthCeremonyErr> {
    944         // [Authentication ceremony](https://www.w3.org/TR/webauthn-3/#sctn-verifying-assertion)
    945         // is handled by:
    946         //
    947         // 1. Calling code.
    948         // 2. Client code and the construction of `resp` (hopefully via [`Authentication::deserialize`]).
    949         // 3. Client code and the construction of `resp` (hopefully via [`AuthenticatorAssertion::deserialize`]).
    950         // 4. Client code and the construction of `resp` (hopefully via [`ClientExtensionsOutputs::deserialize`]).
    951         // 5. Below.
    952         // 6. Below.
    953         // 7. Informative only in that it defines variables.
    954         // 8. [`Self::partial_validate`].
    955         // 9. [`Self::partial_validate`].
    956         // 10. [`Self::partial_validate`].
    957         // 11. [`Self::partial_validate`].
    958         // 12. [`Self::partial_validate`].
    959         // 13. [`Self::partial_validate`].
    960         // 14. [`Self::partial_validate`].
    961         // 15. [`Self::partial_validate`].
    962         // 16. [`Self::partial_validate`].
    963         // 17. [`Self::partial_validate`].
    964         // 18. [`Self::partial_validate`].
    965         // 19. [`Self::partial_validate`].
    966         // 20. [`Self::partial_validate`].
    967         // 21. [`Self::partial_validate`].
    968         // 22. Below.
    969         // 23. Below.
    970         // 24. Below.
    971         // 25. Below.
    972 
    973         if self.allow_credentials.is_empty() {
    974             // Step 6.
    975             response
    976                 .response
    977                 .user_handle()
    978                 .as_ref()
    979                 .ok_or(AuthCeremonyErr::MissingUserHandle)
    980                 .and_then(|user| {
    981                     if cred.user_id() == user {
    982                         if cred.id == response.raw_id {
    983                             Ok(None)
    984                         } else {
    985                             Err(AuthCeremonyErr::CredentialIdMismatch)
    986                         }
    987                     } else {
    988                         Err(AuthCeremonyErr::UserHandleMismatch)
    989                     }
    990                 })
    991         } else {
    992             // Steps 5–6.
    993             self.verify_nondiscoverable(response, cred)
    994         }
    995         .and_then(|ext| {
    996             // Steps 8–21.
    997             self.partial_validate(
    998                 rp_id,
    999                 response,
   1000                 (&cred.static_state.credential_public_key).into(),
   1001                 &CeremonyOptions {
   1002                     allowed_origins: options.allowed_origins,
   1003                     allowed_top_origins: options.allowed_top_origins,
   1004                     backup_requirement: options
   1005                         .backup_requirement
   1006                         .unwrap_or_else(|| BackupReq::from(cred.dynamic_state.backup)),
   1007                     #[cfg(feature = "serde_relaxed")]
   1008                     client_data_json_relaxed: options.client_data_json_relaxed,
   1009                 },
   1010             )
   1011             .map_err(AuthCeremonyErr::from)
   1012             .and_then(|auth_data| {
   1013                 options
   1014                     .auth_attachment_enforcement
   1015                     .validate(
   1016                         cred.dynamic_state.authenticator_attachment,
   1017                         response.authenticator_attachment,
   1018                     )
   1019                     .and_then(|auth_attachment| {
   1020                         // Step 23.
   1021                         validate_extensions(
   1022                             self.extensions,
   1023                             ext,
   1024                             auth_data.extensions(),
   1025                             options.error_on_unsolicited_extensions,
   1026                             cred.static_state.extensions.hmac_secret.unwrap_or_default(),
   1027                         )
   1028                         .map_err(AuthCeremonyErr::Extension)
   1029                         .and_then(|()| {
   1030                             // Step 22.
   1031                             options
   1032                                 .sig_counter_enforcement
   1033                                 .validate(cred.dynamic_state.sign_count, auth_data.sign_count())
   1034                                 .and_then(|sig_counter| {
   1035                                     let flags = auth_data.flags();
   1036                                     let prev_dyn_state = cred.dynamic_state;
   1037                                     // Step 24 item 2.
   1038                                     cred.dynamic_state.backup = flags.backup;
   1039                                     if options.update_uv && flags.user_verified {
   1040                                         // Step 24 item 3.
   1041                                         cred.dynamic_state.user_verified = true;
   1042                                     }
   1043                                     // Step 24 item 1.
   1044                                     cred.dynamic_state.sign_count = sig_counter;
   1045                                     cred.dynamic_state.authenticator_attachment = auth_attachment;
   1046                                     // Step 25.
   1047                                     crate::verify_static_and_dynamic_state(
   1048                                         &cred.static_state,
   1049                                         cred.dynamic_state,
   1050                                     )
   1051                                     .map_err(|e| {
   1052                                         cred.dynamic_state = prev_dyn_state;
   1053                                         AuthCeremonyErr::Credential(e)
   1054                                     })
   1055                                     .map(|()| prev_dyn_state != cred.dynamic_state)
   1056                                 })
   1057                         })
   1058                     })
   1059             })
   1060         })
   1061     }
   1062     /// Retrieves the corresponding [`CredInfo`] used for a non-discoverable request that corresponds to
   1063     /// `response`. Since this is a non-discoverable request, one must have an external way of identifying the
   1064     /// `UserHandle`.
   1065     ///
   1066     /// This MUST be called iff a non-discoverable request was sent to the client (e.g.,
   1067     /// [`PublicKeyCredentialRequestOptions::second_factor`]).
   1068     ///
   1069     /// # Errors
   1070     ///
   1071     /// Errors iff [`AuthenticatedCredential::user_handle`] does not match [`Authentication::user_handle`] or
   1072     /// [`PublicKeyCredentialRequestOptions::allow_credentials`] does not have a [`CredInfo`] such that
   1073     /// [`CredInfo::id`] matches [`Authentication::raw_id`].
   1074     fn verify_nondiscoverable<'a, PublicKey>(
   1075         &self,
   1076         response: &'a Authentication,
   1077         cred: &AuthenticatedCredential<'a, '_, PublicKey>,
   1078     ) -> Result<Option<ServerCredSpecificExtensionInfo>, AuthCeremonyErr> {
   1079         response
   1080             .response
   1081             .user_handle()
   1082             .as_ref()
   1083             .map_or(Ok(()), |user| {
   1084                 if user == cred.user_id() {
   1085                     Ok(())
   1086                 } else {
   1087                     Err(AuthCeremonyErr::UserHandleMismatch)
   1088                 }
   1089             })
   1090             .and_then(|()| {
   1091                 self.allow_credentials
   1092                     .iter()
   1093                     .find(|c| c.id == response.raw_id)
   1094                     .ok_or(AuthCeremonyErr::NoMatchingAllowedCredential)
   1095                     .map(|c| Some(c.ext))
   1096             })
   1097     }
   1098 }
   1099 impl ServerState for AuthenticationServerState {
   1100     #[cfg(any(doc, not(feature = "serializable_server_state")))]
   1101     #[inline]
   1102     fn expiration(&self) -> Instant {
   1103         self.expiration
   1104     }
   1105     #[cfg(all(not(doc), feature = "serializable_server_state"))]
   1106     #[inline]
   1107     fn expiration(&self) -> SystemTime {
   1108         self.expiration
   1109     }
   1110     #[inline]
   1111     fn sent_challenge(&self) -> SentChallenge {
   1112         self.challenge
   1113     }
   1114 }
   1115 impl Ceremony for AuthenticationServerState {
   1116     type R = Authentication;
   1117     fn rand_challenge(&self) -> SentChallenge {
   1118         self.challenge
   1119     }
   1120     #[cfg(not(feature = "serializable_server_state"))]
   1121     fn expiry(&self) -> Instant {
   1122         self.expiration
   1123     }
   1124     #[cfg(feature = "serializable_server_state")]
   1125     fn expiry(&self) -> SystemTime {
   1126         self.expiration
   1127     }
   1128     fn user_verification(&self) -> UserVerificationRequirement {
   1129         self.user_verification
   1130     }
   1131 }
   1132 impl Borrow<SentChallenge> for AuthenticationServerState {
   1133     #[inline]
   1134     fn borrow(&self) -> &SentChallenge {
   1135         &self.challenge
   1136     }
   1137 }
   1138 impl PartialEq for AuthenticationServerState {
   1139     #[inline]
   1140     fn eq(&self, other: &Self) -> bool {
   1141         self.challenge == other.challenge
   1142     }
   1143 }
   1144 impl PartialEq<&Self> for AuthenticationServerState {
   1145     #[inline]
   1146     fn eq(&self, other: &&Self) -> bool {
   1147         *self == **other
   1148     }
   1149 }
   1150 impl PartialEq<AuthenticationServerState> for &AuthenticationServerState {
   1151     #[inline]
   1152     fn eq(&self, other: &AuthenticationServerState) -> bool {
   1153         **self == *other
   1154     }
   1155 }
   1156 impl Eq for AuthenticationServerState {}
   1157 impl Hash for AuthenticationServerState {
   1158     #[inline]
   1159     fn hash<H: Hasher>(&self, state: &mut H) {
   1160         self.challenge.hash(state);
   1161     }
   1162 }
   1163 impl PartialOrd for AuthenticationServerState {
   1164     #[inline]
   1165     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
   1166         Some(self.cmp(other))
   1167     }
   1168 }
   1169 impl Ord for AuthenticationServerState {
   1170     #[inline]
   1171     fn cmp(&self, other: &Self) -> Ordering {
   1172         self.challenge.cmp(&other.challenge)
   1173     }
   1174 }