webauthn_rp

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

auth.rs (70652B)


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