webauthn_rp

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

register.rs (89418B)


      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!(
    981         extensions.cred_protect,
    982         CredProtect::UserVerificationRequired(_, _)
    983     ) {
    984         Err(CreationOptionsErr::CredProtectRequiredWithoutUserVerification)
    985     } else {
    986         Ok(())
    987     }
    988 }
    989 /// The [`CredentialCreationOptions`](https://www.w3.org/TR/credential-management-1/#dictdef-credentialcreationoptions)
    990 /// to send to the client when registering a new credential.
    991 ///
    992 /// Upon saving the [`RegistrationServerState`] returned from [`Self::start_ceremony`], one MUST send
    993 /// [`RegistrationClientState`] to the client ASAP. After receiving the newly created [`Registration`], it is
    994 /// validated using [`RegistrationServerState::verify`].
    995 #[derive(Debug)]
    996 pub struct CredentialCreationOptions<
    997     'rp_id,
    998     'user_name,
    999     'user_display_name,
   1000     'user_id,
   1001     'prf_first,
   1002     'prf_second,
   1003     const USER_LEN: usize,
   1004 > {
   1005     /// [`mediation`](https://www.w3.org/TR/credential-management-1/#dom-credentialcreationoptions-mediation).
   1006     ///
   1007     /// Note if this is [`CredentialMediationRequirement::Conditional`], one may want to ensure
   1008     /// [`AuthenticatorSelectionCriteria::user_verification`] is not [`UserVerificationRequirement::Required`]
   1009     /// since some authenticators cannot enforce user verification during registration ceremonies when conditional
   1010     /// mediation is used. Do note that in the event passkeys are to be created, one may want to set
   1011     /// [`AuthenticationVerificationOptions::update_uv`] to `true` since [`Flag::user_verified`] will
   1012     /// potentially be `false`.
   1013     pub mediation: CredentialMediationRequirement,
   1014     /// `public-key` [credential type](https://www.w3.org/TR/credential-management-1/#sctn-cred-type-registry).
   1015     pub public_key: PublicKeyCredentialCreationOptions<
   1016         'rp_id,
   1017         'user_name,
   1018         'user_display_name,
   1019         'user_id,
   1020         'prf_first,
   1021         'prf_second,
   1022         USER_LEN,
   1023     >,
   1024 }
   1025 impl<
   1026     'rp_id,
   1027     'user_name,
   1028     'user_display_name,
   1029     'user_id,
   1030     'prf_first,
   1031     'prf_second,
   1032     const USER_LEN: usize,
   1033 >
   1034     CredentialCreationOptions<
   1035         'rp_id,
   1036         'user_name,
   1037         'user_display_name,
   1038         'user_id,
   1039         'prf_first,
   1040         'prf_second,
   1041         USER_LEN,
   1042     >
   1043 {
   1044     /// Sets [`Self::mediation`] to [`CredentialMediationRequirement::default`] and
   1045     /// [`Self::public_key`] to [`PublicKeyCredentialCreationOptions::passkey`].
   1046     #[expect(single_use_lifetimes, reason = "false positive")]
   1047     #[inline]
   1048     #[must_use]
   1049     pub fn passkey<'a: 'rp_id, 'b: 'user_name, 'c: 'user_display_name, 'd: 'user_id>(
   1050         rp_id: &'a RpId,
   1051         user: PublicKeyCredentialUserEntity<'b, 'c, 'd, USER_LEN>,
   1052         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   1053     ) -> Self {
   1054         Self {
   1055             mediation: CredentialMediationRequirement::default(),
   1056             public_key: PublicKeyCredentialCreationOptions::passkey(
   1057                 rp_id,
   1058                 user,
   1059                 exclude_credentials,
   1060             ),
   1061         }
   1062     }
   1063     /// Sets [`Self::mediation`] to [`CredentialMediationRequirement::default`] and
   1064     /// [`Self::public_key`] to [`PublicKeyCredentialCreationOptions::second_factor`].
   1065     #[expect(single_use_lifetimes, reason = "false positive")]
   1066     #[inline]
   1067     #[must_use]
   1068     pub fn second_factor<'a: 'rp_id, 'b: 'user_name, 'c: 'user_display_name, 'd: 'user_id>(
   1069         rp_id: &'a RpId,
   1070         user: PublicKeyCredentialUserEntity<'b, 'c, 'd, USER_LEN>,
   1071         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   1072     ) -> Self {
   1073         let mut opts = Self::passkey(rp_id, user, exclude_credentials);
   1074         opts.public_key.authenticator_selection = AuthenticatorSelectionCriteria::second_factor();
   1075         opts.public_key.extensions.cred_props = Some(ExtensionReq::Allow);
   1076         opts.public_key.extensions.cred_protect =
   1077             CredProtect::UserVerificationOptionalWithCredentialIdList(
   1078                 false,
   1079                 ExtensionInfo::AllowEnforceValue,
   1080             );
   1081         opts
   1082     }
   1083     /// Begins the [registration ceremony](https://www.w3.org/TR/webauthn-3/#registration-ceremony) consuming
   1084     /// `self`. Note that the expiration [`Instant`]/[`SystemTime`] is saved, so `RegistrationClientState` MUST be
   1085     /// sent ASAP. In order to complete registration, the returned `RegistrationServerState` MUST be saved so that
   1086     /// it can later be used to verify the new credential with [`RegistrationServerState::verify`].
   1087     ///
   1088     /// # Errors
   1089     ///
   1090     /// Errors iff `self` contains incompatible configuration.
   1091     ///
   1092     /// # Examples
   1093     ///
   1094     /// ```
   1095     /// # #[cfg(not(feature = "serializable_server_state"))]
   1096     /// # use std::time::Instant;
   1097     /// # #[cfg(not(feature = "serializable_server_state"))]
   1098     /// # use webauthn_rp::request::TimedCeremony as _;
   1099     /// # use webauthn_rp::request::{
   1100     /// #     register::{CredentialCreationOptions, PublicKeyCredentialUserEntity, UserHandle64},
   1101     /// #     AsciiDomain, RpId
   1102     /// # };
   1103     /// # #[cfg(not(feature = "serializable_server_state"))]
   1104     /// assert!(
   1105     ///     CredentialCreationOptions::passkey(
   1106     ///         &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
   1107     ///         PublicKeyCredentialUserEntity {
   1108     ///             name: "bernard.riemann",
   1109     ///             id: &UserHandle64::new(),
   1110     ///             display_name: "Georg Friedrich Bernhard Riemann",
   1111     ///         },
   1112     ///         Vec::new()
   1113     ///     ).start_ceremony()?.0.expiration() > Instant::now()
   1114     /// );
   1115     /// # Ok::<_, webauthn_rp::AggErr>(())
   1116     /// ```
   1117     #[inline]
   1118     pub fn start_ceremony(
   1119         mut self,
   1120     ) -> Result<
   1121         (
   1122             RegistrationServerState<USER_LEN>,
   1123             RegistrationClientState<
   1124                 'rp_id,
   1125                 'user_name,
   1126                 'user_display_name,
   1127                 'user_id,
   1128                 'prf_first,
   1129                 'prf_second,
   1130                 USER_LEN,
   1131             >,
   1132         ),
   1133         CreationOptionsErr,
   1134     > {
   1135         let extensions = self.public_key.extensions.into();
   1136         validate_options_helper(self.public_key.authenticator_selection, extensions).and_then(
   1137             |()| {
   1138                 match self
   1139                     .public_key
   1140                     .authenticator_selection
   1141                     .authenticator_attachment
   1142                 {
   1143                     AuthenticatorAttachment::None => Ok(()),
   1144                     AuthenticatorAttachment::Platform => {
   1145                         if self.public_key.hints.contains_cross_platform_hints() {
   1146                             Err(CreationOptionsErr::HintsIncompatibleWithAuthAttachment)
   1147                         } else {
   1148                             Ok(())
   1149                         }
   1150                     }
   1151                     AuthenticatorAttachment::CrossPlatform => {
   1152                         if self.public_key.hints.contains_platform_hints() {
   1153                             Err(CreationOptionsErr::HintsIncompatibleWithAuthAttachment)
   1154                         } else {
   1155                             Ok(())
   1156                         }
   1157                     }
   1158                 }
   1159                 .and_then(|()| {
   1160                     #[cfg(not(feature = "serializable_server_state"))]
   1161                     let now = Instant::now();
   1162                     #[cfg(feature = "serializable_server_state")]
   1163                     let now = SystemTime::now();
   1164                     now.checked_add(Duration::from_millis(
   1165                         NonZeroU64::from(self.public_key.timeout).get(),
   1166                     ))
   1167                     .ok_or(CreationOptionsErr::InvalidTimeout)
   1168                     .map(|expiration| {
   1169                         // We remove duplicates. The order has no significance, so this is OK.
   1170                         self.public_key
   1171                             .exclude_credentials
   1172                             .sort_unstable_by(|a, b| a.id.as_ref().cmp(b.id.as_ref()));
   1173                         self.public_key
   1174                             .exclude_credentials
   1175                             .dedup_by(|a, b| a.id.as_ref() == b.id.as_ref());
   1176                         (
   1177                             RegistrationServerState {
   1178                                 mediation: self.mediation,
   1179                                 challenge: SentChallenge(self.public_key.challenge.0),
   1180                                 pub_key_cred_params: self.public_key.pub_key_cred_params,
   1181                                 authenticator_selection: self.public_key.authenticator_selection,
   1182                                 extensions,
   1183                                 expiration,
   1184                                 user_id: *self.public_key.user.id,
   1185                             },
   1186                             RegistrationClientState(self),
   1187                         )
   1188                     })
   1189                 })
   1190             },
   1191         )
   1192     }
   1193 }
   1194 /// `CredentialCreationOptions` based on a [`UserHandle64`].
   1195 pub type CredentialCreationOptions64<
   1196     'rp_id,
   1197     'user_name,
   1198     'user_display_name,
   1199     'user_id,
   1200     'prf_first,
   1201     'prf_second,
   1202 > = CredentialCreationOptions<
   1203     'rp_id,
   1204     'user_name,
   1205     'user_display_name,
   1206     'user_id,
   1207     'prf_first,
   1208     'prf_second,
   1209     USER_HANDLE_MAX_LEN,
   1210 >;
   1211 /// `CredentialCreationOptions` based on a [`UserHandle16`].
   1212 pub type CredentialCreationOptions16<
   1213     'rp_id,
   1214     'user_name,
   1215     'user_display_name,
   1216     'user_id,
   1217     'prf_first,
   1218     'prf_second,
   1219 > = CredentialCreationOptions<
   1220     'rp_id,
   1221     'user_name,
   1222     'user_display_name,
   1223     'user_id,
   1224     'prf_first,
   1225     'prf_second,
   1226     16,
   1227 >;
   1228 /// The [`PublicKeyCredentialCreationOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialcreationoptions)
   1229 /// to send to the client when registering a new credential.
   1230 #[derive(Debug)]
   1231 pub struct PublicKeyCredentialCreationOptions<
   1232     'rp_id,
   1233     'user_name,
   1234     'user_display_name,
   1235     'user_id,
   1236     'prf_first,
   1237     'prf_second,
   1238     const USER_LEN: usize,
   1239 > {
   1240     /// [`rp`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-rp).
   1241     pub rp_id: &'rp_id RpId,
   1242     /// [`user`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-user).
   1243     pub user: PublicKeyCredentialUserEntity<'user_name, 'user_display_name, 'user_id, USER_LEN>,
   1244     /// [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-challenge).
   1245     pub challenge: Challenge,
   1246     /// [`pubKeyCredParams`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-pubkeycredparams).
   1247     pub pub_key_cred_params: CoseAlgorithmIdentifiers,
   1248     /// [`timeout`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-timeout).
   1249     ///
   1250     /// Note we require a positive value despite the spec allowing an optional nonnegative value. This jives
   1251     /// with the fact that in-memory storage is required when `serializable_server_state` is not enabled
   1252     /// when attesting credentials as no timeout would make out-of-memory (OOM) conditions more likely.
   1253     pub timeout: NonZeroU32,
   1254     /// [`excludeCredentials`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-excludecredentials).
   1255     pub exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   1256     /// [`authenticatorSelection`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-authenticatorselection).
   1257     pub authenticator_selection: AuthenticatorSelectionCriteria,
   1258     /// [`hints`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-hints).
   1259     pub hints: Hints,
   1260     /// [`extensions`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-extensions).
   1261     pub extensions: Extension<'prf_first, 'prf_second>,
   1262 }
   1263 impl<'rp_id, 'user_name, 'user_display_name, 'user_id, const USER_LEN: usize>
   1264     PublicKeyCredentialCreationOptions<
   1265         'rp_id,
   1266         'user_name,
   1267         'user_display_name,
   1268         'user_id,
   1269         '_,
   1270         '_,
   1271         USER_LEN,
   1272     >
   1273 {
   1274     /// Most deployments of passkeys should use this function. Specifically deployments that are both userless and
   1275     /// passwordless and desire multi-factor authentication (MFA) to be done entirely on the authenticator. It
   1276     /// is important `exclude_credentials` contains the information for _all_ [`RegisteredCredential`]s registered to
   1277     /// [`PublicKeyCredentialUserEntity::id`] to avoid accidentally overwriting existing credentials that
   1278     /// have been previously registered.
   1279     ///
   1280     /// Creates a `PublicKeyCredentialCreationOptions` that requires the authenticator to create a client-side
   1281     /// discoverable credential enforcing any form of user verification. [`Self::timeout`] is [`FIVE_MINUTES`].
   1282     /// [`Extension::cred_protect`] with [`CredProtect::UserVerificationRequired`] with `false` and
   1283     /// [`ExtensionInfo::AllowEnforceValue`] is used.
   1284     ///
   1285     /// # Examples
   1286     ///
   1287     /// ```
   1288     /// # use webauthn_rp::request::{
   1289     /// #     register::{
   1290     /// #         PublicKeyCredentialCreationOptions, PublicKeyCredentialUserEntity, UserHandle64
   1291     /// #     },
   1292     /// #     AsciiDomain, RpId, UserVerificationRequirement
   1293     /// # };
   1294     /// assert!(matches!(
   1295     ///     PublicKeyCredentialCreationOptions::passkey(
   1296     ///         &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
   1297     ///         PublicKeyCredentialUserEntity {
   1298     ///             name: "archimedes.of.syracuse",
   1299     ///             id: &UserHandle64::new(),
   1300     ///             display_name: "Αρχιμήδης ο Συρακούσιος",
   1301     ///         },
   1302     ///         Vec::new()
   1303     ///     )
   1304     ///     .authenticator_selection.user_verification, UserVerificationRequirement::Required
   1305     /// ));
   1306     /// # Ok::<_, webauthn_rp::AggErr>(())
   1307     /// ```
   1308     #[expect(single_use_lifetimes, reason = "false positive")]
   1309     #[inline]
   1310     #[must_use]
   1311     pub fn passkey<'a: 'rp_id, 'b: 'user_name, 'c: 'user_display_name, 'd: 'user_id>(
   1312         rp_id: &'a RpId,
   1313         user: PublicKeyCredentialUserEntity<'b, 'c, 'd, USER_LEN>,
   1314         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   1315     ) -> Self {
   1316         Self {
   1317             rp_id,
   1318             user,
   1319             challenge: Challenge::new(),
   1320             pub_key_cred_params: CoseAlgorithmIdentifiers::default(),
   1321             timeout: FIVE_MINUTES,
   1322             exclude_credentials,
   1323             authenticator_selection: AuthenticatorSelectionCriteria::passkey(),
   1324             hints: Hints::EMPTY,
   1325             extensions: Extension {
   1326                 cred_props: None,
   1327                 cred_protect: CredProtect::UserVerificationRequired(
   1328                     false,
   1329                     ExtensionInfo::AllowEnforceValue,
   1330                 ),
   1331                 min_pin_length: None,
   1332                 prf: None,
   1333             },
   1334         }
   1335     }
   1336     /// Deployments that want to incorporate a "something a user has" factor into a larger multi-factor
   1337     /// authentication (MFA) setup. Specifically deployments that are _not_ userless or passwordless. It
   1338     /// is important `exclude_credentials` contains the information for _all_ [`RegisteredCredential`]s registered
   1339     /// to [`PublicKeyCredentialUserEntity::id`] to avoid accidentally overwriting existing credentials that
   1340     /// have been previously registered.
   1341     ///
   1342     /// Creates a `PublicKeyCredentialCreationOptions` that prefers the authenticator to create a server-side
   1343     /// credential without requiring user verification. [`Self::timeout`] is [`FIVE_MINUTES`].
   1344     /// [`Extension::cred_props`] is [`ExtensionReq::Allow`]. [`Extension::cred_protect`] is
   1345     /// [`CredProtect::UserVerificationOptionalWithCredentialIdList`] with `false` and
   1346     /// [`ExtensionInfo::AllowEnforceValue`].
   1347     ///
   1348     /// Note some authenticators require user verification during credential registration (e.g.,
   1349     /// [CTAP 2.0 authenticators](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#authenticatorMakeCredential)).
   1350     /// When an authenticator supports both CTAP 2.0 and
   1351     /// [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)
   1352     /// protocols, user agents will sometimes fall back to U2F when [`UserVerificationRequirement::Discouraged`]
   1353     /// is requested since the latter allows for registration without user verification. If the user agent does not
   1354     /// do this, then users will have an inconsistent experience when authenticating an already-registered
   1355     /// credential. If this is undesirable, one can use [`UserVerificationRequirement::Required`] for this and
   1356     /// [`PublicKeyCredentialRequestOptions::user_verification`] at the expense of requiring a user to verify
   1357     /// themselves twice: once for the first factor and again here.
   1358     ///
   1359     /// # Examples
   1360     ///
   1361     /// ```
   1362     /// # use webauthn_rp::request::{register::{
   1363     /// #     PublicKeyCredentialCreationOptions, PublicKeyCredentialUserEntity, UserHandle64
   1364     /// # }, AsciiDomain, RpId};
   1365     /// assert_eq!(
   1366     ///     PublicKeyCredentialCreationOptions::second_factor(
   1367     ///         &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
   1368     ///         PublicKeyCredentialUserEntity {
   1369     ///             name: "carl.gauss",
   1370     ///             id: &UserHandle64::new(),
   1371     ///             display_name: "Johann Carl Friedrich Gauß",
   1372     ///         },
   1373     ///         Vec::new()
   1374     ///     )
   1375     ///     .timeout
   1376     ///     .get(),
   1377     ///     300_000
   1378     /// );
   1379     /// # Ok::<_, webauthn_rp::AggErr>(())
   1380     /// ```
   1381     #[expect(single_use_lifetimes, reason = "false positive")]
   1382     #[inline]
   1383     #[must_use]
   1384     pub fn second_factor<'a: 'rp_id, 'b: 'user_name, 'c: 'user_display_name, 'd: 'user_id>(
   1385         rp_id: &'a RpId,
   1386         user: PublicKeyCredentialUserEntity<'b, 'c, 'd, USER_LEN>,
   1387         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   1388     ) -> Self {
   1389         let mut opts = Self::passkey(rp_id, user, exclude_credentials);
   1390         opts.authenticator_selection = AuthenticatorSelectionCriteria::second_factor();
   1391         opts.extensions.cred_props = Some(ExtensionReq::Allow);
   1392         opts.extensions.cred_protect = CredProtect::UserVerificationOptionalWithCredentialIdList(
   1393             false,
   1394             ExtensionInfo::AllowEnforceValue,
   1395         );
   1396         opts
   1397     }
   1398 }
   1399 /// `PublicKeyCredentialCreationOptions` based on a [`UserHandle64`].
   1400 pub type PublicKeyCredentialCreationOptions64<
   1401     'rp_id,
   1402     'user_name,
   1403     'user_display_name,
   1404     'user_id,
   1405     'prf_first,
   1406     'prf_second,
   1407 > = PublicKeyCredentialCreationOptions<
   1408     'rp_id,
   1409     'user_name,
   1410     'user_display_name,
   1411     'user_id,
   1412     'prf_first,
   1413     'prf_second,
   1414     USER_HANDLE_MAX_LEN,
   1415 >;
   1416 /// `PublicKeyCredentialCreationOptions` based on a [`UserHandle16`].
   1417 pub type PublicKeyCredentialCreationOptions16<
   1418     'rp_id,
   1419     'user_name,
   1420     'user_display_name,
   1421     'user_id,
   1422     'prf_first,
   1423     'prf_second,
   1424 > = PublicKeyCredentialCreationOptions<
   1425     'rp_id,
   1426     'user_name,
   1427     'user_display_name,
   1428     'user_id,
   1429     'prf_first,
   1430     'prf_second,
   1431     16,
   1432 >;
   1433 /// Container of a [`CredentialCreationOptions`] that has been used to start the registration ceremony.
   1434 /// This gets sent to the client ASAP.
   1435 #[derive(Debug)]
   1436 pub struct RegistrationClientState<
   1437     'rp_id,
   1438     'user_name,
   1439     'user_display_name,
   1440     'user_id,
   1441     'prf_first,
   1442     'prf_second,
   1443     const USER_LEN: usize,
   1444 >(
   1445     CredentialCreationOptions<
   1446         'rp_id,
   1447         'user_name,
   1448         'user_display_name,
   1449         'user_id,
   1450         'prf_first,
   1451         'prf_second,
   1452         USER_LEN,
   1453     >,
   1454 );
   1455 impl<
   1456     'rp_id,
   1457     'user_name,
   1458     'user_display_name,
   1459     'user_id,
   1460     'prf_first,
   1461     'prf_second,
   1462     const USER_LEN: usize,
   1463 >
   1464     RegistrationClientState<
   1465         'rp_id,
   1466         'user_name,
   1467         'user_display_name,
   1468         'user_id,
   1469         'prf_first,
   1470         'prf_second,
   1471         USER_LEN,
   1472     >
   1473 {
   1474     /// Returns the `CredentialCreationOptions` that was used to start a registration ceremony.
   1475     ///
   1476     /// # Examples
   1477     ///
   1478     /// ```
   1479     /// # use webauthn_rp::request::{register::{
   1480     /// #     CoseAlgorithmIdentifiers, CredentialCreationOptions,
   1481     /// #     PublicKeyCredentialUserEntity, UserHandle64,
   1482     /// # }, AsciiDomain, RpId};
   1483     /// assert_eq!(
   1484     ///     CredentialCreationOptions::passkey(
   1485     ///         &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
   1486     ///         PublicKeyCredentialUserEntity {
   1487     ///             name: "david.hilbert",
   1488     ///             id: &UserHandle64::new(),
   1489     ///             display_name: "David Hilbert",
   1490     ///         },
   1491     ///         Vec::new()
   1492     ///     )
   1493     ///     .start_ceremony()?
   1494     ///     .1
   1495     ///     .options()
   1496     ///     .public_key
   1497     ///     .rp_id.as_ref(),
   1498     ///     "example.com"
   1499     /// );
   1500     /// # Ok::<_, webauthn_rp::AggErr>(())
   1501     /// ```
   1502     #[inline]
   1503     #[must_use]
   1504     pub const fn options(
   1505         &self,
   1506     ) -> &CredentialCreationOptions<
   1507         'rp_id,
   1508         'user_name,
   1509         'user_display_name,
   1510         'user_id,
   1511         'prf_first,
   1512         'prf_second,
   1513         USER_LEN,
   1514     > {
   1515         &self.0
   1516     }
   1517 }
   1518 /// `RegistrationClientState` based on a [`UserHandle64`].
   1519 pub type RegistrationClientState64<
   1520     'rp_id,
   1521     'user_name,
   1522     'user_display_name,
   1523     'user_id,
   1524     'prf_first,
   1525     'prf_second,
   1526 > = RegistrationClientState<
   1527     'rp_id,
   1528     'user_name,
   1529     'user_display_name,
   1530     'user_id,
   1531     'prf_first,
   1532     'prf_second,
   1533     USER_HANDLE_MAX_LEN,
   1534 >;
   1535 /// `RegistrationClientState` based on a [`UserHandle16`].
   1536 pub type RegistrationClientState16<
   1537     'rp_id,
   1538     'user_name,
   1539     'user_display_name,
   1540     'user_id,
   1541     'prf_first,
   1542     'prf_second,
   1543 > = RegistrationClientState<
   1544     'rp_id,
   1545     'user_name,
   1546     'user_display_name,
   1547     'user_id,
   1548     'prf_first,
   1549     'prf_second,
   1550     16,
   1551 >;
   1552 /// Additional verification options to perform in [`RegistrationServerState::verify`].
   1553 #[derive(Clone, Copy, Debug)]
   1554 pub struct RegistrationVerificationOptions<'origins, 'top_origins, O, T> {
   1555     /// Origins to use for [origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin).
   1556     ///
   1557     /// When this is empty, the origin that will be used will be based on
   1558     /// the [`RpId`] passed to [`RegistrationServerState::verify`]. If [`RpId::Domain`] or [`RpId::StaticDomain`],
   1559     /// then the [`DomainOrigin`] returned from passing [`AsciiDomain::as_ref`] and [`AsciiDomainStatic::as_str`]
   1560     /// to [`DomainOrigin::new`] respectively will be used; otherwise the [`Url`] in [`RpId::Url`] will be used.
   1561     pub allowed_origins: &'origins [O],
   1562     /// [Top-level origins](https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-top-level-origin)
   1563     /// to use for [origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin).
   1564     ///
   1565     /// When this is `Some`, [`CollectedClientData::cross_origin`] is allowed to be `true`. When the contained
   1566     /// `slice` is empty, [`CollectedClientData::top_origin`] must be `None`. When this is `None`,
   1567     /// `CollectedClientData::cross_origin` must be `false` and `CollectedClientData::top_origin` must be `None`.
   1568     pub allowed_top_origins: Option<&'top_origins [T]>,
   1569     /// The required [`Backup`] state of the credential.
   1570     pub backup_requirement: BackupReq,
   1571     /// Error when unsolicited extensions are sent back iff `true`.
   1572     pub error_on_unsolicited_extensions: bool,
   1573     /// [`AuthenticatorAttachment`] must be sent iff `true`.
   1574     pub require_authenticator_attachment: bool,
   1575     /// [`CollectedClientData::from_client_data_json_relaxed`] is used to extract [`CollectedClientData`] iff `true`.
   1576     #[cfg(feature = "serde_relaxed")]
   1577     pub client_data_json_relaxed: bool,
   1578 }
   1579 impl<O, T> RegistrationVerificationOptions<'_, '_, O, T> {
   1580     /// Returns `Self` such that [`Self::allowed_origins`] is empty, [`Self::allowed_top_origins`] is `None`,
   1581     /// [`Self::backup_requirement`] is [`BackupReq::None`], [`Self::error_on_unsolicited_extensions`] is `true`,
   1582     /// [`Self::require_authenticator_attachment`] is `false`, and [`Self::client_data_json_relaxed`] is
   1583     /// `true`.
   1584     ///
   1585     /// Note `O` and `T` should implement `PartialEq<Origin<'_>>` (e.g., `&str`).
   1586     #[inline]
   1587     #[must_use]
   1588     pub const fn new() -> Self {
   1589         Self {
   1590             allowed_origins: [].as_slice(),
   1591             allowed_top_origins: None,
   1592             backup_requirement: BackupReq::None,
   1593             error_on_unsolicited_extensions: true,
   1594             require_authenticator_attachment: false,
   1595             #[cfg(feature = "serde_relaxed")]
   1596             client_data_json_relaxed: true,
   1597         }
   1598     }
   1599 }
   1600 impl<O, T> Default for RegistrationVerificationOptions<'_, '_, O, T> {
   1601     /// Same as [`Self::new`].
   1602     #[inline]
   1603     fn default() -> Self {
   1604         Self {
   1605             allowed_origins: &[],
   1606             allowed_top_origins: None,
   1607             backup_requirement: BackupReq::default(),
   1608             error_on_unsolicited_extensions: true,
   1609             require_authenticator_attachment: false,
   1610             #[cfg(feature = "serde_relaxed")]
   1611             client_data_json_relaxed: true,
   1612         }
   1613     }
   1614 }
   1615 /// `PrfInput` without the actual data sent to reduce memory usage when storing [`RegistrationServerState`] in an
   1616 /// in-memory collection.
   1617 #[derive(Clone, Copy, Debug)]
   1618 enum ServerPrfInfo {
   1619     /// No `PrfInput`.
   1620     None,
   1621     /// `PrfInput::second` was `None`.
   1622     One(ExtensionInfo),
   1623     /// `PrfInput::second` was `Some`.
   1624     Two(ExtensionInfo),
   1625 }
   1626 #[cfg(test)]
   1627 impl PartialEq for ServerPrfInfo {
   1628     fn eq(&self, other: &Self) -> bool {
   1629         match *self {
   1630             Self::None => matches!(*other, Self::None),
   1631             Self::One(info) => matches!(*other, Self::One(info2) if info == info2),
   1632             Self::Two(info) => matches!(*other, Self::Two(info2) if info == info2),
   1633         }
   1634     }
   1635 }
   1636 impl From<Option<(PrfInput<'_, '_>, ExtensionInfo)>> for ServerPrfInfo {
   1637     fn from(value: Option<(PrfInput<'_, '_>, ExtensionInfo)>) -> Self {
   1638         value.map_or(Self::None, |val| {
   1639             val.0
   1640                 .second
   1641                 .map_or_else(|| Self::One(val.1), |_| Self::Two(val.1))
   1642         })
   1643     }
   1644 }
   1645 /// `Extension` without the actual data sent to reduce memory usage when storing [`AuthenticationServerState`]
   1646 /// in an in-memory collection.
   1647 #[derive(Clone, Copy, Debug)]
   1648 struct ServerExtensionInfo {
   1649     /// `Extension::cred_props`.
   1650     cred_props: Option<ExtensionReq>,
   1651     /// `Extension::cred_protect`.
   1652     cred_protect: CredProtect,
   1653     /// `Extension::min_pin_length`.
   1654     min_pin_length: Option<(FourToSixtyThree, ExtensionInfo)>,
   1655     /// `Extension::prf`.
   1656     prf: ServerPrfInfo,
   1657 }
   1658 impl ServerExtensionInfo {
   1659     /// Validates the extensions.
   1660     fn validate(
   1661         self,
   1662         client_ext: ClientExtensionsOutputs,
   1663         auth_ext: AuthenticatorExtensionOutput,
   1664         error_unsolicited: bool,
   1665     ) -> Result<(), ExtensionErr> {
   1666         if error_unsolicited {
   1667             self.validate_unsolicited(client_ext, auth_ext)
   1668         } else {
   1669             Ok(())
   1670         }
   1671         .and_then(|()| {
   1672             self.validate_required(client_ext, auth_ext)
   1673                 .and_then(|()| self.validate_value(client_ext, auth_ext))
   1674         })
   1675     }
   1676     /// Validates if there are any unsolicited extensions.
   1677     ///
   1678     /// Note no distinction is made between an extension that is empty and one that is not (i.e., we are checking
   1679     ///  purely for the existence of extension keys).
   1680     fn validate_unsolicited(
   1681         mut self,
   1682         client_ext: ClientExtensionsOutputs,
   1683         auth_ext: AuthenticatorExtensionOutput,
   1684     ) -> Result<(), ExtensionErr> {
   1685         // For simpler code, we artificially set non-requested extensions after verifying there was not an error
   1686         // and recursively call this function. There are so few extensions and the checks are fast that there
   1687         // should be no worry of stack overflow or performance overhead.
   1688         if self.cred_props.is_some() {
   1689             if !matches!(self.cred_protect, CredProtect::None) {
   1690                 if self.min_pin_length.is_some() {
   1691                     // This is the last extension, so recursion stops here.
   1692                     if !matches!(self.prf, ServerPrfInfo::None) {
   1693                         Ok(())
   1694                     } else if client_ext.prf.is_some() {
   1695                         Err(ExtensionErr::ForbiddenPrf)
   1696                     } else if !matches!(auth_ext.hmac_secret, HmacSecret::None) {
   1697                         Err(ExtensionErr::ForbiddenHmacSecret)
   1698                     } else {
   1699                         Ok(())
   1700                     }
   1701                 } else if auth_ext.min_pin_length.is_some() {
   1702                     Err(ExtensionErr::ForbiddenMinPinLength)
   1703                 } else {
   1704                     // Pretend to set `minPinLength`, so we can check `prf`.
   1705                     self.min_pin_length =
   1706                         Some((FourToSixtyThree::Four, ExtensionInfo::RequireEnforceValue));
   1707                     self.validate_unsolicited(client_ext, auth_ext)
   1708                 }
   1709             } else if !matches!(auth_ext.cred_protect, CredentialProtectionPolicy::None) {
   1710                 Err(ExtensionErr::ForbiddenCredProtect)
   1711             } else {
   1712                 // Pretend to set `credProtect`, so we can check `minPinLength` and `prf` extensions.
   1713                 self.cred_protect = CredProtect::UserVerificationOptional(
   1714                     false,
   1715                     ExtensionInfo::RequireEnforceValue,
   1716                 );
   1717                 self.validate_unsolicited(client_ext, auth_ext)
   1718             }
   1719         } else if client_ext.cred_props.is_some() {
   1720             Err(ExtensionErr::ForbiddenCredProps)
   1721         } else {
   1722             // Pretend to set `credProps`; so we can check `credProtect`, `minPinLength`, and `prf` extensions.
   1723             self.cred_props = Some(ExtensionReq::Require);
   1724             self.validate_unsolicited(client_ext, auth_ext)
   1725         }
   1726     }
   1727     /// Validates if any required extensions don't have a corresponding response.
   1728     ///
   1729     /// Note empty extensions are treated as missing. For example when requiring the `credProps` extension,
   1730     /// all of the following responses would lead to a failure:
   1731     /// `{"clientExtensionResults":{}}`: no extensions.
   1732     /// `{"clientExtensionResults":{"prf":true}}`: only the `prf` extension.
   1733     /// `{"clientExtensionResults":{"credProps":{}}}`: empty `credProps` extension.
   1734     /// `{"clientExtensionResults":{"credProps":{"foo":false}}}`: `credProps` extension doesn't contain at least one
   1735     /// expected field (i.e., still "empty").
   1736     fn validate_required(
   1737         self,
   1738         client_ext: ClientExtensionsOutputs,
   1739         auth_ext: AuthenticatorExtensionOutput,
   1740     ) -> Result<(), ExtensionErr> {
   1741         // We don't check `self.cred_protect` since `CredProtect::validate` checks for both a required response
   1742         // and value enforcement; thus it only needs to be checked once (which it is in `Self::validate_value`).
   1743         self.cred_props
   1744             .map_or(Ok(()), |info| {
   1745                 if matches!(info, ExtensionReq::Require) {
   1746                     if client_ext
   1747                         .cred_props
   1748                         .is_some_and(|props| props.rk.is_some())
   1749                     {
   1750                         Ok(())
   1751                     } else {
   1752                         Err(ExtensionErr::MissingCredProps)
   1753                     }
   1754                 } else {
   1755                     Ok(())
   1756                 }
   1757             })
   1758             .and_then(|()| {
   1759                 self.min_pin_length
   1760                     .map_or(Ok(()), |info| {
   1761                         if matches!(
   1762                             info.1,
   1763                             ExtensionInfo::RequireEnforceValue
   1764                                 | ExtensionInfo::RequireDontEnforceValue
   1765                         ) {
   1766                             auth_ext
   1767                                 .min_pin_length
   1768                                 .ok_or(ExtensionErr::MissingMinPinLength)
   1769                                 .map(|_| ())
   1770                         } else {
   1771                             Ok(())
   1772                         }
   1773                     })
   1774                     .and_then(|()| match self.prf {
   1775                         ServerPrfInfo::None => Ok(()),
   1776                         ServerPrfInfo::One(info) | ServerPrfInfo::Two(info) => {
   1777                             if matches!(
   1778                                 info,
   1779                                 ExtensionInfo::RequireEnforceValue
   1780                                     | ExtensionInfo::RequireDontEnforceValue
   1781                             ) {
   1782                                 if client_ext.prf.is_some() {
   1783                                     Ok(())
   1784                                 } else {
   1785                                     Err(ExtensionErr::MissingPrf)
   1786                                 }
   1787                             } else {
   1788                                 Ok(())
   1789                             }
   1790                         }
   1791                     })
   1792             })
   1793     }
   1794     /// Validates the value of any extensions sent from the client.
   1795     ///
   1796     /// Note missing and empty extensions are always OK.
   1797     fn validate_value(
   1798         self,
   1799         client_ext: ClientExtensionsOutputs,
   1800         auth_ext: AuthenticatorExtensionOutput,
   1801     ) -> Result<(), ExtensionErr> {
   1802         // This also checks for a missing response. Instead of duplicating that check, we only call
   1803         // `self.cred_protect.validate` once here and not also in `Self::validate_required`.
   1804         self.cred_protect
   1805             .validate(auth_ext.cred_protect)
   1806             .and_then(|()| {
   1807                 self.min_pin_length
   1808                     .map_or(Ok(()), |info| {
   1809                         if matches!(
   1810                             info.1,
   1811                             ExtensionInfo::RequireEnforceValue | ExtensionInfo::AllowEnforceValue
   1812                         ) {
   1813                             auth_ext.min_pin_length.map_or(Ok(()), |pin| {
   1814                                 if pin >= info.0 {
   1815                                     Ok(())
   1816                                 } else {
   1817                                     Err(ExtensionErr::InvalidMinPinLength(info.0, pin))
   1818                                 }
   1819                             })
   1820                         } else {
   1821                             Ok(())
   1822                         }
   1823                     })
   1824                     .and_then(|()| match self.prf {
   1825                         ServerPrfInfo::None => Ok(()),
   1826                         ServerPrfInfo::One(info) | ServerPrfInfo::Two(info) => {
   1827                             if matches!(
   1828                                 info,
   1829                                 ExtensionInfo::RequireEnforceValue
   1830                                     | ExtensionInfo::AllowEnforceValue
   1831                             ) {
   1832                                 client_ext
   1833                                     .prf
   1834                                     .map_or(Ok(()), |prf| {
   1835                                         if prf.enabled {
   1836                                             Ok(())
   1837                                         } else {
   1838                                             Err(ExtensionErr::InvalidPrfValue)
   1839                                         }
   1840                                     })
   1841                                     .and({
   1842                                         if matches!(auth_ext.hmac_secret, HmacSecret::NotEnabled) {
   1843                                             Err(ExtensionErr::InvalidHmacSecretValue)
   1844                                         } else {
   1845                                             Ok(())
   1846                                         }
   1847                                     })
   1848                             } else {
   1849                                 Ok(())
   1850                             }
   1851                         }
   1852                     })
   1853             })
   1854     }
   1855 }
   1856 impl From<Extension<'_, '_>> for ServerExtensionInfo {
   1857     fn from(value: Extension<'_, '_>) -> Self {
   1858         Self {
   1859             cred_props: value.cred_props,
   1860             cred_protect: value.cred_protect,
   1861             min_pin_length: value.min_pin_length,
   1862             prf: value.prf.into(),
   1863         }
   1864     }
   1865 }
   1866 #[cfg(test)]
   1867 impl PartialEq for ServerExtensionInfo {
   1868     fn eq(&self, other: &Self) -> bool {
   1869         self.prf == other.prf
   1870     }
   1871 }
   1872 // This is essentially the `PublicKeyCredentialCreationOptions` used to create it; however to reduce
   1873 // memory usage, we remove all unnecessary data making an instance of this 48 bytes in size on
   1874 // `x86_64-unknown-linux-gnu` platforms when `USER_LEN` is `USER_HANDLE_MIN_LEN`.
   1875 /// State needed to be saved when beginning the registration ceremony.
   1876 ///
   1877 /// Saves the necessary information associated with the [`CredentialCreationOptions`] used to create it
   1878 /// via [`CredentialCreationOptions::start_ceremony`] so that registration of a new credential can be
   1879 /// performed with [`Self::verify`].
   1880 ///
   1881 /// `RegistrationServerState` implements [`Borrow`] of [`SentChallenge`]; thus to obtain the correct
   1882 /// `RegistrationServerState` associated with a [`Registration`], one should use its corresponding
   1883 /// [`Registration::challenge`].
   1884 #[derive(Debug)]
   1885 pub struct RegistrationServerState<const USER_LEN: usize> {
   1886     /// [`mediation`](https://www.w3.org/TR/credential-management-1/#dom-credentialcreationoptions-mediation).
   1887     mediation: CredentialMediationRequirement,
   1888     // This is a `SentChallenge` since we need `RegistrationServerState` to be fetchable after receiving the
   1889     // response from the client. This response must obviously be constructable; thus its challenge is a
   1890     // `SentChallenge`.
   1891     //
   1892     // This must never be mutated since we want to ensure it is actually a `Challenge` (which
   1893     // can only be constructed via `Challenge::new`). This is guaranteed to be true iff
   1894     // `serializable_server_state` is not enabled. We avoid implementing `trait`s like `Hash` when that
   1895     // is enabled.
   1896     /// [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-challenge).
   1897     challenge: SentChallenge,
   1898     /// [`pubKeyCredParams`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-pubkeycredparams).
   1899     pub_key_cred_params: CoseAlgorithmIdentifiers,
   1900     /// [`authenticatorSelection`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-authenticatorselection).
   1901     authenticator_selection: AuthenticatorSelectionCriteria,
   1902     /// [`extensions`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-extensions).
   1903     extensions: ServerExtensionInfo,
   1904     /// `Instant` the ceremony expires.
   1905     #[cfg(not(feature = "serializable_server_state"))]
   1906     expiration: Instant,
   1907     /// `SystemTime` the ceremony expires.
   1908     #[cfg(feature = "serializable_server_state")]
   1909     expiration: SystemTime,
   1910     /// User handle.
   1911     user_id: UserHandle<USER_LEN>,
   1912 }
   1913 impl<const USER_LEN: usize> RegistrationServerState<USER_LEN> {
   1914     #[cfg(all(test, feature = "custom", feature = "serializable_server_state"))]
   1915     fn is_eq(&self, other: &Self) -> bool {
   1916         self.mediation == other.mediation
   1917             && self.challenge == other.challenge
   1918             && self.pub_key_cred_params == other.pub_key_cred_params
   1919             && self.authenticator_selection == other.authenticator_selection
   1920             && self.extensions == other.extensions
   1921             && self.expiration == other.expiration
   1922             && self.user_id == other.user_id
   1923     }
   1924     /// Verifies `response` is valid based on `self` consuming `self` and returning a `RegisteredCredential` that
   1925     /// borrows the necessary data from `response`.
   1926     ///
   1927     /// `rp_id` MUST be the same as the [`PublicKeyCredentialCreationOptions::rp_id`] used when starting the
   1928     /// ceremony.
   1929     ///
   1930     /// It is _essential_ to ensure [`RegisteredCredential::id`] has not been previously registered; if
   1931     /// so, the ceremony SHOULD be aborted and a failure reported. When saving `RegisteredCredential`, one may
   1932     /// want to save the [`RpId`] and [`PublicKeyCredentialUserEntity`] information; however since [`RpId`] is
   1933     /// likely static, that may not be necessary. User information is also likely static for a given [`UserHandle`]
   1934     /// (which is saved in `RegisteredCredential`); so if such info is saved, one may want to save it once per
   1935     /// `UserHandle` and not per `RegisteredCredential`.
   1936     ///
   1937     /// # Errors
   1938     ///
   1939     /// Errors iff `response` is not valid according to the
   1940     /// [registration ceremony criteria](https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential)
   1941     /// or violates any of the settings in `options`.
   1942     #[inline]
   1943     pub fn verify<'a, O: PartialEq<Origin<'a>>, T: PartialEq<Origin<'a>>>(
   1944         self,
   1945         rp_id: &RpId,
   1946         response: &'a Registration,
   1947         options: &RegistrationVerificationOptions<'_, '_, O, T>,
   1948     ) -> Result<RegisteredCredential<'a, USER_LEN>, RegCeremonyErr> {
   1949         // [Registration ceremony](https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential)
   1950         // is handled by:
   1951         //
   1952         // 1. Calling code.
   1953         // 2. Client code and the construction of `resp` (hopefully via [`Registration::deserialize`]).
   1954         // 3. Client code and the construction of `resp` (hopefully via [`AuthenticatorAttestation::deserialize`]).
   1955         // 4. Client code and the construction of `resp` (hopefully via [`ClientExtensionsOutputs::deserialize`]).
   1956         // 5. [`Self::partial_validate`].
   1957         // 6. [`Self::partial_validate`].
   1958         // 7. [`Self::partial_validate`].
   1959         // 8. [`Self::partial_validate`].
   1960         // 9. [`Self::partial_validate`].
   1961         // 10. [`Self::partial_validate`].
   1962         // 11. [`Self::partial_validate`].
   1963         // 12. [`Self::partial_validate`].
   1964         // 13. [`Self::partial_validate`].
   1965         // 14. [`Self::partial_validate`].
   1966         // 15. Below.
   1967         // 16. [`Self::partial_validate`].
   1968         // 17. [`Self::partial_validate`].
   1969         // 18. [`Self::partial_validate`].
   1970         // 19. [`Self::partial_validate`].
   1971         // 20. Below.
   1972         // 21. [`Self::partial_validate`].
   1973         // 22. [`Self::partial_validate`].
   1974         // 23. N/A since only none and self attestations are supported.
   1975         // 24. Always satisfied since only none and self attestations are supported (Item 3 is N/A).
   1976         // 25. [`Self::partial_validate`].
   1977         // 26. Calling code.
   1978         // 27. Below.
   1979         // 28. N/A since only none and self attestations are supported.
   1980         // 29. Below.
   1981 
   1982         // Steps 5–14, 16–19, 21–22, and 25.
   1983         self.partial_validate(rp_id, response, (), &options.into())
   1984             .map_err(RegCeremonyErr::from)
   1985             .and_then(|attestation_object| {
   1986                 let auth_data = attestation_object.auth_data();
   1987                 let flags = auth_data.flags();
   1988                 // Step 15.
   1989                 if matches!(self.mediation, CredentialMediationRequirement::Conditional)
   1990                     || flags.user_present
   1991                 {
   1992                     self.authenticator_selection
   1993                         // Verify any required authenticator attachment modality.
   1994                         .validate(
   1995                             options.require_authenticator_attachment,
   1996                             response.authenticator_attachment,
   1997                         )
   1998                         .and_then(|()| {
   1999                             let attested_credential_data = auth_data.attested_credential_data();
   2000                             self.pub_key_cred_params
   2001                                 // Step 20.
   2002                                 .validate(attested_credential_data.credential_public_key)
   2003                                 .and_then(|()| {
   2004                                     let extensions = auth_data.extensions();
   2005                                     // Step 27.
   2006                                     self.extensions
   2007                                         .validate(
   2008                                             response.client_extension_results,
   2009                                             extensions,
   2010                                             options.error_on_unsolicited_extensions,
   2011                                         )
   2012                                         .map_err(RegCeremonyErr::Extension)
   2013                                         .and_then(|()| {
   2014                                             // Step 29.
   2015                                             RegisteredCredential::new(
   2016                                                 attested_credential_data.credential_id,
   2017                                                 response.response.transports(),
   2018                                                 self.user_id,
   2019                                                 StaticState {
   2020                                                     credential_public_key: attested_credential_data
   2021                                                         .credential_public_key,
   2022                                                     extensions: extensions.into(),
   2023                                                     client_extension_results: response
   2024                                                         .client_extension_results
   2025                                                         .into(),
   2026                                                 },
   2027                                                 DynamicState {
   2028                                                     user_verified: flags.user_verified,
   2029                                                     backup: flags.backup,
   2030                                                     sign_count: auth_data.sign_count(),
   2031                                                     authenticator_attachment: response
   2032                                                         .authenticator_attachment,
   2033                                                 },
   2034                                                 Metadata {
   2035                                                     attestation: match attestation_object
   2036                                                         .attestation()
   2037                                                     {
   2038                                                         AttestationFormat::None => {
   2039                                                             Attestation::None
   2040                                                         }
   2041                                                         AttestationFormat::Packed(_) => {
   2042                                                             Attestation::Surrogate
   2043                                                         }
   2044                                                     },
   2045                                                     aaguid: attested_credential_data.aaguid,
   2046                                                     extensions: extensions.into(),
   2047                                                     client_extension_results: response
   2048                                                         .client_extension_results
   2049                                                         .into(),
   2050                                                     resident_key: self
   2051                                                         .authenticator_selection
   2052                                                         .resident_key,
   2053                                                 },
   2054                                             )
   2055                                             .map_err(RegCeremonyErr::Credential)
   2056                                         })
   2057                                 })
   2058                         })
   2059                 } else {
   2060                     Err(RegCeremonyErr::UserNotPresent)
   2061                 }
   2062             })
   2063     }
   2064 }
   2065 impl<const USER_LEN: usize> TimedCeremony for RegistrationServerState<USER_LEN> {
   2066     #[cfg(any(doc, not(feature = "serializable_server_state")))]
   2067     #[inline]
   2068     fn expiration(&self) -> Instant {
   2069         self.expiration
   2070     }
   2071     #[cfg(all(not(doc), feature = "serializable_server_state"))]
   2072     #[inline]
   2073     fn expiration(&self) -> SystemTime {
   2074         self.expiration
   2075     }
   2076 }
   2077 impl<const USER_LEN: usize> Ceremony<USER_LEN, false> for RegistrationServerState<USER_LEN> {
   2078     type R = Registration;
   2079     fn rand_challenge(&self) -> SentChallenge {
   2080         self.challenge
   2081     }
   2082     #[cfg(not(feature = "serializable_server_state"))]
   2083     fn expiry(&self) -> Instant {
   2084         self.expiration
   2085     }
   2086     #[cfg(feature = "serializable_server_state")]
   2087     fn expiry(&self) -> SystemTime {
   2088         self.expiration
   2089     }
   2090     fn user_verification(&self) -> UserVerificationRequirement {
   2091         self.authenticator_selection.user_verification
   2092     }
   2093 }
   2094 impl<const USER_LEN: usize> Borrow<SentChallenge> for RegistrationServerState<USER_LEN> {
   2095     #[inline]
   2096     fn borrow(&self) -> &SentChallenge {
   2097         &self.challenge
   2098     }
   2099 }
   2100 impl<const USER_LEN: usize> PartialEq for RegistrationServerState<USER_LEN> {
   2101     #[inline]
   2102     fn eq(&self, other: &Self) -> bool {
   2103         self.challenge == other.challenge
   2104     }
   2105 }
   2106 impl<const USER_LEN: usize> PartialEq<&Self> for RegistrationServerState<USER_LEN> {
   2107     #[inline]
   2108     fn eq(&self, other: &&Self) -> bool {
   2109         *self == **other
   2110     }
   2111 }
   2112 impl<const USER_LEN: usize> PartialEq<RegistrationServerState<USER_LEN>>
   2113     for &RegistrationServerState<USER_LEN>
   2114 {
   2115     #[inline]
   2116     fn eq(&self, other: &RegistrationServerState<USER_LEN>) -> bool {
   2117         **self == *other
   2118     }
   2119 }
   2120 impl<const USER_LEN: usize> Eq for RegistrationServerState<USER_LEN> {}
   2121 impl<const USER_LEN: usize> Hash for RegistrationServerState<USER_LEN> {
   2122     #[inline]
   2123     fn hash<H: Hasher>(&self, state: &mut H) {
   2124         self.challenge.hash(state);
   2125     }
   2126 }
   2127 impl<const USER_LEN: usize> PartialOrd for RegistrationServerState<USER_LEN> {
   2128     #[inline]
   2129     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
   2130         Some(self.cmp(other))
   2131     }
   2132 }
   2133 impl<const USER_LEN: usize> Ord for RegistrationServerState<USER_LEN> {
   2134     #[inline]
   2135     fn cmp(&self, other: &Self) -> Ordering {
   2136         self.challenge.cmp(&other.challenge)
   2137     }
   2138 }
   2139 /// `RegistrationServerState` based on a [`UserHandle64`].
   2140 pub type RegistrationServerState64 = RegistrationServerState<USER_HANDLE_MAX_LEN>;
   2141 /// `RegistrationServerState` based on a [`UserHandle16`].
   2142 pub type RegistrationServerState16 = RegistrationServerState<16>;