webauthn_rp

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

register.rs (89541B)


      1 #[cfg(test)]
      2 mod tests;
      3 use super::{
      4     super::{
      5         DynamicState, Metadata, RegisteredCredential, StaticState,
      6         response::{
      7             AuthenticatorAttachment,
      8             register::{
      9                 Attestation, AttestationFormat, AuthenticatorExtensionOutput,
     10                 ClientExtensionsOutputs, CredentialProtectionPolicy, HmacSecret, Registration,
     11                 UncompressedPubKey,
     12                 error::{ExtensionErr, RegCeremonyErr},
     13             },
     14         },
     15     },
     16     Ceremony, Challenge, CredentialMediationRequirement, ExtensionInfo, ExtensionReq, FIVE_MINUTES,
     17     Hints, Origin, PrfInput, PublicKeyCredentialDescriptor, RpId, SentChallenge, TimedCeremony,
     18     UserVerificationRequirement,
     19     register::error::CreationOptionsErr,
     20 };
     21 #[cfg(doc)]
     22 use crate::{
     23     request::{
     24         AsciiDomain, AsciiDomainStatic, DomainOrigin, Url,
     25         auth::{AuthenticationVerificationOptions, PublicKeyCredentialRequestOptions},
     26     },
     27     response::{AuthTransports, AuthenticatorTransport, Backup, CollectedClientData, Flag},
     28 };
     29 use core::{
     30     borrow::Borrow,
     31     cmp::Ordering,
     32     fmt::{self, Display, Formatter},
     33     hash::{Hash, Hasher},
     34     mem,
     35     num::{NonZeroU32, NonZeroU64},
     36     time::Duration,
     37 };
     38 #[cfg(any(doc, not(feature = "serializable_server_state")))]
     39 use std::time::Instant;
     40 #[cfg(any(doc, feature = "serializable_server_state"))]
     41 use std::time::SystemTime;
     42 /// Contains functionality to (de)serialize data to a data store.
     43 #[cfg(feature = "bin")]
     44 pub mod bin;
     45 /// Contains functionality that needs to be accessible when `bin` or `serde` are not enabled.
     46 #[cfg(feature = "custom")]
     47 mod custom;
     48 /// Contains error types.
     49 pub mod error;
     50 /// Contains functionality to (de)serialize data to a client.
     51 #[cfg(feature = "serde")]
     52 pub mod ser;
     53 /// Contains functionality to (de)serialize [`RegistrationServerState`] to a data store.
     54 #[cfg(feature = "serializable_server_state")]
     55 pub mod ser_server_state;
     56 /// Backup requirements for the credential.
     57 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
     58 pub enum BackupReq {
     59     /// No requirements (i.e., any [`Backup`] is allowed).
     60     #[default]
     61     None,
     62     /// Credential must not be eligible for backup.
     63     NotEligible,
     64     /// Credential must be eligible for backup.
     65     ///
     66     /// Note the existence of a backup is ignored. If a backup must exist, then use [`Self::Exists`]; if a
     67     /// backup must not exist, then use [`Self::EligibleNotExists`].
     68     Eligible,
     69     /// Credential must be eligible for backup, but a backup must not exist.
     70     EligibleNotExists,
     71     /// Credential must be backed up.
     72     Exists,
     73 }
     74 /// Used by [`Extension::cred_protect`] to enforce the [`CredentialProtectionPolicy`] sent by the client via
     75 /// [`Registration`].
     76 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
     77 pub enum CredProtect {
     78     /// No `credProtect` request.
     79     #[default]
     80     None,
     81     /// Request
     82     /// [`userVerificationOptional`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#userverificationoptional)
     83     /// but allow any.
     84     ///
     85     /// The `bool` corresponds to
     86     /// [`enforceCredentialProtectionPolicy`](https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#dom-authenticationextensionsclientinputs-enforcecredentialprotectionpolicy).
     87     UserVerificationOptional(bool, ExtensionInfo),
     88     /// Request
     89     /// [`userVerificationOptionalWithCredentialIDList`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#userverificationoptionalwithcredentialidlist);
     90     /// and when enforcing the value, disallow [`CredentialProtectionPolicy::UserVerificationOptional`].
     91     ///
     92     /// The `bool` corresponds to
     93     /// [`enforceCredentialProtectionPolicy`](https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#dom-authenticationextensionsclientinputs-enforcecredentialprotectionpolicy).
     94     UserVerificationOptionalWithCredentialIdList(bool, ExtensionInfo),
     95     /// Request
     96     /// [`userVerificationRequired`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#userverificationrequired);
     97     /// and when enforcing the value, only allow [`CredentialProtectionPolicy::UserVerificationRequired`].
     98     ///
     99     /// The `bool` corresponds to
    100     /// [`enforceCredentialProtectionPolicy`](https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#dom-authenticationextensionsclientinputs-enforcecredentialprotectionpolicy).
    101     UserVerificationRequired(bool, ExtensionInfo),
    102 }
    103 impl CredProtect {
    104     /// Validates `other` is allowed based on `self`.
    105     ///
    106     /// # Errors
    107     ///
    108     /// Errors iff other is a less "secure" policy than `self` when enforcing the value or other does not exist
    109     /// despite requiring a value to be sent back.
    110     ///
    111     /// Note a missing response is OK when enforcing a value.
    112     const fn validate(self, other: CredentialProtectionPolicy) -> Result<(), ExtensionErr> {
    113         match self {
    114             Self::None => Ok(()),
    115             Self::UserVerificationOptional(_, info) => {
    116                 if matches!(other, CredentialProtectionPolicy::None) {
    117                     if matches!(
    118                         info,
    119                         ExtensionInfo::RequireEnforceValue | ExtensionInfo::RequireDontEnforceValue
    120                     ) {
    121                         Err(ExtensionErr::MissingCredProtect)
    122                     } else {
    123                         Ok(())
    124                     }
    125                 } else {
    126                     Ok(())
    127                 }
    128             }
    129             Self::UserVerificationOptionalWithCredentialIdList(_, info) => match info {
    130                 ExtensionInfo::RequireEnforceValue => match other {
    131                     CredentialProtectionPolicy::None => Err(ExtensionErr::MissingCredProtect),
    132                     CredentialProtectionPolicy::UserVerificationOptional => {
    133                         Err(ExtensionErr::InvalidCredProtectValue(self, other))
    134                     }
    135                     CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList
    136                     | CredentialProtectionPolicy::UserVerificationRequired => Ok(()),
    137                 },
    138                 ExtensionInfo::RequireDontEnforceValue => {
    139                     if matches!(other, CredentialProtectionPolicy::None) {
    140                         Err(ExtensionErr::MissingCredProtect)
    141                     } else {
    142                         Ok(())
    143                     }
    144                 }
    145                 ExtensionInfo::AllowEnforceValue => {
    146                     if matches!(other, CredentialProtectionPolicy::UserVerificationOptional) {
    147                         Err(ExtensionErr::InvalidCredProtectValue(self, other))
    148                     } else {
    149                         Ok(())
    150                     }
    151                 }
    152                 ExtensionInfo::AllowDontEnforceValue => Ok(()),
    153             },
    154             Self::UserVerificationRequired(_, info) => match info {
    155                 ExtensionInfo::RequireEnforceValue => match other {
    156                     CredentialProtectionPolicy::None => Err(ExtensionErr::MissingCredProtect),
    157                     CredentialProtectionPolicy::UserVerificationOptional
    158                     | CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList => {
    159                         Err(ExtensionErr::InvalidCredProtectValue(self, other))
    160                     }
    161                     CredentialProtectionPolicy::UserVerificationRequired => Ok(()),
    162                 },
    163                 ExtensionInfo::RequireDontEnforceValue => {
    164                     if matches!(other, CredentialProtectionPolicy::None) {
    165                         Err(ExtensionErr::MissingCredProtect)
    166                     } else {
    167                         Ok(())
    168                     }
    169                 }
    170                 ExtensionInfo::AllowEnforceValue => {
    171                     if matches!(
    172                         other,
    173                         CredentialProtectionPolicy::None
    174                             | CredentialProtectionPolicy::UserVerificationRequired
    175                     ) {
    176                         Ok(())
    177                     } else {
    178                         Err(ExtensionErr::InvalidCredProtectValue(self, other))
    179                     }
    180                 }
    181                 ExtensionInfo::AllowDontEnforceValue => Ok(()),
    182             },
    183         }
    184     }
    185 }
    186 impl Display for CredProtect {
    187     #[inline]
    188     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    189         match *self {
    190             Self::None => f.write_str("do not sent a credProtect request"),
    191             Self::UserVerificationOptional(enforce, info) => {
    192                 write!(
    193                     f,
    194                     "request user verification optional with enforcement {enforce} and {info}"
    195                 )
    196             }
    197             Self::UserVerificationOptionalWithCredentialIdList(enforce, info) => write!(
    198                 f,
    199                 "user verification optional with credential ID list with enforcement {enforce} and {info}"
    200             ),
    201             Self::UserVerificationRequired(enforce, info) => {
    202                 write!(
    203                     f,
    204                     "user verification required with enforcement {enforce} and {info}"
    205                 )
    206             }
    207         }
    208     }
    209 }
    210 /// [`COSEAlgorithmIdentifier`](https://www.w3.org/TR/webauthn-3/#typedefdef-cosealgorithmidentifier).
    211 ///
    212 /// Note the order of variants is the following:
    213 ///
    214 /// [`Self::Mldsa87`] `<` [`Self::Mldsa65`] `<` [`Self::Mldsa44`] `<` [`Self::Eddsa`] `<` [`Self::Es256`]
    215 /// `<` [`Self::Es384`] `<` [`Self::Rs256`].
    216 ///
    217 /// This is relevant for [`CoseAlgorithmIdentifiers`]. For example a `CoseAlgorithmIdentifiers`
    218 /// that contains `Self::Mldsa87` will prioritize it over all others.
    219 #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
    220 pub enum CoseAlgorithmIdentifier {
    221     /// [ML-DSA-87](https://www.iana.org/assignments/cose/cose.xhtml#algorithms).
    222     Mldsa87,
    223     /// [ML-DSA-65](https://www.iana.org/assignments/cose/cose.xhtml#algorithms).
    224     Mldsa65,
    225     /// [ML-DSA-44](https://www.iana.org/assignments/cose/cose.xhtml#algorithms).
    226     Mldsa44,
    227     /// [EdDSA](https://www.iana.org/assignments/cose/cose.xhtml#algorithms).
    228     ///
    229     /// Note that Ed25519 must be used for the `crv` parameter
    230     /// [per the spec](https://www.w3.org/TR/webauthn-3/#sctn-alg-identifier).
    231     Eddsa,
    232     /// [ES256](https://www.iana.org/assignments/cose/cose.xhtml#algorithms).
    233     ///
    234     /// Note the uncompressed form must be used and P-256 must be used for the `crv` parameter
    235     /// [per the spec](https://www.w3.org/TR/webauthn-3/#sctn-alg-identifier).
    236     Es256,
    237     /// [ES384](https://www.iana.org/assignments/cose/cose.xhtml#algorithms).
    238     ///
    239     /// Note the uncompressed form must be used and P-384 must be used for the `crv` parameter
    240     /// [per the spec](https://www.w3.org/TR/webauthn-3/#sctn-alg-identifier).
    241     Es384,
    242     /// [RS256](https://www.iana.org/assignments/cose/cose.xhtml#algorithms).
    243     Rs256,
    244 }
    245 impl CoseAlgorithmIdentifier {
    246     /// Transforms `self` into a `u8`.
    247     const fn to_u8(self) -> u8 {
    248         match self {
    249             Self::Mldsa87 => 0x1,
    250             Self::Mldsa65 => 0x2,
    251             Self::Mldsa44 => 0x4,
    252             Self::Eddsa => 0x8,
    253             Self::Es256 => 0x10,
    254             Self::Es384 => 0x20,
    255             Self::Rs256 => 0x40,
    256         }
    257     }
    258 }
    259 impl PartialEq<&Self> for CoseAlgorithmIdentifier {
    260     #[inline]
    261     fn eq(&self, other: &&Self) -> bool {
    262         *self == **other
    263     }
    264 }
    265 impl PartialEq<CoseAlgorithmIdentifier> for &CoseAlgorithmIdentifier {
    266     #[inline]
    267     fn eq(&self, other: &CoseAlgorithmIdentifier) -> bool {
    268         **self == *other
    269     }
    270 }
    271 /// Non-empty ordered set of [`CoseAlgorithmIdentifier`]s.
    272 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    273 pub struct CoseAlgorithmIdentifiers(u8);
    274 impl CoseAlgorithmIdentifiers {
    275     /// Contains all [`CoseAlgorithmIdentifier`]s.
    276     pub const ALL: Self = Self(0)
    277         .add(CoseAlgorithmIdentifier::Mldsa87)
    278         .add(CoseAlgorithmIdentifier::Mldsa65)
    279         .add(CoseAlgorithmIdentifier::Mldsa44)
    280         .add(CoseAlgorithmIdentifier::Eddsa)
    281         .add(CoseAlgorithmIdentifier::Es256)
    282         .add(CoseAlgorithmIdentifier::Es384)
    283         .add(CoseAlgorithmIdentifier::Rs256);
    284     /// Returns a `CoseAlgorithmIdentifiers` containing all `CoseAlgorithmIdentifier`s in `self` plus `alg`.
    285     const fn add(self, alg: CoseAlgorithmIdentifier) -> Self {
    286         Self(self.0 | alg.to_u8())
    287     }
    288     /// Returns a copy of `self` with `alg` removed if there would be at least one `CoseAlgorithmIdentifier`
    289     /// remaining; otherwise returns `self`.
    290     #[inline]
    291     #[must_use]
    292     pub const fn remove(self, alg: CoseAlgorithmIdentifier) -> Self {
    293         let val = alg.to_u8();
    294         if self.0 == val {
    295             self
    296         } else {
    297             Self(self.0 & !val)
    298         }
    299     }
    300     /// Returns `true` iff `self` contains `alg`.
    301     #[inline]
    302     #[must_use]
    303     pub const fn contains(self, alg: CoseAlgorithmIdentifier) -> bool {
    304         let val = alg.to_u8();
    305         self.0 & val == val
    306     }
    307     /// Validates `other` is allowed based on `self`.
    308     const fn validate(self, other: UncompressedPubKey<'_>) -> Result<(), RegCeremonyErr> {
    309         if match other {
    310             UncompressedPubKey::MlDsa87(_) => self.contains(CoseAlgorithmIdentifier::Mldsa87),
    311             UncompressedPubKey::MlDsa65(_) => self.contains(CoseAlgorithmIdentifier::Mldsa65),
    312             UncompressedPubKey::MlDsa44(_) => self.contains(CoseAlgorithmIdentifier::Mldsa44),
    313             UncompressedPubKey::Ed25519(_) => self.contains(CoseAlgorithmIdentifier::Eddsa),
    314             UncompressedPubKey::P256(_) => self.contains(CoseAlgorithmIdentifier::Es256),
    315             UncompressedPubKey::P384(_) => self.contains(CoseAlgorithmIdentifier::Es384),
    316             UncompressedPubKey::Rsa(_) => self.contains(CoseAlgorithmIdentifier::Rs256),
    317         } {
    318             Ok(())
    319         } else {
    320             Err(RegCeremonyErr::PublicKeyAlgorithmMismatch)
    321         }
    322     }
    323 }
    324 impl Default for CoseAlgorithmIdentifiers {
    325     /// Returns [`Self::ALL`].
    326     #[inline]
    327     fn default() -> Self {
    328         Self::ALL
    329     }
    330 }
    331 /// Four to sixty-three inclusively.
    332 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    333 #[repr(u8)]
    334 pub enum FourToSixtyThree {
    335     /// 4.
    336     Four = 4,
    337     /// 5.
    338     Five,
    339     /// 6.
    340     Six,
    341     /// 7.
    342     Seven,
    343     /// 8.
    344     Eight,
    345     /// 9.
    346     Nine,
    347     /// 10.
    348     Ten,
    349     /// 11.
    350     Eleven,
    351     /// 12.
    352     Twelve,
    353     /// 13.
    354     Thirteen,
    355     /// 14.
    356     Fourteen,
    357     /// 15.
    358     Fifteen,
    359     /// 16.
    360     Sixteen,
    361     /// 17.
    362     Seventeen,
    363     /// 18.
    364     Eighteen,
    365     /// 19.
    366     Nineteen,
    367     /// 20.
    368     Twenty,
    369     /// 21.
    370     TwentyOne,
    371     /// 22.
    372     TwentyTwo,
    373     /// 23.
    374     TwentyThree,
    375     /// 24.
    376     TwentyFour,
    377     /// 25.
    378     TwentyFive,
    379     /// 26.
    380     TwentySix,
    381     /// 27.
    382     TwentySeven,
    383     /// 28.
    384     TwentyEight,
    385     /// 29.
    386     TwentyNine,
    387     /// 30.
    388     Thirty,
    389     /// 31.
    390     ThirtyOne,
    391     /// 32.
    392     ThirtyTwo,
    393     /// 33.
    394     ThirtyThree,
    395     /// 34.
    396     ThirtyFour,
    397     /// 35.
    398     ThirtyFive,
    399     /// 36.
    400     ThirtySix,
    401     /// 37.
    402     ThirtySeven,
    403     /// 38.
    404     ThirtyEight,
    405     /// 39.
    406     ThirtyNine,
    407     /// 40.
    408     Fourty,
    409     /// 41.
    410     FourtyOne,
    411     /// 42.
    412     FourtyTwo,
    413     /// 43.
    414     FourtyThree,
    415     /// 44.
    416     FourtyFour,
    417     /// 45.
    418     FourtyFive,
    419     /// 46.
    420     FourtySix,
    421     /// 47.
    422     FourtySeven,
    423     /// 48.
    424     FourtyEight,
    425     /// 49.
    426     FourtyNine,
    427     /// 50.
    428     Fifty,
    429     /// 51.
    430     FiftyOne,
    431     /// 52.
    432     FiftyTwo,
    433     /// 53.
    434     FiftyThree,
    435     /// 54.
    436     FiftyFour,
    437     /// 55.
    438     FiftyFive,
    439     /// 56.
    440     FiftySix,
    441     /// 57.
    442     FiftySeven,
    443     /// 58.
    444     FiftyEight,
    445     /// 59.
    446     FiftyNine,
    447     /// 60.
    448     Sixty,
    449     /// 61.
    450     SixtyOne,
    451     /// 62.
    452     SixtyTwo,
    453     /// 63.
    454     SixtyThree,
    455 }
    456 impl FourToSixtyThree {
    457     /// Returns the equivalent `u8`.
    458     #[expect(clippy::as_conversions, reason = "comment justifies correctness")]
    459     #[inline]
    460     #[must_use]
    461     pub const fn into_u8(self) -> u8 {
    462         // This is correct since `Self` is `repr(u8)`, and the initial discriminant has the value `4`
    463         // and subsequent discriminants are implicitly incremented by 1.
    464         self as u8
    465     }
    466     /// Returns `Some` representing `val` iff `val` is inclusively between 4 and 63.
    467     #[expect(unsafe_code, reason = "comment justifies correctness")]
    468     #[inline]
    469     #[must_use]
    470     pub const fn from_u8(val: u8) -> Option<Self> {
    471         match val {
    472             0..=3 | 64.. => None,
    473             _ => {
    474                 // SAFETY:
    475                 // `val` is inclusively between 4 and 63, and `Self` is `repr(u8)`; thus this
    476                 // is safe and correct.
    477                 Some(unsafe { mem::transmute::<u8, Self>(val) })
    478             }
    479         }
    480     }
    481 }
    482 impl Display for FourToSixtyThree {
    483     #[inline]
    484     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    485         self.into_u8().fmt(f)
    486     }
    487 }
    488 impl From<FourToSixtyThree> for u8 {
    489     #[inline]
    490     fn from(value: FourToSixtyThree) -> Self {
    491         value.into_u8()
    492     }
    493 }
    494 impl Default for FourToSixtyThree {
    495     #[inline]
    496     fn default() -> Self {
    497         Self::Four
    498     }
    499 }
    500 /// The [defined extensions](https://www.w3.org/TR/webauthn-3/#sctn-defined-extensions) to send to the client.
    501 #[derive(Clone, Copy, Debug)]
    502 pub struct Extension<'prf_first, 'prf_second> {
    503     /// [`credProps`](https://www.w3.org/TR/webauthn-3/#sctn-authenticator-credential-properties-extension).
    504     ///
    505     /// The best one can do to ensure a server-side credential is created is by sending
    506     /// [`ResidentKeyRequirement::Discouraged`]; however authenticators are still allowed
    507     /// to create a client-side credential. To more definitively check that a server-side credential is
    508     /// created, send this extension. Note that it can be difficult to impossible for a client/user agent to
    509     /// know that a server-side credential is created; thus even when the response is
    510     /// `Some(CredentialPropertiesOutput { rk: Some(false) })`, a client-side/"resident" credential could still
    511     /// have been created. One may have better luck checking if [`AuthTransports::contains`]
    512     /// [`AuthenticatorTransport::Internal`] and using that as an indicator if a client-side credential was created.
    513     ///
    514     /// In the event [`ClientExtensionsOutputs::cred_props`] is `Some(CredentialPropertiesOutput { rk: Some(false) })`
    515     /// and [`ResidentKeyRequirement::Required`] was sent, an error will happen regardless of this value.
    516     pub cred_props: Option<ExtensionReq>,
    517     /// [`credProtect`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-credProtect-extension).
    518     pub cred_protect: CredProtect,
    519     /// [`minPinLength`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-minpinlength-extension).
    520     ///
    521     /// When the value is enforced, that corresponds to
    522     /// [`minPinLength`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-minpinlength-extension)
    523     /// in [`extensions`](https://www.w3.org/TR/webauthn-3/#authdata-extensions) set to a value at least as large
    524     /// as the contained `FourToSixtyThree`.
    525     pub min_pin_length: Option<(FourToSixtyThree, ExtensionInfo)>,
    526     /// [`prf`](https://www.w3.org/TR/webauthn-3/#prf-extension).
    527     ///
    528     /// When the value is enforced, that corresponds to
    529     /// [`enabled`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-enabled) set to `true`.
    530     /// In contrast [`results`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-results)
    531     /// must not exist, be `null`, or be an
    532     /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues)
    533     /// such that [`first`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-first) is `null`
    534     /// and [`second`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-second) does not
    535     /// exist or is `null`. This is to ensure the decrypted outputs stay on the client.
    536     ///
    537     /// Note some authenticators can only enable `prf` during registration (e.g., CTAP authenticators that only
    538     /// support
    539     /// [`hmac-secret`](https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#sctn-hmac-secret-extension)
    540     /// and not
    541     /// [`hmac-secret-mc`](https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#sctn-hmac-secret-make-cred-extension));
    542     /// thus the value of `PrfInput` is ignored and only used as a signal to enable `prf`. For many such
    543     /// authenticators, not using this extension during registration will not preclude them from being used during
    544     /// authentication; however it is still encouraged to use the extension during registration since some
    545     /// authenticators actually require it.
    546     ///
    547     /// When the underlying credential is expected to be used during discoverable requests, it is likely that
    548     /// `'prf_first` will be `'static` and [`PrfInput::second`] is `None` since one will not be able to
    549     /// realistically rotate the underlying inputs and further the same input will likely be used for all credentials.
    550     /// For credentials intended to be used during non-discoverable requests, however, one is encouraged to rotate
    551     /// the inputs and have unique values for each credential.
    552     pub prf: Option<(PrfInput<'prf_first, 'prf_second>, ExtensionInfo)>,
    553 }
    554 impl<'prf_first, 'prf_second> Extension<'prf_first, 'prf_second> {
    555     /// Returns an empty `Extension`.
    556     #[inline]
    557     #[must_use]
    558     pub const fn none() -> Self {
    559         Self {
    560             cred_props: None,
    561             cred_protect: CredProtect::None,
    562             min_pin_length: None,
    563             prf: None,
    564         }
    565     }
    566     /// Same as [`Self::none`] except [`Self::cred_props`] is `Some` containing `req`.
    567     #[inline]
    568     #[must_use]
    569     pub const fn with_cred_props(req: ExtensionReq) -> Self {
    570         Self {
    571             cred_props: Some(req),
    572             ..Self::none()
    573         }
    574     }
    575     /// Same as [`Self::none`] except [`Self::cred_protect`] is `cred_protect`.
    576     #[inline]
    577     #[must_use]
    578     pub const fn with_cred_protect(cred_protect: CredProtect) -> Self {
    579         Self {
    580             cred_protect,
    581             ..Self::none()
    582         }
    583     }
    584     /// Same as [`Self::none`] except [`Self::min_pin_length`] is `Some` containing `min_len` and `info`.
    585     #[inline]
    586     #[must_use]
    587     pub const fn with_min_pin_length(min_len: FourToSixtyThree, info: ExtensionInfo) -> Self {
    588         Self {
    589             min_pin_length: Some((min_len, info)),
    590             ..Self::none()
    591         }
    592     }
    593     /// Same as [`Self::none`] except [`Self::prf`] is `Some` containing `input` and `info`.
    594     #[expect(single_use_lifetimes, reason = "false positive")]
    595     #[inline]
    596     #[must_use]
    597     pub const fn with_prf<'a: 'prf_first, 'b: 'prf_second>(
    598         input: PrfInput<'a, 'b>,
    599         info: ExtensionInfo,
    600     ) -> Self {
    601         Self {
    602             prf: Some((input, info)),
    603             ..Self::none()
    604         }
    605     }
    606 }
    607 impl Default for Extension<'_, '_> {
    608     /// Same as [`Self::none`].
    609     #[inline]
    610     fn default() -> Self {
    611         Self::none()
    612     }
    613 }
    614 #[cfg(test)]
    615 impl PartialEq for Extension<'_, '_> {
    616     #[inline]
    617     fn eq(&self, other: &Self) -> bool {
    618         self.cred_props == other.cred_props
    619             && self.cred_protect == other.cred_protect
    620             && self.min_pin_length == other.min_pin_length
    621             && self.prf == other.prf
    622     }
    623 }
    624 /// The maximum number of bytes a [`UserHandle`] can be made of per
    625 /// [WebAuthn](https://www.w3.org/TR/webauthn-3/#user-handle).
    626 pub const USER_HANDLE_MAX_LEN: usize = 64;
    627 /// The minimum number of bytes a [`UserHandle`] can be made of per
    628 /// [WebAuthn](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentity-id).
    629 pub const USER_HANDLE_MIN_LEN: usize = 1;
    630 /// A [user handle](https://www.w3.org/TR/webauthn-3/#user-handle) that is made up of
    631 /// [`USER_HANDLE_MIN_LEN`]–[`USER_HANDLE_MAX_LEN`] bytes.
    632 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    633 pub struct UserHandle<const LEN: usize>([u8; LEN]);
    634 impl<const LEN: usize> UserHandle<LEN> {
    635     /// Returns the contained data as a `slice`.
    636     #[inline]
    637     #[must_use]
    638     pub const fn as_slice(&self) -> &[u8] {
    639         self.0.as_slice()
    640     }
    641     /// Returns the contained data as a shared reference to the array.
    642     #[inline]
    643     #[must_use]
    644     pub const fn as_array(&self) -> &[u8; LEN] {
    645         &self.0
    646     }
    647     /// Returns the contained data.
    648     #[inline]
    649     #[must_use]
    650     pub const fn into_array(self) -> [u8; LEN] {
    651         self.0
    652     }
    653 }
    654 /// Implements [`Default`] for [`UserHandle`] of array of length of the passed `usize` literal.
    655 ///
    656 /// Only [`USER_HANDLE_MIN_LEN`]–[`USER_HANDLE_MAX_LEN`] inclusively are allowed to be passed.
    657 macro_rules! user {
    658     ( $( $x:literal),* ) => {
    659         $(
    660 impl Default for UserHandle<$x> {
    661     #[inline]
    662     fn default() -> Self {
    663         let mut data = [0; $x];
    664         rand::fill(data.as_mut_slice());
    665         Self(data)
    666     }
    667 }
    668         )*
    669     };
    670 }
    671 // MUST only pass [`USER_HANDLE_MIN_LEN`]–[`USER_HANDLE_MAX_LEN`] inclusively.
    672 user!(
    673     1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
    674     27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
    675     51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
    676 );
    677 impl<const LEN: usize> UserHandle<LEN>
    678 where
    679     Self: Default,
    680 {
    681     /// Returns a new `UserHandle` based on `LEN` randomly-generated [`u8`]s.
    682     ///
    683     /// # Examples
    684     ///
    685     /// ```
    686     /// # use webauthn_rp::request::register::{UserHandle, UserHandle64, USER_HANDLE_MIN_LEN, USER_HANDLE_MAX_LEN};
    687     /// assert_eq!(
    688     ///     UserHandle::<USER_HANDLE_MIN_LEN>::new()
    689     ///         .as_ref()
    690     ///         .len(),
    691     ///     1
    692     /// );
    693     /// // The probability of an all-zero `UserHandle` being generated (assuming a good entropy
    694     /// // source) is 2^-512 ≈ 7.5 x 10^-155.
    695     /// assert_ne!(
    696     ///     UserHandle64::new().as_ref(),
    697     ///     [0; USER_HANDLE_MAX_LEN]
    698     /// );
    699     /// ```
    700     #[inline]
    701     #[must_use]
    702     pub fn new() -> Self {
    703         Self::default()
    704     }
    705 }
    706 impl<const LEN: usize> AsRef<[u8]> for UserHandle<LEN> {
    707     #[inline]
    708     fn as_ref(&self) -> &[u8] {
    709         self.as_slice()
    710     }
    711 }
    712 impl<const LEN: usize> Borrow<[u8]> for UserHandle<LEN> {
    713     #[inline]
    714     fn borrow(&self) -> &[u8] {
    715         self.as_slice()
    716     }
    717 }
    718 impl<const LEN: usize> PartialEq<&Self> for UserHandle<LEN> {
    719     #[inline]
    720     fn eq(&self, other: &&Self) -> bool {
    721         *self == **other
    722     }
    723 }
    724 impl<const LEN: usize> PartialEq<UserHandle<LEN>> for &UserHandle<LEN> {
    725     #[inline]
    726     fn eq(&self, other: &UserHandle<LEN>) -> bool {
    727         **self == *other
    728     }
    729 }
    730 impl<const LEN: usize> From<UserHandle<LEN>> for [u8; LEN] {
    731     #[inline]
    732     fn from(value: UserHandle<LEN>) -> Self {
    733         value.into_array()
    734     }
    735 }
    736 impl<'a: 'b, 'b, const LEN: usize> From<&'a UserHandle<LEN>> for &'b [u8; LEN] {
    737     #[inline]
    738     fn from(value: &'a UserHandle<LEN>) -> Self {
    739         value.as_array()
    740     }
    741 }
    742 /// `UserHandle` that is based on the [spec recommendation](https://www.w3.org/TR/webauthn-3/#user-handle).
    743 pub type UserHandle64 = UserHandle<USER_HANDLE_MAX_LEN>;
    744 /// `UserHandle` that is based on 16 bytes.
    745 ///
    746 /// While not the recommended size like [`UserHandle64`], 16 bytes is common for many deployments since
    747 /// it's the same size as [Universally Unique IDentifiers (UUIDs)](https://www.rfc-editor.org/rfc/rfc9562).
    748 pub type UserHandle16 = UserHandle<16>;
    749 impl UserHandle16 {
    750     /// Same as [`Self::new`] except 6 bits of metadata is encoded to conform with
    751     /// [UUID Version 4](https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-4).
    752     ///
    753     /// # Examples
    754     ///
    755     /// ```
    756     /// # use webauthn_rp::request::register::UserHandle16;
    757     /// assert!(UserHandle16::new_uuid_v4().is_uuid_v4());
    758     /// ```
    759     #[inline]
    760     #[must_use]
    761     pub fn new_uuid_v4() -> Self {
    762         let mut this = Self::new();
    763         // The first 4 bits of the 6th octet (0-based index) represents the UUID version (i.e., 4).
    764         // We first 0-out the version bits retaining the other 4 bits, then set the version bits to 4.
    765         this.0[6] = (this.0[6] & 0x0F) | 0x40;
    766         // The first 2 bits of the 8th octet (0-based index) represents the UUID variant (i.e., 8,9,A,B)
    767         // which is defined to be 2.
    768         // We first 0-out the variant bits retaining the other 6 bits, then set the variant bits to 2.
    769         this.0[8] = (this.0[8] & 0x3F) | 0x80;
    770         this
    771     }
    772     /// Returns `true` iff `self` is a valid
    773     /// [UUID Version 4](https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-4).
    774     ///
    775     /// # Examples
    776     ///
    777     /// ```
    778     /// # use webauthn_rp::request::register::UserHandle16;
    779     /// assert!(UserHandle16::new_uuid_v4().is_uuid_v4());
    780     /// let mut user = UserHandle16::new_uuid_v4().into_array();
    781     /// user[6] = 255;
    782     /// # #[cfg(feature = "custom")]
    783     /// assert!(!UserHandle16::from(user).is_uuid_v4());
    784     /// ```
    785     #[inline]
    786     #[must_use]
    787     pub const fn is_uuid_v4(&self) -> bool {
    788         // The first 4 bits of the 6th octet (0-based index) represents the UUID version (i.e., 4).
    789         // The first 2 bits of the 8th octet (0-based index) represents the UUID variant (i.e., 8,9,A,B) which
    790         // is defined to be 2.
    791         self.0[6] >> 4 == 0x4 && self.0[8] >> 6 == 0x2
    792     }
    793     /// Returns `Some` containing `uuid_v4` iff `uuid_v4` is a valid
    794     /// [UUID Version 4](https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-4).
    795     ///
    796     /// # Examples
    797     ///
    798     /// ```
    799     /// # use webauthn_rp::request::register::UserHandle16;
    800     /// let mut user = UserHandle16::new_uuid_v4().into_array();
    801     /// assert!(UserHandle16::from_uuid_v4(user).is_some());
    802     /// user[8] = 255;
    803     /// assert!(UserHandle16::from_uuid_v4(user).is_none());
    804     /// ```
    805     #[cfg(feature = "custom")]
    806     #[inline]
    807     #[must_use]
    808     pub fn from_uuid_v4(uuid_v4: [u8; 16]) -> Option<Self> {
    809         let this = Self(uuid_v4);
    810         this.is_uuid_v4().then_some(this)
    811     }
    812 }
    813 /// [The `PublicKeyCredentialUserEntity`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialuserentity)
    814 /// sent to the client.
    815 #[derive(Clone, Debug)]
    816 pub struct PublicKeyCredentialUserEntity<'name, 'display_name, 'id, const LEN: usize> {
    817     /// [`name`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialentity-name).
    818     ///
    819     /// Note the spec recommends RPs enforce
    820     /// [RFC 8265 § 3.4.3](https://www.rfc-editor.org/rfc/rfc8265#section-3.4.3).
    821     pub name: &'name str,
    822     /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentity-id).
    823     pub id: &'id UserHandle<LEN>,
    824     /// [`displayName`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentity-displayname).
    825     ///
    826     /// Note the spec recommends RPs enforce [RFC 8266 § 2.3](https://www.rfc-editor.org/rfc/rfc8266#section-2.3)
    827     /// when this isn't empty.
    828     pub display_name: &'display_name str,
    829 }
    830 /// `PublicKeyCredentialUserEntity` based on a [`UserHandle64`].
    831 pub type PublicKeyCredentialUserEntity64<'name, 'display_name, 'id> =
    832     PublicKeyCredentialUserEntity<'name, 'display_name, 'id, USER_HANDLE_MAX_LEN>;
    833 /// `PublicKeyCredentialUserEntity` based on a [`UserHandle16`].
    834 pub type PublicKeyCredentialUserEntity16<'name, 'display_name, 'id> =
    835     PublicKeyCredentialUserEntity<'name, 'display_name, 'id, 16>;
    836 /// [`ResidentKeyRequirement`](https://www.w3.org/TR/webauthn-3/#enumdef-residentkeyrequirement) sent to the client.
    837 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    838 pub enum ResidentKeyRequirement {
    839     /// [`required`](https://www.w3.org/TR/webauthn-3/#dom-residentkeyrequirement-required).
    840     Required,
    841     /// [`discouraged`](https://www.w3.org/TR/webauthn-3/#dom-residentkeyrequirement-discouraged).
    842     Discouraged,
    843     /// [`preferred`](https://www.w3.org/TR/webauthn-3/#dom-residentkeyrequirement-preferred).
    844     Preferred,
    845 }
    846 impl AuthenticatorAttachment {
    847     /// Validates `self` against `other` based on `require_response`.
    848     const fn validate(self, require_response: bool, other: Self) -> Result<(), RegCeremonyErr> {
    849         match self {
    850             Self::None => {
    851                 if require_response && matches!(other, Self::None) {
    852                     Err(RegCeremonyErr::MissingAuthenticatorAttachment)
    853                 } else {
    854                     Ok(())
    855                 }
    856             }
    857             Self::Platform => match other {
    858                 Self::None => {
    859                     if require_response {
    860                         Err(RegCeremonyErr::MissingAuthenticatorAttachment)
    861                     } else {
    862                         Ok(())
    863                     }
    864                 }
    865                 Self::Platform => Ok(()),
    866                 Self::CrossPlatform => Err(RegCeremonyErr::AuthenticatorAttachmentMismatch),
    867             },
    868             Self::CrossPlatform => match other {
    869                 Self::None => {
    870                     if require_response {
    871                         Err(RegCeremonyErr::MissingAuthenticatorAttachment)
    872                     } else {
    873                         Ok(())
    874                     }
    875                 }
    876                 Self::CrossPlatform => Ok(()),
    877                 Self::Platform => Err(RegCeremonyErr::AuthenticatorAttachmentMismatch),
    878             },
    879         }
    880     }
    881 }
    882 /// [`AuthenticatorSelectionCriteria`](https://www.w3.org/TR/webauthn-3/#dictionary-authenticatorSelection).
    883 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    884 pub struct AuthenticatorSelectionCriteria {
    885     /// [`authenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-authenticatorattachment).
    886     pub authenticator_attachment: AuthenticatorAttachment,
    887     /// [`residentKey`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-residentkey).
    888     pub resident_key: ResidentKeyRequirement,
    889     /// [`userVerification`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-userverification).
    890     pub user_verification: UserVerificationRequirement,
    891 }
    892 impl AuthenticatorSelectionCriteria {
    893     /// Returns an `AuthenticatorSelectionCriteria` useful for passkeys (i.e., [`Self::resident_key`] is set to
    894     /// [`ResidentKeyRequirement::Required`] and [`Self::user_verification`] is set to
    895     /// [`UserVerificationRequirement::Required`]).
    896     ///
    897     /// # Examples
    898     ///
    899     /// ```
    900     /// # use webauthn_rp::{request::{
    901     /// #     register::{
    902     /// #         AuthenticatorSelectionCriteria, ResidentKeyRequirement,
    903     /// #     },
    904     /// #     UserVerificationRequirement,
    905     /// # }, response::AuthenticatorAttachment};
    906     /// let crit = AuthenticatorSelectionCriteria::passkey();
    907     /// assert_eq!(crit.authenticator_attachment, AuthenticatorAttachment::None);
    908     /// assert_eq!(crit.resident_key, ResidentKeyRequirement::Required);
    909     /// assert_eq!(crit.user_verification, UserVerificationRequirement::Required);
    910     /// ```
    911     #[inline]
    912     #[must_use]
    913     pub fn passkey() -> Self {
    914         Self {
    915             authenticator_attachment: AuthenticatorAttachment::default(),
    916             resident_key: ResidentKeyRequirement::Required,
    917             user_verification: UserVerificationRequirement::Required,
    918         }
    919     }
    920     /// Returns an `AuthenticatorSelectionCriteria` useful for second-factor flows (i.e., [`Self::resident_key`]
    921     /// is set to [`ResidentKeyRequirement::Discouraged`] and [`Self::user_verification`] is set to
    922     /// [`UserVerificationRequirement::Discouraged`]).
    923     ///
    924     /// Note some authenticators require user verification during credential registration (e.g.,
    925     /// [CTAP 2.0 authenticators](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#authenticatorMakeCredential)).
    926     /// When an authenticator supports both CTAP 2.0 and
    927     /// [Universal 2nd Factor (U2F)](https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-overview-v1.2-ps-20170411.html#registration-creating-a-key-pair)
    928     /// protocols, user agents will sometimes fall back to U2F when `UserVerificationRequirement::Discouraged`
    929     /// is requested since the latter allows for registration without user verification. If the user agent does not
    930     /// do this, then users will have an inconsistent experience when authenticating an already-registered
    931     /// credential. If this is undesirable, one can use [`UserVerificationRequirement::Required`] for this and
    932     /// [`PublicKeyCredentialRequestOptions::user_verification`] at the expense of requiring a user to verify
    933     /// themselves twice: once for the first factor and again here.
    934     ///
    935     /// # Examples
    936     ///
    937     /// ```
    938     /// # use webauthn_rp::{request::{
    939     /// #     register::{
    940     /// #         AuthenticatorSelectionCriteria, ResidentKeyRequirement,
    941     /// #     },
    942     /// #     UserVerificationRequirement,
    943     /// # }, response::AuthenticatorAttachment};
    944     /// let crit = AuthenticatorSelectionCriteria::second_factor();
    945     /// assert_eq!(crit.authenticator_attachment, AuthenticatorAttachment::None);
    946     /// assert_eq!(crit.resident_key, ResidentKeyRequirement::Discouraged);
    947     /// assert_eq!(crit.user_verification, UserVerificationRequirement::Discouraged);
    948     /// ```
    949     #[inline]
    950     #[must_use]
    951     pub fn second_factor() -> Self {
    952         Self {
    953             authenticator_attachment: AuthenticatorAttachment::default(),
    954             resident_key: ResidentKeyRequirement::Discouraged,
    955             user_verification: UserVerificationRequirement::Discouraged,
    956         }
    957     }
    958     /// Ensures a client-side credential was created when applicable. Also enforces `auth_attachment` when
    959     /// applicable.
    960     const fn validate(
    961         self,
    962         require_auth_attachment: bool,
    963         auth_attachment: AuthenticatorAttachment,
    964     ) -> Result<(), RegCeremonyErr> {
    965         self.authenticator_attachment
    966             .validate(require_auth_attachment, auth_attachment)
    967     }
    968 }
    969 /// Helper that verifies the overlap of [`CredentialCreationOptions::start_ceremony`] and
    970 /// [`RegistrationServerState::decode`].
    971 const fn validate_options_helper(
    972     auth_crit: AuthenticatorSelectionCriteria,
    973     extensions: ServerExtensionInfo,
    974 ) -> Result<(), CreationOptionsErr> {
    975     if matches!(
    976         auth_crit.user_verification,
    977         UserVerificationRequirement::Required
    978     ) {
    979         Ok(())
    980     } else if !matches!(extensions.prf, ServerPrfInfo::None) {
    981         Err(CreationOptionsErr::PrfWithoutUserVerification)
    982     } else if matches!(
    983         extensions.cred_protect,
    984         CredProtect::UserVerificationRequired(_, _)
    985     ) {
    986         Err(CreationOptionsErr::CredProtectRequiredWithoutUserVerification)
    987     } else {
    988         Ok(())
    989     }
    990 }
    991 /// The [`CredentialCreationOptions`](https://www.w3.org/TR/credential-management-1/#dictdef-credentialcreationoptions)
    992 /// to send to the client when registering a new credential.
    993 ///
    994 /// Upon saving the [`RegistrationServerState`] returned from [`Self::start_ceremony`], one MUST send
    995 /// [`RegistrationClientState`] to the client ASAP. After receiving the newly created [`Registration`], it is
    996 /// validated using [`RegistrationServerState::verify`].
    997 #[derive(Debug)]
    998 pub struct CredentialCreationOptions<
    999     'rp_id,
   1000     'user_name,
   1001     'user_display_name,
   1002     'user_id,
   1003     'prf_first,
   1004     'prf_second,
   1005     const USER_LEN: usize,
   1006 > {
   1007     /// [`mediation`](https://www.w3.org/TR/credential-management-1/#dom-credentialcreationoptions-mediation).
   1008     ///
   1009     /// Note if this is [`CredentialMediationRequirement::Conditional`], one may want to ensure
   1010     /// [`AuthenticatorSelectionCriteria::user_verification`] is not [`UserVerificationRequirement::Required`]
   1011     /// since some authenticators cannot enforce user verification during registration ceremonies when conditional
   1012     /// mediation is used. Do note that in the event passkeys are to be created, one may want to set
   1013     /// [`AuthenticationVerificationOptions::update_uv`] to `true` since [`Flag::user_verified`] will
   1014     /// potentially be `false`.
   1015     pub mediation: CredentialMediationRequirement,
   1016     /// `public-key` [credential type](https://www.w3.org/TR/credential-management-1/#sctn-cred-type-registry).
   1017     pub public_key: PublicKeyCredentialCreationOptions<
   1018         'rp_id,
   1019         'user_name,
   1020         'user_display_name,
   1021         'user_id,
   1022         'prf_first,
   1023         'prf_second,
   1024         USER_LEN,
   1025     >,
   1026 }
   1027 impl<
   1028     'rp_id,
   1029     'user_name,
   1030     'user_display_name,
   1031     'user_id,
   1032     'prf_first,
   1033     'prf_second,
   1034     const USER_LEN: usize,
   1035 >
   1036     CredentialCreationOptions<
   1037         'rp_id,
   1038         'user_name,
   1039         'user_display_name,
   1040         'user_id,
   1041         'prf_first,
   1042         'prf_second,
   1043         USER_LEN,
   1044     >
   1045 {
   1046     /// Sets [`Self::mediation`] to [`CredentialMediationRequirement::default`] and
   1047     /// [`Self::public_key`] to [`PublicKeyCredentialCreationOptions::passkey`].
   1048     #[expect(single_use_lifetimes, reason = "false positive")]
   1049     #[inline]
   1050     #[must_use]
   1051     pub fn passkey<'a: 'rp_id, 'b: 'user_name, 'c: 'user_display_name, 'd: 'user_id>(
   1052         rp_id: &'a RpId,
   1053         user: PublicKeyCredentialUserEntity<'b, 'c, 'd, USER_LEN>,
   1054         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   1055     ) -> Self {
   1056         Self {
   1057             mediation: CredentialMediationRequirement::default(),
   1058             public_key: PublicKeyCredentialCreationOptions::passkey(
   1059                 rp_id,
   1060                 user,
   1061                 exclude_credentials,
   1062             ),
   1063         }
   1064     }
   1065     /// Sets [`Self::mediation`] to [`CredentialMediationRequirement::default`] and
   1066     /// [`Self::public_key`] to [`PublicKeyCredentialCreationOptions::second_factor`].
   1067     #[expect(single_use_lifetimes, reason = "false positive")]
   1068     #[inline]
   1069     #[must_use]
   1070     pub fn second_factor<'a: 'rp_id, 'b: 'user_name, 'c: 'user_display_name, 'd: 'user_id>(
   1071         rp_id: &'a RpId,
   1072         user: PublicKeyCredentialUserEntity<'b, 'c, 'd, USER_LEN>,
   1073         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   1074     ) -> Self {
   1075         let mut opts = Self::passkey(rp_id, user, exclude_credentials);
   1076         opts.public_key.authenticator_selection = AuthenticatorSelectionCriteria::second_factor();
   1077         opts.public_key.extensions.cred_props = Some(ExtensionReq::Allow);
   1078         opts.public_key.extensions.cred_protect =
   1079             CredProtect::UserVerificationOptionalWithCredentialIdList(
   1080                 false,
   1081                 ExtensionInfo::AllowEnforceValue,
   1082             );
   1083         opts
   1084     }
   1085     /// Begins the [registration ceremony](https://www.w3.org/TR/webauthn-3/#registration-ceremony) consuming
   1086     /// `self`. Note that the expiration [`Instant`]/[`SystemTime`] is saved, so `RegistrationClientState` MUST be
   1087     /// sent ASAP. In order to complete registration, the returned `RegistrationServerState` MUST be saved so that
   1088     /// it can later be used to verify the new credential with [`RegistrationServerState::verify`].
   1089     ///
   1090     /// # Errors
   1091     ///
   1092     /// Errors iff `self` contains incompatible configuration.
   1093     ///
   1094     /// # Examples
   1095     ///
   1096     /// ```
   1097     /// # #[cfg(not(feature = "serializable_server_state"))]
   1098     /// # use std::time::Instant;
   1099     /// # #[cfg(not(feature = "serializable_server_state"))]
   1100     /// # use webauthn_rp::request::TimedCeremony as _;
   1101     /// # use webauthn_rp::request::{
   1102     /// #     register::{CredentialCreationOptions, PublicKeyCredentialUserEntity, UserHandle64},
   1103     /// #     AsciiDomain, RpId
   1104     /// # };
   1105     /// # #[cfg(not(feature = "serializable_server_state"))]
   1106     /// assert!(
   1107     ///     CredentialCreationOptions::passkey(
   1108     ///         &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
   1109     ///         PublicKeyCredentialUserEntity {
   1110     ///             name: "bernard.riemann",
   1111     ///             id: &UserHandle64::new(),
   1112     ///             display_name: "Georg Friedrich Bernhard Riemann",
   1113     ///         },
   1114     ///         Vec::new()
   1115     ///     ).start_ceremony()?.0.expiration() > Instant::now()
   1116     /// );
   1117     /// # Ok::<_, webauthn_rp::AggErr>(())
   1118     /// ```
   1119     #[inline]
   1120     pub fn start_ceremony(
   1121         mut self,
   1122     ) -> Result<
   1123         (
   1124             RegistrationServerState<USER_LEN>,
   1125             RegistrationClientState<
   1126                 'rp_id,
   1127                 'user_name,
   1128                 'user_display_name,
   1129                 'user_id,
   1130                 'prf_first,
   1131                 'prf_second,
   1132                 USER_LEN,
   1133             >,
   1134         ),
   1135         CreationOptionsErr,
   1136     > {
   1137         let extensions = self.public_key.extensions.into();
   1138         validate_options_helper(self.public_key.authenticator_selection, extensions).and_then(
   1139             |()| {
   1140                 match self
   1141                     .public_key
   1142                     .authenticator_selection
   1143                     .authenticator_attachment
   1144                 {
   1145                     AuthenticatorAttachment::None => Ok(()),
   1146                     AuthenticatorAttachment::Platform => {
   1147                         if self.public_key.hints.contains_cross_platform_hints() {
   1148                             Err(CreationOptionsErr::HintsIncompatibleWithAuthAttachment)
   1149                         } else {
   1150                             Ok(())
   1151                         }
   1152                     }
   1153                     AuthenticatorAttachment::CrossPlatform => {
   1154                         if self.public_key.hints.contains_platform_hints() {
   1155                             Err(CreationOptionsErr::HintsIncompatibleWithAuthAttachment)
   1156                         } else {
   1157                             Ok(())
   1158                         }
   1159                     }
   1160                 }
   1161                 .and_then(|()| {
   1162                     #[cfg(not(feature = "serializable_server_state"))]
   1163                     let now = Instant::now();
   1164                     #[cfg(feature = "serializable_server_state")]
   1165                     let now = SystemTime::now();
   1166                     now.checked_add(Duration::from_millis(
   1167                         NonZeroU64::from(self.public_key.timeout).get(),
   1168                     ))
   1169                     .ok_or(CreationOptionsErr::InvalidTimeout)
   1170                     .map(|expiration| {
   1171                         // We remove duplicates. The order has no significance, so this is OK.
   1172                         self.public_key
   1173                             .exclude_credentials
   1174                             .sort_unstable_by(|a, b| a.id.as_ref().cmp(b.id.as_ref()));
   1175                         self.public_key
   1176                             .exclude_credentials
   1177                             .dedup_by(|a, b| a.id.as_ref() == b.id.as_ref());
   1178                         (
   1179                             RegistrationServerState {
   1180                                 mediation: self.mediation,
   1181                                 challenge: SentChallenge(self.public_key.challenge.0),
   1182                                 pub_key_cred_params: self.public_key.pub_key_cred_params,
   1183                                 authenticator_selection: self.public_key.authenticator_selection,
   1184                                 extensions,
   1185                                 expiration,
   1186                                 user_id: *self.public_key.user.id,
   1187                             },
   1188                             RegistrationClientState(self),
   1189                         )
   1190                     })
   1191                 })
   1192             },
   1193         )
   1194     }
   1195 }
   1196 /// `CredentialCreationOptions` based on a [`UserHandle64`].
   1197 pub type CredentialCreationOptions64<
   1198     'rp_id,
   1199     'user_name,
   1200     'user_display_name,
   1201     'user_id,
   1202     'prf_first,
   1203     'prf_second,
   1204 > = CredentialCreationOptions<
   1205     'rp_id,
   1206     'user_name,
   1207     'user_display_name,
   1208     'user_id,
   1209     'prf_first,
   1210     'prf_second,
   1211     USER_HANDLE_MAX_LEN,
   1212 >;
   1213 /// `CredentialCreationOptions` based on a [`UserHandle16`].
   1214 pub type CredentialCreationOptions16<
   1215     'rp_id,
   1216     'user_name,
   1217     'user_display_name,
   1218     'user_id,
   1219     'prf_first,
   1220     'prf_second,
   1221 > = CredentialCreationOptions<
   1222     'rp_id,
   1223     'user_name,
   1224     'user_display_name,
   1225     'user_id,
   1226     'prf_first,
   1227     'prf_second,
   1228     16,
   1229 >;
   1230 /// The [`PublicKeyCredentialCreationOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialcreationoptions)
   1231 /// to send to the client when registering a new credential.
   1232 #[derive(Debug)]
   1233 pub struct PublicKeyCredentialCreationOptions<
   1234     'rp_id,
   1235     'user_name,
   1236     'user_display_name,
   1237     'user_id,
   1238     'prf_first,
   1239     'prf_second,
   1240     const USER_LEN: usize,
   1241 > {
   1242     /// [`rp`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-rp).
   1243     pub rp_id: &'rp_id RpId,
   1244     /// [`user`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-user).
   1245     pub user: PublicKeyCredentialUserEntity<'user_name, 'user_display_name, 'user_id, USER_LEN>,
   1246     /// [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-challenge).
   1247     pub challenge: Challenge,
   1248     /// [`pubKeyCredParams`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-pubkeycredparams).
   1249     pub pub_key_cred_params: CoseAlgorithmIdentifiers,
   1250     /// [`timeout`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-timeout).
   1251     ///
   1252     /// Note we require a positive value despite the spec allowing an optional nonnegative value. This jives
   1253     /// with the fact that in-memory storage is required when `serializable_server_state` is not enabled
   1254     /// when attesting credentials as no timeout would make out-of-memory (OOM) conditions more likely.
   1255     pub timeout: NonZeroU32,
   1256     /// [`excludeCredentials`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-excludecredentials).
   1257     pub exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   1258     /// [`authenticatorSelection`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-authenticatorselection).
   1259     pub authenticator_selection: AuthenticatorSelectionCriteria,
   1260     /// [`hints`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-hints).
   1261     pub hints: Hints,
   1262     /// [`extensions`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-extensions).
   1263     pub extensions: Extension<'prf_first, 'prf_second>,
   1264 }
   1265 impl<'rp_id, 'user_name, 'user_display_name, 'user_id, const USER_LEN: usize>
   1266     PublicKeyCredentialCreationOptions<
   1267         'rp_id,
   1268         'user_name,
   1269         'user_display_name,
   1270         'user_id,
   1271         '_,
   1272         '_,
   1273         USER_LEN,
   1274     >
   1275 {
   1276     /// Most deployments of passkeys should use this function. Specifically deployments that are both userless and
   1277     /// passwordless and desire multi-factor authentication (MFA) to be done entirely on the authenticator. It
   1278     /// is important `exclude_credentials` contains the information for _all_ [`RegisteredCredential`]s registered to
   1279     /// [`PublicKeyCredentialUserEntity::id`] to avoid accidentally overwriting existing credentials that
   1280     /// have been previously registered.
   1281     ///
   1282     /// Creates a `PublicKeyCredentialCreationOptions` that requires the authenticator to create a client-side
   1283     /// discoverable credential enforcing any form of user verification. [`Self::timeout`] is [`FIVE_MINUTES`].
   1284     /// [`Extension::cred_protect`] with [`CredProtect::UserVerificationRequired`] with `false` and
   1285     /// [`ExtensionInfo::AllowEnforceValue`] is used.
   1286     ///
   1287     /// # Examples
   1288     ///
   1289     /// ```
   1290     /// # use webauthn_rp::request::{
   1291     /// #     register::{
   1292     /// #         PublicKeyCredentialCreationOptions, PublicKeyCredentialUserEntity, UserHandle64
   1293     /// #     },
   1294     /// #     AsciiDomain, RpId, UserVerificationRequirement
   1295     /// # };
   1296     /// assert!(matches!(
   1297     ///     PublicKeyCredentialCreationOptions::passkey(
   1298     ///         &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
   1299     ///         PublicKeyCredentialUserEntity {
   1300     ///             name: "archimedes.of.syracuse",
   1301     ///             id: &UserHandle64::new(),
   1302     ///             display_name: "Αρχιμήδης ο Συρακούσιος",
   1303     ///         },
   1304     ///         Vec::new()
   1305     ///     )
   1306     ///     .authenticator_selection.user_verification, UserVerificationRequirement::Required
   1307     /// ));
   1308     /// # Ok::<_, webauthn_rp::AggErr>(())
   1309     /// ```
   1310     #[expect(single_use_lifetimes, reason = "false positive")]
   1311     #[inline]
   1312     #[must_use]
   1313     pub fn passkey<'a: 'rp_id, 'b: 'user_name, 'c: 'user_display_name, 'd: 'user_id>(
   1314         rp_id: &'a RpId,
   1315         user: PublicKeyCredentialUserEntity<'b, 'c, 'd, USER_LEN>,
   1316         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   1317     ) -> Self {
   1318         Self {
   1319             rp_id,
   1320             user,
   1321             challenge: Challenge::new(),
   1322             pub_key_cred_params: CoseAlgorithmIdentifiers::default(),
   1323             timeout: FIVE_MINUTES,
   1324             exclude_credentials,
   1325             authenticator_selection: AuthenticatorSelectionCriteria::passkey(),
   1326             hints: Hints::EMPTY,
   1327             extensions: Extension {
   1328                 cred_props: None,
   1329                 cred_protect: CredProtect::UserVerificationRequired(
   1330                     false,
   1331                     ExtensionInfo::AllowEnforceValue,
   1332                 ),
   1333                 min_pin_length: None,
   1334                 prf: None,
   1335             },
   1336         }
   1337     }
   1338     /// Deployments that want to incorporate a "something a user has" factor into a larger multi-factor
   1339     /// authentication (MFA) setup. Specifically deployments that are _not_ userless or passwordless. It
   1340     /// is important `exclude_credentials` contains the information for _all_ [`RegisteredCredential`]s registered
   1341     /// to [`PublicKeyCredentialUserEntity::id`] to avoid accidentally overwriting existing credentials that
   1342     /// have been previously registered.
   1343     ///
   1344     /// Creates a `PublicKeyCredentialCreationOptions` that prefers the authenticator to create a server-side
   1345     /// credential without requiring user verification. [`Self::timeout`] is [`FIVE_MINUTES`].
   1346     /// [`Extension::cred_props`] is [`ExtensionReq::Allow`]. [`Extension::cred_protect`] is
   1347     /// [`CredProtect::UserVerificationOptionalWithCredentialIdList`] with `false` and
   1348     /// [`ExtensionInfo::AllowEnforceValue`].
   1349     ///
   1350     /// Note some authenticators require user verification during credential registration (e.g.,
   1351     /// [CTAP 2.0 authenticators](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#authenticatorMakeCredential)).
   1352     /// When an authenticator supports both CTAP 2.0 and
   1353     /// [Universal 2nd Factor (U2F)](https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-overview-v1.2-ps-20170411.html#registration-creating-a-key-pair)
   1354     /// protocols, user agents will sometimes fall back to U2F when [`UserVerificationRequirement::Discouraged`]
   1355     /// is requested since the latter allows for registration without user verification. If the user agent does not
   1356     /// do this, then users will have an inconsistent experience when authenticating an already-registered
   1357     /// credential. If this is undesirable, one can use [`UserVerificationRequirement::Required`] for this and
   1358     /// [`PublicKeyCredentialRequestOptions::user_verification`] at the expense of requiring a user to verify
   1359     /// themselves twice: once for the first factor and again here.
   1360     ///
   1361     /// # Examples
   1362     ///
   1363     /// ```
   1364     /// # use webauthn_rp::request::{register::{
   1365     /// #     PublicKeyCredentialCreationOptions, PublicKeyCredentialUserEntity, UserHandle64
   1366     /// # }, AsciiDomain, RpId};
   1367     /// assert_eq!(
   1368     ///     PublicKeyCredentialCreationOptions::second_factor(
   1369     ///         &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
   1370     ///         PublicKeyCredentialUserEntity {
   1371     ///             name: "carl.gauss",
   1372     ///             id: &UserHandle64::new(),
   1373     ///             display_name: "Johann Carl Friedrich Gauß",
   1374     ///         },
   1375     ///         Vec::new()
   1376     ///     )
   1377     ///     .timeout
   1378     ///     .get(),
   1379     ///     300_000
   1380     /// );
   1381     /// # Ok::<_, webauthn_rp::AggErr>(())
   1382     /// ```
   1383     #[expect(single_use_lifetimes, reason = "false positive")]
   1384     #[inline]
   1385     #[must_use]
   1386     pub fn second_factor<'a: 'rp_id, 'b: 'user_name, 'c: 'user_display_name, 'd: 'user_id>(
   1387         rp_id: &'a RpId,
   1388         user: PublicKeyCredentialUserEntity<'b, 'c, 'd, USER_LEN>,
   1389         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   1390     ) -> Self {
   1391         let mut opts = Self::passkey(rp_id, user, exclude_credentials);
   1392         opts.authenticator_selection = AuthenticatorSelectionCriteria::second_factor();
   1393         opts.extensions.cred_props = Some(ExtensionReq::Allow);
   1394         opts.extensions.cred_protect = CredProtect::UserVerificationOptionalWithCredentialIdList(
   1395             false,
   1396             ExtensionInfo::AllowEnforceValue,
   1397         );
   1398         opts
   1399     }
   1400 }
   1401 /// `PublicKeyCredentialCreationOptions` based on a [`UserHandle64`].
   1402 pub type PublicKeyCredentialCreationOptions64<
   1403     'rp_id,
   1404     'user_name,
   1405     'user_display_name,
   1406     'user_id,
   1407     'prf_first,
   1408     'prf_second,
   1409 > = PublicKeyCredentialCreationOptions<
   1410     'rp_id,
   1411     'user_name,
   1412     'user_display_name,
   1413     'user_id,
   1414     'prf_first,
   1415     'prf_second,
   1416     USER_HANDLE_MAX_LEN,
   1417 >;
   1418 /// `PublicKeyCredentialCreationOptions` based on a [`UserHandle16`].
   1419 pub type PublicKeyCredentialCreationOptions16<
   1420     'rp_id,
   1421     'user_name,
   1422     'user_display_name,
   1423     'user_id,
   1424     'prf_first,
   1425     'prf_second,
   1426 > = PublicKeyCredentialCreationOptions<
   1427     'rp_id,
   1428     'user_name,
   1429     'user_display_name,
   1430     'user_id,
   1431     'prf_first,
   1432     'prf_second,
   1433     16,
   1434 >;
   1435 /// Container of a [`CredentialCreationOptions`] that has been used to start the registration ceremony.
   1436 /// This gets sent to the client ASAP.
   1437 #[derive(Debug)]
   1438 pub struct RegistrationClientState<
   1439     'rp_id,
   1440     'user_name,
   1441     'user_display_name,
   1442     'user_id,
   1443     'prf_first,
   1444     'prf_second,
   1445     const USER_LEN: usize,
   1446 >(
   1447     CredentialCreationOptions<
   1448         'rp_id,
   1449         'user_name,
   1450         'user_display_name,
   1451         'user_id,
   1452         'prf_first,
   1453         'prf_second,
   1454         USER_LEN,
   1455     >,
   1456 );
   1457 impl<
   1458     'rp_id,
   1459     'user_name,
   1460     'user_display_name,
   1461     'user_id,
   1462     'prf_first,
   1463     'prf_second,
   1464     const USER_LEN: usize,
   1465 >
   1466     RegistrationClientState<
   1467         'rp_id,
   1468         'user_name,
   1469         'user_display_name,
   1470         'user_id,
   1471         'prf_first,
   1472         'prf_second,
   1473         USER_LEN,
   1474     >
   1475 {
   1476     /// Returns the `CredentialCreationOptions` that was used to start a registration ceremony.
   1477     ///
   1478     /// # Examples
   1479     ///
   1480     /// ```
   1481     /// # use webauthn_rp::request::{register::{
   1482     /// #     CoseAlgorithmIdentifiers, CredentialCreationOptions,
   1483     /// #     PublicKeyCredentialUserEntity, UserHandle64,
   1484     /// # }, AsciiDomain, RpId};
   1485     /// assert_eq!(
   1486     ///     CredentialCreationOptions::passkey(
   1487     ///         &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
   1488     ///         PublicKeyCredentialUserEntity {
   1489     ///             name: "david.hilbert",
   1490     ///             id: &UserHandle64::new(),
   1491     ///             display_name: "David Hilbert",
   1492     ///         },
   1493     ///         Vec::new()
   1494     ///     )
   1495     ///     .start_ceremony()?
   1496     ///     .1
   1497     ///     .options()
   1498     ///     .public_key
   1499     ///     .rp_id.as_ref(),
   1500     ///     "example.com"
   1501     /// );
   1502     /// # Ok::<_, webauthn_rp::AggErr>(())
   1503     /// ```
   1504     #[inline]
   1505     #[must_use]
   1506     pub const fn options(
   1507         &self,
   1508     ) -> &CredentialCreationOptions<
   1509         'rp_id,
   1510         'user_name,
   1511         'user_display_name,
   1512         'user_id,
   1513         'prf_first,
   1514         'prf_second,
   1515         USER_LEN,
   1516     > {
   1517         &self.0
   1518     }
   1519 }
   1520 /// `RegistrationClientState` based on a [`UserHandle64`].
   1521 pub type RegistrationClientState64<
   1522     'rp_id,
   1523     'user_name,
   1524     'user_display_name,
   1525     'user_id,
   1526     'prf_first,
   1527     'prf_second,
   1528 > = RegistrationClientState<
   1529     'rp_id,
   1530     'user_name,
   1531     'user_display_name,
   1532     'user_id,
   1533     'prf_first,
   1534     'prf_second,
   1535     USER_HANDLE_MAX_LEN,
   1536 >;
   1537 /// `RegistrationClientState` based on a [`UserHandle16`].
   1538 pub type RegistrationClientState16<
   1539     'rp_id,
   1540     'user_name,
   1541     'user_display_name,
   1542     'user_id,
   1543     'prf_first,
   1544     'prf_second,
   1545 > = RegistrationClientState<
   1546     'rp_id,
   1547     'user_name,
   1548     'user_display_name,
   1549     'user_id,
   1550     'prf_first,
   1551     'prf_second,
   1552     16,
   1553 >;
   1554 /// Additional verification options to perform in [`RegistrationServerState::verify`].
   1555 #[derive(Clone, Copy, Debug)]
   1556 pub struct RegistrationVerificationOptions<'origins, 'top_origins, O, T> {
   1557     /// Origins to use for [origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin).
   1558     ///
   1559     /// When this is empty, the origin that will be used will be based on
   1560     /// the [`RpId`] passed to [`RegistrationServerState::verify`]. If [`RpId::Domain`] or [`RpId::StaticDomain`],
   1561     /// then the [`DomainOrigin`] returned from passing [`AsciiDomain::as_ref`] and [`AsciiDomainStatic::as_str`]
   1562     /// to [`DomainOrigin::new`] respectively will be used; otherwise the [`Url`] in [`RpId::Url`] will be used.
   1563     pub allowed_origins: &'origins [O],
   1564     /// [Top-level origins](https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-top-level-origin)
   1565     /// to use for [origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin).
   1566     ///
   1567     /// When this is `Some`, [`CollectedClientData::cross_origin`] is allowed to be `true`. When the contained
   1568     /// `slice` is empty, [`CollectedClientData::top_origin`] must be `None`. When this is `None`,
   1569     /// `CollectedClientData::cross_origin` must be `false` and `CollectedClientData::top_origin` must be `None`.
   1570     pub allowed_top_origins: Option<&'top_origins [T]>,
   1571     /// The required [`Backup`] state of the credential.
   1572     pub backup_requirement: BackupReq,
   1573     /// Error when unsolicited extensions are sent back iff `true`.
   1574     pub error_on_unsolicited_extensions: bool,
   1575     /// [`AuthenticatorAttachment`] must be sent iff `true`.
   1576     pub require_authenticator_attachment: bool,
   1577     /// [`CollectedClientData::from_client_data_json_relaxed`] is used to extract [`CollectedClientData`] iff `true`.
   1578     #[cfg(feature = "serde_relaxed")]
   1579     pub client_data_json_relaxed: bool,
   1580 }
   1581 impl<O, T> RegistrationVerificationOptions<'_, '_, O, T> {
   1582     /// Returns `Self` such that [`Self::allowed_origins`] is empty, [`Self::allowed_top_origins`] is `None`,
   1583     /// [`Self::backup_requirement`] is [`BackupReq::None`], [`Self::error_on_unsolicited_extensions`] is `true`,
   1584     /// [`Self::require_authenticator_attachment`] is `false`, and [`Self::client_data_json_relaxed`] is
   1585     /// `true`.
   1586     ///
   1587     /// Note `O` and `T` should implement `PartialEq<Origin<'_>>` (e.g., `&str`).
   1588     #[inline]
   1589     #[must_use]
   1590     pub const fn new() -> Self {
   1591         Self {
   1592             allowed_origins: [].as_slice(),
   1593             allowed_top_origins: None,
   1594             backup_requirement: BackupReq::None,
   1595             error_on_unsolicited_extensions: true,
   1596             require_authenticator_attachment: false,
   1597             #[cfg(feature = "serde_relaxed")]
   1598             client_data_json_relaxed: true,
   1599         }
   1600     }
   1601 }
   1602 impl<O, T> Default for RegistrationVerificationOptions<'_, '_, O, T> {
   1603     /// Same as [`Self::new`].
   1604     #[inline]
   1605     fn default() -> Self {
   1606         Self {
   1607             allowed_origins: &[],
   1608             allowed_top_origins: None,
   1609             backup_requirement: BackupReq::default(),
   1610             error_on_unsolicited_extensions: true,
   1611             require_authenticator_attachment: false,
   1612             #[cfg(feature = "serde_relaxed")]
   1613             client_data_json_relaxed: true,
   1614         }
   1615     }
   1616 }
   1617 /// `PrfInput` without the actual data sent to reduce memory usage when storing [`RegistrationServerState`] in an
   1618 /// in-memory collection.
   1619 #[derive(Clone, Copy, Debug)]
   1620 enum ServerPrfInfo {
   1621     /// No `PrfInput`.
   1622     None,
   1623     /// `PrfInput::second` was `None`.
   1624     One(ExtensionInfo),
   1625     /// `PrfInput::second` was `Some`.
   1626     Two(ExtensionInfo),
   1627 }
   1628 #[cfg(test)]
   1629 impl PartialEq for ServerPrfInfo {
   1630     fn eq(&self, other: &Self) -> bool {
   1631         match *self {
   1632             Self::None => matches!(*other, Self::None),
   1633             Self::One(info) => matches!(*other, Self::One(info2) if info == info2),
   1634             Self::Two(info) => matches!(*other, Self::Two(info2) if info == info2),
   1635         }
   1636     }
   1637 }
   1638 impl From<Option<(PrfInput<'_, '_>, ExtensionInfo)>> for ServerPrfInfo {
   1639     fn from(value: Option<(PrfInput<'_, '_>, ExtensionInfo)>) -> Self {
   1640         value.map_or(Self::None, |val| {
   1641             val.0
   1642                 .second
   1643                 .map_or_else(|| Self::One(val.1), |_| Self::Two(val.1))
   1644         })
   1645     }
   1646 }
   1647 /// `Extension` without the actual data sent to reduce memory usage when storing [`AuthenticationServerState`]
   1648 /// in an in-memory collection.
   1649 #[derive(Clone, Copy, Debug)]
   1650 struct ServerExtensionInfo {
   1651     /// `Extension::cred_props`.
   1652     cred_props: Option<ExtensionReq>,
   1653     /// `Extension::cred_protect`.
   1654     cred_protect: CredProtect,
   1655     /// `Extension::min_pin_length`.
   1656     min_pin_length: Option<(FourToSixtyThree, ExtensionInfo)>,
   1657     /// `Extension::prf`.
   1658     prf: ServerPrfInfo,
   1659 }
   1660 impl ServerExtensionInfo {
   1661     /// Validates the extensions.
   1662     fn validate(
   1663         self,
   1664         client_ext: ClientExtensionsOutputs,
   1665         auth_ext: AuthenticatorExtensionOutput,
   1666         error_unsolicited: bool,
   1667     ) -> Result<(), ExtensionErr> {
   1668         if error_unsolicited {
   1669             self.validate_unsolicited(client_ext, auth_ext)
   1670         } else {
   1671             Ok(())
   1672         }
   1673         .and_then(|()| {
   1674             self.validate_required(client_ext, auth_ext)
   1675                 .and_then(|()| self.validate_value(client_ext, auth_ext))
   1676         })
   1677     }
   1678     /// Validates if there are any unsolicited extensions.
   1679     ///
   1680     /// Note no distinction is made between an extension that is empty and one that is not (i.e., we are checking
   1681     ///  purely for the existence of extension keys).
   1682     fn validate_unsolicited(
   1683         mut self,
   1684         client_ext: ClientExtensionsOutputs,
   1685         auth_ext: AuthenticatorExtensionOutput,
   1686     ) -> Result<(), ExtensionErr> {
   1687         // For simpler code, we artificially set non-requested extensions after verifying there was not an error
   1688         // and recursively call this function. There are so few extensions and the checks are fast that there
   1689         // should be no worry of stack overflow or performance overhead.
   1690         if self.cred_props.is_some() {
   1691             if !matches!(self.cred_protect, CredProtect::None) {
   1692                 if self.min_pin_length.is_some() {
   1693                     // This is the last extension, so recursion stops here.
   1694                     if !matches!(self.prf, ServerPrfInfo::None) {
   1695                         Ok(())
   1696                     } else if client_ext.prf.is_some() {
   1697                         Err(ExtensionErr::ForbiddenPrf)
   1698                     } else if !matches!(auth_ext.hmac_secret, HmacSecret::None) {
   1699                         Err(ExtensionErr::ForbiddenHmacSecret)
   1700                     } else {
   1701                         Ok(())
   1702                     }
   1703                 } else if auth_ext.min_pin_length.is_some() {
   1704                     Err(ExtensionErr::ForbiddenMinPinLength)
   1705                 } else {
   1706                     // Pretend to set `minPinLength`, so we can check `prf`.
   1707                     self.min_pin_length =
   1708                         Some((FourToSixtyThree::Four, ExtensionInfo::RequireEnforceValue));
   1709                     self.validate_unsolicited(client_ext, auth_ext)
   1710                 }
   1711             } else if !matches!(auth_ext.cred_protect, CredentialProtectionPolicy::None) {
   1712                 Err(ExtensionErr::ForbiddenCredProtect)
   1713             } else {
   1714                 // Pretend to set `credProtect`, so we can check `minPinLength` and `prf` extensions.
   1715                 self.cred_protect = CredProtect::UserVerificationOptional(
   1716                     false,
   1717                     ExtensionInfo::RequireEnforceValue,
   1718                 );
   1719                 self.validate_unsolicited(client_ext, auth_ext)
   1720             }
   1721         } else if client_ext.cred_props.is_some() {
   1722             Err(ExtensionErr::ForbiddenCredProps)
   1723         } else {
   1724             // Pretend to set `credProps`; so we can check `credProtect`, `minPinLength`, and `prf` extensions.
   1725             self.cred_props = Some(ExtensionReq::Require);
   1726             self.validate_unsolicited(client_ext, auth_ext)
   1727         }
   1728     }
   1729     /// Validates if any required extensions don't have a corresponding response.
   1730     ///
   1731     /// Note empty extensions are treated as missing. For example when requiring the `credProps` extension,
   1732     /// all of the following responses would lead to a failure:
   1733     /// `{"clientExtensionResults":{}}`: no extensions.
   1734     /// `{"clientExtensionResults":{"prf":true}}`: only the `prf` extension.
   1735     /// `{"clientExtensionResults":{"credProps":{}}}`: empty `credProps` extension.
   1736     /// `{"clientExtensionResults":{"credProps":{"foo":false}}}`: `credProps` extension doesn't contain at least one
   1737     /// expected field (i.e., still "empty").
   1738     fn validate_required(
   1739         self,
   1740         client_ext: ClientExtensionsOutputs,
   1741         auth_ext: AuthenticatorExtensionOutput,
   1742     ) -> Result<(), ExtensionErr> {
   1743         // We don't check `self.cred_protect` since `CredProtect::validate` checks for both a required response
   1744         // and value enforcement; thus it only needs to be checked once (which it is in `Self::validate_value`).
   1745         self.cred_props
   1746             .map_or(Ok(()), |info| {
   1747                 if matches!(info, ExtensionReq::Require) {
   1748                     if client_ext
   1749                         .cred_props
   1750                         .is_some_and(|props| props.rk.is_some())
   1751                     {
   1752                         Ok(())
   1753                     } else {
   1754                         Err(ExtensionErr::MissingCredProps)
   1755                     }
   1756                 } else {
   1757                     Ok(())
   1758                 }
   1759             })
   1760             .and_then(|()| {
   1761                 self.min_pin_length
   1762                     .map_or(Ok(()), |info| {
   1763                         if matches!(
   1764                             info.1,
   1765                             ExtensionInfo::RequireEnforceValue
   1766                                 | ExtensionInfo::RequireDontEnforceValue
   1767                         ) {
   1768                             auth_ext
   1769                                 .min_pin_length
   1770                                 .ok_or(ExtensionErr::MissingMinPinLength)
   1771                                 .map(|_| ())
   1772                         } else {
   1773                             Ok(())
   1774                         }
   1775                     })
   1776                     .and_then(|()| match self.prf {
   1777                         ServerPrfInfo::None => Ok(()),
   1778                         ServerPrfInfo::One(info) | ServerPrfInfo::Two(info) => {
   1779                             if matches!(
   1780                                 info,
   1781                                 ExtensionInfo::RequireEnforceValue
   1782                                     | ExtensionInfo::RequireDontEnforceValue
   1783                             ) {
   1784                                 if client_ext.prf.is_some() {
   1785                                     Ok(())
   1786                                 } else {
   1787                                     Err(ExtensionErr::MissingPrf)
   1788                                 }
   1789                             } else {
   1790                                 Ok(())
   1791                             }
   1792                         }
   1793                     })
   1794             })
   1795     }
   1796     /// Validates the value of any extensions sent from the client.
   1797     ///
   1798     /// Note missing and empty extensions are always OK.
   1799     fn validate_value(
   1800         self,
   1801         client_ext: ClientExtensionsOutputs,
   1802         auth_ext: AuthenticatorExtensionOutput,
   1803     ) -> Result<(), ExtensionErr> {
   1804         // This also checks for a missing response. Instead of duplicating that check, we only call
   1805         // `self.cred_protect.validate` once here and not also in `Self::validate_required`.
   1806         self.cred_protect
   1807             .validate(auth_ext.cred_protect)
   1808             .and_then(|()| {
   1809                 self.min_pin_length
   1810                     .map_or(Ok(()), |info| {
   1811                         if matches!(
   1812                             info.1,
   1813                             ExtensionInfo::RequireEnforceValue | ExtensionInfo::AllowEnforceValue
   1814                         ) {
   1815                             auth_ext.min_pin_length.map_or(Ok(()), |pin| {
   1816                                 if pin >= info.0 {
   1817                                     Ok(())
   1818                                 } else {
   1819                                     Err(ExtensionErr::InvalidMinPinLength(info.0, pin))
   1820                                 }
   1821                             })
   1822                         } else {
   1823                             Ok(())
   1824                         }
   1825                     })
   1826                     .and_then(|()| match self.prf {
   1827                         ServerPrfInfo::None => Ok(()),
   1828                         ServerPrfInfo::One(info) | ServerPrfInfo::Two(info) => {
   1829                             if matches!(
   1830                                 info,
   1831                                 ExtensionInfo::RequireEnforceValue
   1832                                     | ExtensionInfo::AllowEnforceValue
   1833                             ) {
   1834                                 client_ext
   1835                                     .prf
   1836                                     .map_or(Ok(()), |prf| {
   1837                                         if prf.enabled {
   1838                                             Ok(())
   1839                                         } else {
   1840                                             Err(ExtensionErr::InvalidPrfValue)
   1841                                         }
   1842                                     })
   1843                                     .and({
   1844                                         if matches!(auth_ext.hmac_secret, HmacSecret::NotEnabled) {
   1845                                             Err(ExtensionErr::InvalidHmacSecretValue)
   1846                                         } else {
   1847                                             Ok(())
   1848                                         }
   1849                                     })
   1850                             } else {
   1851                                 Ok(())
   1852                             }
   1853                         }
   1854                     })
   1855             })
   1856     }
   1857 }
   1858 impl From<Extension<'_, '_>> for ServerExtensionInfo {
   1859     fn from(value: Extension<'_, '_>) -> Self {
   1860         Self {
   1861             cred_props: value.cred_props,
   1862             cred_protect: value.cred_protect,
   1863             min_pin_length: value.min_pin_length,
   1864             prf: value.prf.into(),
   1865         }
   1866     }
   1867 }
   1868 #[cfg(test)]
   1869 impl PartialEq for ServerExtensionInfo {
   1870     fn eq(&self, other: &Self) -> bool {
   1871         self.prf == other.prf
   1872     }
   1873 }
   1874 // This is essentially the `PublicKeyCredentialCreationOptions` used to create it; however to reduce
   1875 // memory usage, we remove all unnecessary data making an instance of this 48 bytes in size on
   1876 // `x86_64-unknown-linux-gnu` platforms when `USER_LEN` is `USER_HANDLE_MIN_LEN`.
   1877 /// State needed to be saved when beginning the registration ceremony.
   1878 ///
   1879 /// Saves the necessary information associated with the [`CredentialCreationOptions`] used to create it
   1880 /// via [`CredentialCreationOptions::start_ceremony`] so that registration of a new credential can be
   1881 /// performed with [`Self::verify`].
   1882 ///
   1883 /// `RegistrationServerState` implements [`Borrow`] of [`SentChallenge`]; thus to obtain the correct
   1884 /// `RegistrationServerState` associated with a [`Registration`], one should use its corresponding
   1885 /// [`Registration::challenge`].
   1886 #[derive(Debug)]
   1887 pub struct RegistrationServerState<const USER_LEN: usize> {
   1888     /// [`mediation`](https://www.w3.org/TR/credential-management-1/#dom-credentialcreationoptions-mediation).
   1889     mediation: CredentialMediationRequirement,
   1890     // This is a `SentChallenge` since we need `RegistrationServerState` to be fetchable after receiving the
   1891     // response from the client. This response must obviously be constructable; thus its challenge is a
   1892     // `SentChallenge`.
   1893     //
   1894     // This must never be mutated since we want to ensure it is actually a `Challenge` (which
   1895     // can only be constructed via `Challenge::new`). This is guaranteed to be true iff
   1896     // `serializable_server_state` is not enabled. We avoid implementing `trait`s like `Hash` when that
   1897     // is enabled.
   1898     /// [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-challenge).
   1899     challenge: SentChallenge,
   1900     /// [`pubKeyCredParams`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-pubkeycredparams).
   1901     pub_key_cred_params: CoseAlgorithmIdentifiers,
   1902     /// [`authenticatorSelection`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-authenticatorselection).
   1903     authenticator_selection: AuthenticatorSelectionCriteria,
   1904     /// [`extensions`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-extensions).
   1905     extensions: ServerExtensionInfo,
   1906     /// `Instant` the ceremony expires.
   1907     #[cfg(not(feature = "serializable_server_state"))]
   1908     expiration: Instant,
   1909     /// `SystemTime` the ceremony expires.
   1910     #[cfg(feature = "serializable_server_state")]
   1911     expiration: SystemTime,
   1912     /// User handle.
   1913     user_id: UserHandle<USER_LEN>,
   1914 }
   1915 impl<const USER_LEN: usize> RegistrationServerState<USER_LEN> {
   1916     #[cfg(all(test, feature = "custom", feature = "serializable_server_state"))]
   1917     fn is_eq(&self, other: &Self) -> bool {
   1918         self.mediation == other.mediation
   1919             && self.challenge == other.challenge
   1920             && self.pub_key_cred_params == other.pub_key_cred_params
   1921             && self.authenticator_selection == other.authenticator_selection
   1922             && self.extensions == other.extensions
   1923             && self.expiration == other.expiration
   1924             && self.user_id == other.user_id
   1925     }
   1926     /// Verifies `response` is valid based on `self` consuming `self` and returning a `RegisteredCredential` that
   1927     /// borrows the necessary data from `response`.
   1928     ///
   1929     /// `rp_id` MUST be the same as the [`PublicKeyCredentialCreationOptions::rp_id`] used when starting the
   1930     /// ceremony.
   1931     ///
   1932     /// It is _essential_ to ensure [`RegisteredCredential::id`] has not been previously registered; if
   1933     /// so, the ceremony SHOULD be aborted and a failure reported. When saving `RegisteredCredential`, one may
   1934     /// want to save the [`RpId`] and [`PublicKeyCredentialUserEntity`] information; however since [`RpId`] is
   1935     /// likely static, that may not be necessary. User information is also likely static for a given [`UserHandle`]
   1936     /// (which is saved in `RegisteredCredential`); so if such info is saved, one may want to save it once per
   1937     /// `UserHandle` and not per `RegisteredCredential`.
   1938     ///
   1939     /// # Errors
   1940     ///
   1941     /// Errors iff `response` is not valid according to the
   1942     /// [registration ceremony criteria](https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential)
   1943     /// or violates any of the settings in `options`.
   1944     #[inline]
   1945     pub fn verify<'a, O: PartialEq<Origin<'a>>, T: PartialEq<Origin<'a>>>(
   1946         self,
   1947         rp_id: &RpId,
   1948         response: &'a Registration,
   1949         options: &RegistrationVerificationOptions<'_, '_, O, T>,
   1950     ) -> Result<RegisteredCredential<'a, USER_LEN>, RegCeremonyErr> {
   1951         // [Registration ceremony](https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential)
   1952         // is handled by:
   1953         //
   1954         // 1. Calling code.
   1955         // 2. Client code and the construction of `resp` (hopefully via [`Registration::deserialize`]).
   1956         // 3. Client code and the construction of `resp` (hopefully via [`AuthenticatorAttestation::deserialize`]).
   1957         // 4. Client code and the construction of `resp` (hopefully via [`ClientExtensionsOutputs::deserialize`]).
   1958         // 5. [`Self::partial_validate`].
   1959         // 6. [`Self::partial_validate`].
   1960         // 7. [`Self::partial_validate`].
   1961         // 8. [`Self::partial_validate`].
   1962         // 9. [`Self::partial_validate`].
   1963         // 10. [`Self::partial_validate`].
   1964         // 11. [`Self::partial_validate`].
   1965         // 12. [`Self::partial_validate`].
   1966         // 13. [`Self::partial_validate`].
   1967         // 14. [`Self::partial_validate`].
   1968         // 15. Below.
   1969         // 16. [`Self::partial_validate`].
   1970         // 17. [`Self::partial_validate`].
   1971         // 18. [`Self::partial_validate`].
   1972         // 19. [`Self::partial_validate`].
   1973         // 20. Below.
   1974         // 21. [`Self::partial_validate`].
   1975         // 22. [`Self::partial_validate`].
   1976         // 23. N/A since only none and self attestations are supported.
   1977         // 24. Always satisfied since only none and self attestations are supported (Item 3 is N/A).
   1978         // 25. [`Self::partial_validate`].
   1979         // 26. Calling code.
   1980         // 27. Below.
   1981         // 28. N/A since only none and self attestations are supported.
   1982         // 29. Below.
   1983 
   1984         // Steps 5–14, 16–19, 21–22, and 25.
   1985         self.partial_validate(rp_id, response, (), &options.into())
   1986             .map_err(RegCeremonyErr::from)
   1987             .and_then(|attestation_object| {
   1988                 let auth_data = attestation_object.auth_data();
   1989                 let flags = auth_data.flags();
   1990                 // Step 15.
   1991                 if matches!(self.mediation, CredentialMediationRequirement::Conditional)
   1992                     || flags.user_present
   1993                 {
   1994                     self.authenticator_selection
   1995                         // Verify any required authenticator attachment modality.
   1996                         .validate(
   1997                             options.require_authenticator_attachment,
   1998                             response.authenticator_attachment,
   1999                         )
   2000                         .and_then(|()| {
   2001                             let attested_credential_data = auth_data.attested_credential_data();
   2002                             self.pub_key_cred_params
   2003                                 // Step 20.
   2004                                 .validate(attested_credential_data.credential_public_key)
   2005                                 .and_then(|()| {
   2006                                     let extensions = auth_data.extensions();
   2007                                     // Step 27.
   2008                                     self.extensions
   2009                                         .validate(
   2010                                             response.client_extension_results,
   2011                                             extensions,
   2012                                             options.error_on_unsolicited_extensions,
   2013                                         )
   2014                                         .map_err(RegCeremonyErr::Extension)
   2015                                         .and_then(|()| {
   2016                                             // Step 29.
   2017                                             RegisteredCredential::new(
   2018                                                 attested_credential_data.credential_id,
   2019                                                 response.response.transports(),
   2020                                                 self.user_id,
   2021                                                 StaticState {
   2022                                                     credential_public_key: attested_credential_data
   2023                                                         .credential_public_key,
   2024                                                     extensions: extensions.into(),
   2025                                                     client_extension_results: response
   2026                                                         .client_extension_results
   2027                                                         .into(),
   2028                                                 },
   2029                                                 DynamicState {
   2030                                                     user_verified: flags.user_verified,
   2031                                                     backup: flags.backup,
   2032                                                     sign_count: auth_data.sign_count(),
   2033                                                     authenticator_attachment: response
   2034                                                         .authenticator_attachment,
   2035                                                 },
   2036                                                 Metadata {
   2037                                                     attestation: match attestation_object
   2038                                                         .attestation()
   2039                                                     {
   2040                                                         AttestationFormat::None => {
   2041                                                             Attestation::None
   2042                                                         }
   2043                                                         AttestationFormat::Packed(_) => {
   2044                                                             Attestation::Surrogate
   2045                                                         }
   2046                                                     },
   2047                                                     aaguid: attested_credential_data.aaguid,
   2048                                                     extensions: extensions.into(),
   2049                                                     client_extension_results: response
   2050                                                         .client_extension_results
   2051                                                         .into(),
   2052                                                     resident_key: self
   2053                                                         .authenticator_selection
   2054                                                         .resident_key,
   2055                                                 },
   2056                                             )
   2057                                             .map_err(RegCeremonyErr::Credential)
   2058                                         })
   2059                                 })
   2060                         })
   2061                 } else {
   2062                     Err(RegCeremonyErr::UserNotPresent)
   2063                 }
   2064             })
   2065     }
   2066 }
   2067 impl<const USER_LEN: usize> TimedCeremony for RegistrationServerState<USER_LEN> {
   2068     #[cfg(any(doc, not(feature = "serializable_server_state")))]
   2069     #[inline]
   2070     fn expiration(&self) -> Instant {
   2071         self.expiration
   2072     }
   2073     #[cfg(all(not(doc), feature = "serializable_server_state"))]
   2074     #[inline]
   2075     fn expiration(&self) -> SystemTime {
   2076         self.expiration
   2077     }
   2078 }
   2079 impl<const USER_LEN: usize> Ceremony<USER_LEN, false> for RegistrationServerState<USER_LEN> {
   2080     type R = Registration;
   2081     fn rand_challenge(&self) -> SentChallenge {
   2082         self.challenge
   2083     }
   2084     #[cfg(not(feature = "serializable_server_state"))]
   2085     fn expiry(&self) -> Instant {
   2086         self.expiration
   2087     }
   2088     #[cfg(feature = "serializable_server_state")]
   2089     fn expiry(&self) -> SystemTime {
   2090         self.expiration
   2091     }
   2092     fn user_verification(&self) -> UserVerificationRequirement {
   2093         self.authenticator_selection.user_verification
   2094     }
   2095 }
   2096 impl<const USER_LEN: usize> Borrow<SentChallenge> for RegistrationServerState<USER_LEN> {
   2097     #[inline]
   2098     fn borrow(&self) -> &SentChallenge {
   2099         &self.challenge
   2100     }
   2101 }
   2102 impl<const USER_LEN: usize> PartialEq for RegistrationServerState<USER_LEN> {
   2103     #[inline]
   2104     fn eq(&self, other: &Self) -> bool {
   2105         self.challenge == other.challenge
   2106     }
   2107 }
   2108 impl<const USER_LEN: usize> PartialEq<&Self> for RegistrationServerState<USER_LEN> {
   2109     #[inline]
   2110     fn eq(&self, other: &&Self) -> bool {
   2111         *self == **other
   2112     }
   2113 }
   2114 impl<const USER_LEN: usize> PartialEq<RegistrationServerState<USER_LEN>>
   2115     for &RegistrationServerState<USER_LEN>
   2116 {
   2117     #[inline]
   2118     fn eq(&self, other: &RegistrationServerState<USER_LEN>) -> bool {
   2119         **self == *other
   2120     }
   2121 }
   2122 impl<const USER_LEN: usize> Eq for RegistrationServerState<USER_LEN> {}
   2123 impl<const USER_LEN: usize> Hash for RegistrationServerState<USER_LEN> {
   2124     #[inline]
   2125     fn hash<H: Hasher>(&self, state: &mut H) {
   2126         self.challenge.hash(state);
   2127     }
   2128 }
   2129 impl<const USER_LEN: usize> PartialOrd for RegistrationServerState<USER_LEN> {
   2130     #[inline]
   2131     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
   2132         Some(self.cmp(other))
   2133     }
   2134 }
   2135 impl<const USER_LEN: usize> Ord for RegistrationServerState<USER_LEN> {
   2136     #[inline]
   2137     fn cmp(&self, other: &Self) -> Ordering {
   2138         self.challenge.cmp(&other.challenge)
   2139     }
   2140 }
   2141 /// `RegistrationServerState` based on a [`UserHandle64`].
   2142 pub type RegistrationServerState64 = RegistrationServerState<USER_HANDLE_MAX_LEN>;
   2143 /// `RegistrationServerState` based on a [`UserHandle16`].
   2144 pub type RegistrationServerState16 = RegistrationServerState<16>;