webauthn_rp

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

register.rs (142830B)


      1 extern crate alloc;
      2 use super::{
      3     super::{
      4         DynamicState, Metadata, RegisteredCredential, StaticState,
      5         response::{
      6             AuthenticatorAttachment,
      7             register::{
      8                 Attestation, AttestationFormat, AuthenticatorExtensionOutput,
      9                 ClientExtensionsOutputs, CredentialProtectionPolicy, HmacSecret, Registration,
     10                 UncompressedPubKey,
     11                 error::{ExtensionErr, RegCeremonyErr},
     12             },
     13         },
     14     },
     15     BackupReq, Ceremony, Challenge, CredentialMediationRequirement, ExtensionInfo, ExtensionReq,
     16     FIVE_MINUTES, Hint, Origin, PrfInput, PublicKeyCredentialDescriptor, RpId, SentChallenge,
     17     TimedCeremony, UserVerificationRequirement,
     18     register::error::{CreationOptionsErr, NicknameErr, UsernameErr},
     19 };
     20 #[cfg(doc)]
     21 use crate::{
     22     request::{
     23         AsciiDomain, AsciiDomainStatic, DomainOrigin, Url,
     24         auth::{AuthenticationVerificationOptions, PublicKeyCredentialRequestOptions},
     25     },
     26     response::{AuthTransports, AuthenticatorTransport, Backup, CollectedClientData, Flag},
     27 };
     28 use alloc::borrow::Cow;
     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 use precis_profiles::{UsernameCasePreserved, precis_core::profile::Profile as _};
     39 #[cfg(any(doc, not(feature = "serializable_server_state")))]
     40 use std::time::Instant;
     41 #[cfg(any(doc, feature = "serializable_server_state"))]
     42 use std::time::SystemTime;
     43 /// Contains functionality to (de)serialize data to a data store.
     44 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))]
     45 #[cfg(feature = "bin")]
     46 pub mod bin;
     47 /// Contains functionality that needs to be accessible when `bin` or `serde` are not enabled.
     48 #[cfg_attr(docsrs, doc(cfg(feature = "custom")))]
     49 #[cfg(feature = "custom")]
     50 mod custom;
     51 /// Contains error types.
     52 pub mod error;
     53 /// Contains functionality to (de)serialize data to a client.
     54 #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
     55 #[cfg(feature = "serde")]
     56 pub mod ser;
     57 /// Contains functionality to (de)serialize [`RegistrationServerState`] to a data store.
     58 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))]
     59 #[cfg(feature = "serializable_server_state")]
     60 pub mod ser_server_state;
     61 /// Used by [`Extension::cred_protect`] to enforce the [`CredentialProtectionPolicy`] sent by the client via
     62 /// [`Registration`].
     63 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
     64 pub enum CredProtect {
     65     /// No `credProtect` request.
     66     #[default]
     67     None,
     68     /// Request
     69     /// [`userVerificationOptional`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#userverificationoptional)
     70     /// but allow any.
     71     ///
     72     /// The `bool` corresponds to
     73     /// [`enforceCredentialProtectionPolicy`](https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#dom-authenticationextensionsclientinputs-enforcecredentialprotectionpolicy).
     74     UserVerificationOptional(bool, ExtensionInfo),
     75     /// Request
     76     /// [`userVerificationOptionalWithCredentialIDList`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#userverificationoptionalwithcredentialidlist);
     77     /// and when enforcing the value, disallow [`CredentialProtectionPolicy::UserVerificationOptional`].
     78     ///
     79     /// The `bool` corresponds to
     80     /// [`enforceCredentialProtectionPolicy`](https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#dom-authenticationextensionsclientinputs-enforcecredentialprotectionpolicy).
     81     UserVerificationOptionalWithCredentialIdList(bool, ExtensionInfo),
     82     /// Request
     83     /// [`userVerificationRequired`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#userverificationrequired);
     84     /// and when enforcing the value, only allow [`CredentialProtectionPolicy::UserVerificationRequired`].
     85     ///
     86     /// The `bool` corresponds to
     87     /// [`enforceCredentialProtectionPolicy`](https://fidoalliance.org/specs/fido-v2.2-ps-20250228/fido-client-to-authenticator-protocol-v2.2-ps-20250228.html#dom-authenticationextensionsclientinputs-enforcecredentialprotectionpolicy).
     88     UserVerificationRequired(bool, ExtensionInfo),
     89 }
     90 impl CredProtect {
     91     /// Validates `other` is allowed based on `self`.
     92     ///
     93     /// # Errors
     94     ///
     95     /// Errors iff other is a less "secure" policy than `self` when enforcing the value or other does not exist
     96     /// despite requiring a value to be sent back.
     97     ///
     98     /// Note a missing response is OK when enforcing a value.
     99     const fn validate(self, other: CredentialProtectionPolicy) -> Result<(), ExtensionErr> {
    100         match self {
    101             Self::None => Ok(()),
    102             Self::UserVerificationOptional(_, info) => {
    103                 if matches!(other, CredentialProtectionPolicy::None) {
    104                     if matches!(
    105                         info,
    106                         ExtensionInfo::RequireEnforceValue | ExtensionInfo::RequireDontEnforceValue
    107                     ) {
    108                         Err(ExtensionErr::MissingCredProtect)
    109                     } else {
    110                         Ok(())
    111                     }
    112                 } else {
    113                     Ok(())
    114                 }
    115             }
    116             Self::UserVerificationOptionalWithCredentialIdList(_, info) => match info {
    117                 ExtensionInfo::RequireEnforceValue => match other {
    118                     CredentialProtectionPolicy::None => Err(ExtensionErr::MissingCredProtect),
    119                     CredentialProtectionPolicy::UserVerificationOptional => {
    120                         Err(ExtensionErr::InvalidCredProtectValue(self, other))
    121                     }
    122                     CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList
    123                     | CredentialProtectionPolicy::UserVerificationRequired => Ok(()),
    124                 },
    125                 ExtensionInfo::RequireDontEnforceValue => {
    126                     if matches!(other, CredentialProtectionPolicy::None) {
    127                         Err(ExtensionErr::MissingCredProtect)
    128                     } else {
    129                         Ok(())
    130                     }
    131                 }
    132                 ExtensionInfo::AllowEnforceValue => {
    133                     if matches!(other, CredentialProtectionPolicy::UserVerificationOptional) {
    134                         Err(ExtensionErr::InvalidCredProtectValue(self, other))
    135                     } else {
    136                         Ok(())
    137                     }
    138                 }
    139                 ExtensionInfo::AllowDontEnforceValue => Ok(()),
    140             },
    141             Self::UserVerificationRequired(_, info) => match info {
    142                 ExtensionInfo::RequireEnforceValue => match other {
    143                     CredentialProtectionPolicy::None => Err(ExtensionErr::MissingCredProtect),
    144                     CredentialProtectionPolicy::UserVerificationOptional
    145                     | CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList => {
    146                         Err(ExtensionErr::InvalidCredProtectValue(self, other))
    147                     }
    148                     CredentialProtectionPolicy::UserVerificationRequired => Ok(()),
    149                 },
    150                 ExtensionInfo::RequireDontEnforceValue => {
    151                     if matches!(other, CredentialProtectionPolicy::None) {
    152                         Err(ExtensionErr::MissingCredProtect)
    153                     } else {
    154                         Ok(())
    155                     }
    156                 }
    157                 ExtensionInfo::AllowEnforceValue => {
    158                     if matches!(
    159                         other,
    160                         CredentialProtectionPolicy::None
    161                             | CredentialProtectionPolicy::UserVerificationRequired
    162                     ) {
    163                         Ok(())
    164                     } else {
    165                         Err(ExtensionErr::InvalidCredProtectValue(self, other))
    166                     }
    167                 }
    168                 ExtensionInfo::AllowDontEnforceValue => Ok(()),
    169             },
    170         }
    171     }
    172 }
    173 impl Display for CredProtect {
    174     #[inline]
    175     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    176         match *self {
    177             Self::None => f.write_str("do not sent a credProtect request"),
    178             Self::UserVerificationOptional(enforce, info) => {
    179                 write!(
    180                     f,
    181                     "request user verification optional with enforcement {enforce} and {info}"
    182                 )
    183             }
    184             Self::UserVerificationOptionalWithCredentialIdList(enforce, info) => write!(
    185                 f,
    186                 "user verification optional with credential ID list with enforcement {enforce} and {info}"
    187             ),
    188             Self::UserVerificationRequired(enforce, info) => {
    189                 write!(
    190                     f,
    191                     "user verification required with enforcement {enforce} and {info}"
    192                 )
    193             }
    194         }
    195     }
    196 }
    197 /// String returned from the [Nickname Enforcement rule](https://www.rfc-editor.org/rfc/rfc8266#section-2.3)
    198 /// as defined in RFC 8266.
    199 ///
    200 /// Note [string truncation](https://www.w3.org/TR/webauthn-3/#sctn-strings-truncation) is allowed, so one may
    201 /// want to enforce [`Self::RECOMMENDED_MAX_LEN`].
    202 #[derive(Clone, Debug)]
    203 pub struct Nickname<'a>(Cow<'a, str>);
    204 impl<'a> Nickname<'a> {
    205     /// The maximum allowed length.
    206     pub const MAX_LEN: usize = 1023;
    207     /// The recommended maximum length to allow.
    208     pub const RECOMMENDED_MAX_LEN: usize = 64;
    209     /// Returns a `Nickname` that consumes `self`. When `self` owns the data, the data is simply moved;
    210     /// when the data is borrowed, then it is cloned into an owned instance.
    211     #[inline]
    212     #[must_use]
    213     pub fn into_owned<'b>(self) -> Nickname<'b> {
    214         Nickname(Cow::Owned(self.0.into_owned()))
    215     }
    216     /// Same as [`Self::with_max_len`] except the length must not exceed [`Self::RECOMMENDED_MAX_LEN`] instead of
    217     /// [`Self::MAX_LEN`].
    218     ///
    219     /// # Errors
    220     ///
    221     /// Errors iff `value` violates [RFC 8266 § 2.3](https://www.rfc-editor.org/rfc/rfc8266.html#section-2.3)
    222     /// or the resulting length exceeds [`Self::RECOMMENDED_MAX_LEN`].
    223     #[expect(single_use_lifetimes, reason = "false positive")]
    224     #[inline]
    225     pub fn with_recommended_len<'b: 'a>(value: Cow<'b, str>) -> Result<Self, NicknameErr> {
    226         precis_profiles::Nickname::new()
    227             .enforce(value)
    228             .map_err(|_e| NicknameErr::Rfc8266)
    229             .and_then(|val| {
    230                 if val.len() <= Self::RECOMMENDED_MAX_LEN {
    231                     Ok(Self(val))
    232                 } else {
    233                     Err(NicknameErr::Len)
    234                 }
    235             })
    236     }
    237     /// Same as [`Self::try_from`].
    238     /// # Errors
    239     ///
    240     /// Errors iff `value` violates [RFC 8266 § 2.3](https://www.rfc-editor.org/rfc/rfc8266.html#section-2.3)
    241     /// or the resulting length exceeds [`Self::MAX_LEN`].
    242     #[expect(single_use_lifetimes, reason = "false positive")]
    243     #[inline]
    244     pub fn with_max_len<'b: 'a>(value: Cow<'b, str>) -> Result<Self, NicknameErr> {
    245         Self::try_from(value)
    246     }
    247 }
    248 impl AsRef<str> for Nickname<'_> {
    249     #[inline]
    250     fn as_ref(&self) -> &str {
    251         self.0.as_ref()
    252     }
    253 }
    254 impl Borrow<str> for Nickname<'_> {
    255     #[inline]
    256     fn borrow(&self) -> &str {
    257         self.0.as_ref()
    258     }
    259 }
    260 impl<'a: 'b, 'b> From<&'a Nickname<'_>> for Nickname<'b> {
    261     #[inline]
    262     fn from(value: &'a Nickname<'_>) -> Self {
    263         match value.0 {
    264             Cow::Borrowed(val) => Self(Cow::Borrowed(val)),
    265             Cow::Owned(ref val) => Self(Cow::Borrowed(val.as_str())),
    266         }
    267     }
    268 }
    269 impl<'a: 'b, 'b> From<Nickname<'a>> for Cow<'b, str> {
    270     #[inline]
    271     fn from(value: Nickname<'a>) -> Self {
    272         value.0
    273     }
    274 }
    275 impl<'a: 'b, 'b> TryFrom<Cow<'a, str>> for Nickname<'b> {
    276     type Error = NicknameErr;
    277     /// # Examples
    278     ///
    279     /// ```
    280     /// # use std::borrow::Cow;
    281     /// # use webauthn_rp::request::register::{error::NicknameErr, Nickname};
    282     /// assert_eq!(
    283     ///     Nickname::try_from(Cow::Borrowed("Srinivasa Ramanujan"))?.as_ref(),
    284     ///     "Srinivasa Ramanujan"
    285     /// );
    286     /// assert_eq!(
    287     ///     Nickname::try_from(Cow::Borrowed("श्रीनिवास रामानुजन्"))?.as_ref(),
    288     ///     "श्रीनिवास रामानुजन्"
    289     /// );
    290     /// // Empty strings are not valid.
    291     /// assert!(Nickname::try_from(Cow::Borrowed("")).map_or_else(
    292     ///     |e| matches!(e, NicknameErr::Rfc8266),
    293     ///     |_| false
    294     /// ));
    295     /// # Ok::<_, webauthn_rp::AggErr>(())
    296     /// ```
    297     #[inline]
    298     fn try_from(value: Cow<'a, str>) -> Result<Self, Self::Error> {
    299         precis_profiles::Nickname::new()
    300             .enforce(value)
    301             .map_err(|_e| NicknameErr::Rfc8266)
    302             .and_then(|val| {
    303                 if val.len() <= Self::MAX_LEN {
    304                     Ok(Self(val))
    305                 } else {
    306                     Err(NicknameErr::Len)
    307                 }
    308             })
    309     }
    310 }
    311 impl<'a: 'b, 'b> TryFrom<&'a str> for Nickname<'b> {
    312     type Error = NicknameErr;
    313     /// Same as [`Nickname::try_from`] except the input is a `str`.
    314     #[inline]
    315     fn try_from(value: &'a str) -> Result<Self, Self::Error> {
    316         Self::try_from(Cow::Borrowed(value))
    317     }
    318 }
    319 impl TryFrom<String> for Nickname<'_> {
    320     type Error = NicknameErr;
    321     /// Same as [`Nickname::try_from`] except the input is a `String`.
    322     #[inline]
    323     fn try_from(value: String) -> Result<Self, Self::Error> {
    324         Self::try_from(Cow::Owned(value))
    325     }
    326 }
    327 impl PartialEq<Nickname<'_>> for Nickname<'_> {
    328     #[inline]
    329     fn eq(&self, other: &Nickname<'_>) -> bool {
    330         self.0 == other.0
    331     }
    332 }
    333 impl PartialEq<&Nickname<'_>> for Nickname<'_> {
    334     #[inline]
    335     fn eq(&self, other: &&Nickname<'_>) -> bool {
    336         *self == **other
    337     }
    338 }
    339 impl PartialEq<Nickname<'_>> for &Nickname<'_> {
    340     #[inline]
    341     fn eq(&self, other: &Nickname<'_>) -> bool {
    342         **self == *other
    343     }
    344 }
    345 impl Eq for Nickname<'_> {}
    346 impl Hash for Nickname<'_> {
    347     #[inline]
    348     fn hash<H: Hasher>(&self, state: &mut H) {
    349         self.0.hash(state);
    350     }
    351 }
    352 impl PartialOrd<Nickname<'_>> for Nickname<'_> {
    353     #[inline]
    354     fn partial_cmp(&self, other: &Nickname<'_>) -> Option<Ordering> {
    355         self.0.partial_cmp(&other.0)
    356     }
    357 }
    358 impl Ord for Nickname<'_> {
    359     #[inline]
    360     fn cmp(&self, other: &Self) -> Ordering {
    361         self.0.cmp(&other.0)
    362     }
    363 }
    364 /// String returned from the
    365 /// [UsernameCasePreserved Enforcement rule](https://www.rfc-editor.org/rfc/rfc8265#section-3.4.3) as defined in
    366 /// RFC 8265.
    367 ///
    368 /// Note [string truncation](https://www.w3.org/TR/webauthn-3/#sctn-strings-truncation) is allowed, so one may
    369 /// want to enforce [`Self::RECOMMENDED_MAX_LEN`].
    370 #[derive(Clone, Debug)]
    371 pub struct Username<'a>(Cow<'a, str>);
    372 impl<'a> Username<'a> {
    373     /// The maximum allowed length.
    374     pub const MAX_LEN: usize = 1023;
    375     /// The recommended maximum length to allow.
    376     pub const RECOMMENDED_MAX_LEN: usize = 64;
    377     /// Returns a `Username` that consumes `self`. When `self` owns the data, the data is simply moved;
    378     /// when the data is borrowed, then it is cloned into an owned instance.
    379     #[inline]
    380     #[must_use]
    381     pub fn into_owned<'b>(self) -> Username<'b> {
    382         Username(Cow::Owned(self.0.into_owned()))
    383     }
    384     /// Same as [`Self::with_max_len`] except the length must not exceed [`Self::RECOMMENDED_MAX_LEN`] instead of
    385     /// [`Self::MAX_LEN`].
    386     ///
    387     /// # Errors
    388     ///
    389     /// Errors iff `value` violates [RFC 8265 § 3.4.3](https://www.rfc-editor.org/rfc/rfc8265.html#section-3.4.3)
    390     /// or the resulting length exceeds [`Self::RECOMMENDED_MAX_LEN`].
    391     #[expect(single_use_lifetimes, reason = "false positive")]
    392     #[inline]
    393     pub fn with_recommended_len<'b: 'a>(value: Cow<'b, str>) -> Result<Self, UsernameErr> {
    394         UsernameCasePreserved::default()
    395             .enforce(value)
    396             .map_err(|_e| UsernameErr::Rfc8265)
    397             .and_then(|val| {
    398                 if val.len() <= Self::RECOMMENDED_MAX_LEN {
    399                     Ok(Self(val))
    400                 } else {
    401                     Err(UsernameErr::Len)
    402                 }
    403             })
    404     }
    405     /// Same as [`Self::try_from`].
    406     /// # Errors
    407     ///
    408     /// Errors iff `value` violates [RFC 8265 § 3.4.3](https://www.rfc-editor.org/rfc/rfc8265.html#section-3.4.3)
    409     /// or the resulting length exceeds [`Self::MAX_LEN`].
    410     #[expect(single_use_lifetimes, reason = "false positive")]
    411     #[inline]
    412     pub fn with_max_len<'b: 'a>(value: Cow<'b, str>) -> Result<Self, UsernameErr> {
    413         Self::try_from(value)
    414     }
    415     /// Returns `Self` containing `"blank"`.
    416     #[expect(clippy::unreachable, reason = "want to crash when there is a bug")]
    417     fn blank() -> Self {
    418         Self::try_from("blank")
    419             .unwrap_or_else(|_e| unreachable!("'blank' is no longer a valid Username"))
    420     }
    421 }
    422 impl AsRef<str> for Username<'_> {
    423     #[inline]
    424     fn as_ref(&self) -> &str {
    425         self.0.as_ref()
    426     }
    427 }
    428 impl Borrow<str> for Username<'_> {
    429     #[inline]
    430     fn borrow(&self) -> &str {
    431         self.0.as_ref()
    432     }
    433 }
    434 impl<'a: 'b, 'b> TryFrom<Cow<'a, str>> for Username<'b> {
    435     type Error = UsernameErr;
    436     /// # Examples
    437     ///
    438     /// ```
    439     /// # use std::borrow::Cow;
    440     /// # use webauthn_rp::request::register::{error::UsernameErr, Username};
    441     /// assert_eq!(
    442     ///     Username::try_from(Cow::Borrowed("leonhard.euler"))?.as_ref(),
    443     ///     "leonhard.euler"
    444     /// );
    445     /// // Empty strings are not valid.
    446     /// assert!(Username::try_from(Cow::Borrowed("")).map_or_else(
    447     ///     |e| matches!(e, UsernameErr::Rfc8265),
    448     ///     |_| false
    449     /// ));
    450     /// # Ok::<_, webauthn_rp::AggErr>(())
    451     /// ```
    452     #[inline]
    453     fn try_from(value: Cow<'a, str>) -> Result<Self, Self::Error> {
    454         UsernameCasePreserved::default()
    455             .enforce(value)
    456             .map_err(|_e| UsernameErr::Rfc8265)
    457             .and_then(|val| {
    458                 if val.len() <= Self::MAX_LEN {
    459                     Ok(Self(val))
    460                 } else {
    461                     Err(UsernameErr::Len)
    462                 }
    463             })
    464     }
    465 }
    466 impl<'a: 'b, 'b> TryFrom<&'a str> for Username<'b> {
    467     type Error = UsernameErr;
    468     /// Same as [`Username::try_from`] except the input is a `str`.
    469     #[inline]
    470     fn try_from(value: &'a str) -> Result<Self, Self::Error> {
    471         Self::try_from(Cow::Borrowed(value))
    472     }
    473 }
    474 impl TryFrom<String> for Username<'_> {
    475     type Error = UsernameErr;
    476     /// Same as [`Username::try_from`] except the input is a `String`.
    477     #[inline]
    478     fn try_from(value: String) -> Result<Self, Self::Error> {
    479         Self::try_from(Cow::Owned(value))
    480     }
    481 }
    482 impl<'a: 'b, 'b> From<Username<'a>> for Cow<'b, str> {
    483     #[inline]
    484     fn from(value: Username<'a>) -> Self {
    485         value.0
    486     }
    487 }
    488 impl<'a: 'b, 'b> From<&'a Username<'_>> for Username<'b> {
    489     #[inline]
    490     fn from(value: &'a Username<'_>) -> Self {
    491         match value.0 {
    492             Cow::Borrowed(val) => Self(Cow::Borrowed(val)),
    493             Cow::Owned(ref val) => Self(Cow::Borrowed(val.as_str())),
    494         }
    495     }
    496 }
    497 impl PartialEq<Username<'_>> for Username<'_> {
    498     #[inline]
    499     fn eq(&self, other: &Username<'_>) -> bool {
    500         self.0 == other.0
    501     }
    502 }
    503 impl PartialEq<&Username<'_>> for Username<'_> {
    504     #[inline]
    505     fn eq(&self, other: &&Username<'_>) -> bool {
    506         *self == **other
    507     }
    508 }
    509 impl PartialEq<Username<'_>> for &Username<'_> {
    510     #[inline]
    511     fn eq(&self, other: &Username<'_>) -> bool {
    512         **self == *other
    513     }
    514 }
    515 impl Eq for Username<'_> {}
    516 impl Hash for Username<'_> {
    517     #[inline]
    518     fn hash<H: Hasher>(&self, state: &mut H) {
    519         self.0.hash(state);
    520     }
    521 }
    522 impl PartialOrd<Username<'_>> for Username<'_> {
    523     #[inline]
    524     fn partial_cmp(&self, other: &Username<'_>) -> Option<Ordering> {
    525         self.0.partial_cmp(&other.0)
    526     }
    527 }
    528 impl Ord for Username<'_> {
    529     #[inline]
    530     fn cmp(&self, other: &Self) -> Ordering {
    531         self.0.cmp(&other.0)
    532     }
    533 }
    534 /// [`COSEAlgorithmIdentifier`](https://www.w3.org/TR/webauthn-3/#typedefdef-cosealgorithmidentifier).
    535 ///
    536 /// Note the order of variants is the following:
    537 ///
    538 /// [`Self::Eddsa`] `<` [`Self::Es256`] `<` [`Self::Es384`] `<` [`Self::Rs256`].
    539 ///
    540 /// This is relevant for [`CoseAlgorithmIdentifiers`]. For example a `CoseAlgorithmIdentifiers`
    541 /// that contains `Self::Eddsa` will prioritize it over all others.
    542 #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
    543 pub enum CoseAlgorithmIdentifier {
    544     /// [EdDSA](https://www.iana.org/assignments/cose/cose.xhtml#algorithms).
    545     ///
    546     /// Note that Ed25519 must be used for the `crv` parameter
    547     /// [per the spec](https://www.w3.org/TR/webauthn-3/#sctn-alg-identifier).
    548     Eddsa,
    549     /// [ES256](https://www.iana.org/assignments/cose/cose.xhtml#algorithms).
    550     ///
    551     /// Note the uncompressed form must be used and P-256 must be used for the `crv` parameter
    552     /// [per the spec](https://www.w3.org/TR/webauthn-3/#sctn-alg-identifier).
    553     Es256,
    554     /// [ES384](https://www.iana.org/assignments/cose/cose.xhtml#algorithms).
    555     ///
    556     /// Note the uncompressed form must be used and P-384 must be used for the `crv` parameter
    557     /// [per the spec](https://www.w3.org/TR/webauthn-3/#sctn-alg-identifier).
    558     Es384,
    559     /// [RS256](https://www.iana.org/assignments/cose/cose.xhtml#algorithms).
    560     Rs256,
    561 }
    562 impl CoseAlgorithmIdentifier {
    563     /// Transforms `self` into a `u8`.
    564     const fn to_u8(self) -> u8 {
    565         match self {
    566             Self::Eddsa => 1,
    567             Self::Es256 => 2,
    568             Self::Es384 => 4,
    569             Self::Rs256 => 8,
    570         }
    571     }
    572 }
    573 impl PartialEq<&Self> for CoseAlgorithmIdentifier {
    574     #[inline]
    575     fn eq(&self, other: &&Self) -> bool {
    576         *self == **other
    577     }
    578 }
    579 impl PartialEq<CoseAlgorithmIdentifier> for &CoseAlgorithmIdentifier {
    580     #[inline]
    581     fn eq(&self, other: &CoseAlgorithmIdentifier) -> bool {
    582         **self == *other
    583     }
    584 }
    585 /// Non-empty ordered set of [`CoseAlgorithmIdentifier`]s.
    586 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
    587 pub struct CoseAlgorithmIdentifiers(u8);
    588 impl CoseAlgorithmIdentifiers {
    589     /// Contains all [`CoseAlgorithmIdentifier`]s.
    590     pub const ALL: Self = Self(0)
    591         .add(CoseAlgorithmIdentifier::Eddsa)
    592         .add(CoseAlgorithmIdentifier::Es256)
    593         .add(CoseAlgorithmIdentifier::Es384)
    594         .add(CoseAlgorithmIdentifier::Rs256);
    595     /// Returns a `CoseAlgorithmIdentifiers` containing all `CoseAlgorithmIdentifier`s in `self` plus `alg`.
    596     const fn add(self, alg: CoseAlgorithmIdentifier) -> Self {
    597         Self(self.0 | alg.to_u8())
    598     }
    599     /// Returns a copy of `self` with `alg` removed if there would be at least one `CoseAlgorithmIdentifier`
    600     /// remaining; otherwise returns `self`.
    601     #[inline]
    602     #[must_use]
    603     pub const fn remove(self, alg: CoseAlgorithmIdentifier) -> Self {
    604         let val = alg.to_u8();
    605         if self.0 == val {
    606             self
    607         } else {
    608             Self(self.0 & !val)
    609         }
    610     }
    611     /// Returns `true` iff `self` contains `alg`.
    612     #[inline]
    613     #[must_use]
    614     pub const fn contains(self, alg: CoseAlgorithmIdentifier) -> bool {
    615         let val = alg.to_u8();
    616         self.0 & val == val
    617     }
    618     /// Validates `other` is allowed based on `self`.
    619     const fn validate(self, other: UncompressedPubKey<'_>) -> Result<(), RegCeremonyErr> {
    620         if match other {
    621             UncompressedPubKey::Ed25519(_) => self.contains(CoseAlgorithmIdentifier::Eddsa),
    622             UncompressedPubKey::P256(_) => self.contains(CoseAlgorithmIdentifier::Es256),
    623             UncompressedPubKey::P384(_) => self.contains(CoseAlgorithmIdentifier::Es384),
    624             UncompressedPubKey::Rsa(_) => self.contains(CoseAlgorithmIdentifier::Rs256),
    625         } {
    626             Ok(())
    627         } else {
    628             Err(RegCeremonyErr::PublicKeyAlgorithmMismatch)
    629         }
    630     }
    631 }
    632 impl Default for CoseAlgorithmIdentifiers {
    633     /// Returns [`Self::ALL`].
    634     #[inline]
    635     fn default() -> Self {
    636         Self::ALL
    637     }
    638 }
    639 /// Four to sixty-three inclusively.
    640 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    641 #[repr(u8)]
    642 pub enum FourToSixtyThree {
    643     /// 4.
    644     Four = 4,
    645     /// 5.
    646     Five,
    647     /// 6.
    648     Six,
    649     /// 7.
    650     Seven,
    651     /// 8.
    652     Eight,
    653     /// 9.
    654     Nine,
    655     /// 10.
    656     Ten,
    657     /// 11.
    658     Eleven,
    659     /// 12.
    660     Twelve,
    661     /// 13.
    662     Thirteen,
    663     /// 14.
    664     Fourteen,
    665     /// 15.
    666     Fifteen,
    667     /// 16.
    668     Sixteen,
    669     /// 17.
    670     Seventeen,
    671     /// 18.
    672     Eighteen,
    673     /// 19.
    674     Nineteen,
    675     /// 20.
    676     Twenty,
    677     /// 21.
    678     TwentyOne,
    679     /// 22.
    680     TwentyTwo,
    681     /// 23.
    682     TwentyThree,
    683     /// 24.
    684     TwentyFour,
    685     /// 25.
    686     TwentyFive,
    687     /// 26.
    688     TwentySix,
    689     /// 27.
    690     TwentySeven,
    691     /// 28.
    692     TwentyEight,
    693     /// 29.
    694     TwentyNine,
    695     /// 30.
    696     Thirty,
    697     /// 31.
    698     ThirtyOne,
    699     /// 32.
    700     ThirtyTwo,
    701     /// 33.
    702     ThirtyThree,
    703     /// 34.
    704     ThirtyFour,
    705     /// 35.
    706     ThirtyFive,
    707     /// 36.
    708     ThirtySix,
    709     /// 37.
    710     ThirtySeven,
    711     /// 38.
    712     ThirtyEight,
    713     /// 39.
    714     ThirtyNine,
    715     /// 40.
    716     Fourty,
    717     /// 41.
    718     FourtyOne,
    719     /// 42.
    720     FourtyTwo,
    721     /// 43.
    722     FourtyThree,
    723     /// 44.
    724     FourtyFour,
    725     /// 45.
    726     FourtyFive,
    727     /// 46.
    728     FourtySix,
    729     /// 47.
    730     FourtySeven,
    731     /// 48.
    732     FourtyEight,
    733     /// 49.
    734     FourtyNine,
    735     /// 50.
    736     Fifty,
    737     /// 51.
    738     FiftyOne,
    739     /// 52.
    740     FiftyTwo,
    741     /// 53.
    742     FiftyThree,
    743     /// 54.
    744     FiftyFour,
    745     /// 55.
    746     FiftyFive,
    747     /// 56.
    748     FiftySix,
    749     /// 57.
    750     FiftySeven,
    751     /// 58.
    752     FiftyEight,
    753     /// 59.
    754     FiftyNine,
    755     /// 60.
    756     Sixty,
    757     /// 61.
    758     SixtyOne,
    759     /// 62.
    760     SixtyTwo,
    761     /// 63.
    762     SixtyThree,
    763 }
    764 impl FourToSixtyThree {
    765     /// Returns the equivalent `u8`.
    766     #[expect(clippy::as_conversions, reason = "comment justifies correctness")]
    767     #[inline]
    768     #[must_use]
    769     pub const fn into_u8(self) -> u8 {
    770         // This is correct since `Self` is `repr(u8)`, and the initial discriminant has the value `4`
    771         // and subsequent discriminants are implicitly incremented by 1.
    772         self as u8
    773     }
    774     /// Returns `Some` representing `val` iff `val` is inclusively between 4 and 63.
    775     #[expect(unsafe_code, reason = "comment justifies correctness")]
    776     #[inline]
    777     #[must_use]
    778     pub const fn from_u8(val: u8) -> Option<Self> {
    779         match val {
    780             0..=3 | 64.. => None,
    781             _ => {
    782                 // SAFETY:
    783                 // `val` is inclusively between 4 and 63, and `Self` is `repr(u8)`; thus this
    784                 // is safe and correct.
    785                 Some(unsafe { mem::transmute::<u8, Self>(val) })
    786             }
    787         }
    788     }
    789 }
    790 impl Display for FourToSixtyThree {
    791     #[inline]
    792     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    793         self.into_u8().fmt(f)
    794     }
    795 }
    796 impl From<FourToSixtyThree> for u8 {
    797     #[inline]
    798     fn from(value: FourToSixtyThree) -> Self {
    799         value.into_u8()
    800     }
    801 }
    802 impl Default for FourToSixtyThree {
    803     #[inline]
    804     fn default() -> Self {
    805         Self::Four
    806     }
    807 }
    808 /// The [defined extensions](https://www.w3.org/TR/webauthn-3/#sctn-defined-extensions) to send to the client.
    809 #[derive(Clone, Copy, Debug)]
    810 pub struct Extension<'prf_first, 'prf_second> {
    811     /// [`credProps`](https://www.w3.org/TR/webauthn-3/#sctn-authenticator-credential-properties-extension).
    812     ///
    813     /// The best one can do to ensure a server-side credential is created is by sending
    814     /// [`ResidentKeyRequirement::Discouraged`]; however authenticators are still allowed
    815     /// to create a client-side credential. To more definitively check that a server-side credential is
    816     /// created, send this extension. Note that it can be difficult to impossible for a client/user agent to
    817     /// know that a server-side credential is created; thus even when the response is
    818     /// `Some(CredentialPropertiesOutput { rk: Some(false) })`, a client-side/"resident" credential could still
    819     /// have been created. One may have better luck checking if [`AuthTransports::contains`]
    820     /// [`AuthenticatorTransport::Internal`] and using that as an indicator if a client-side credential was created.
    821     ///
    822     /// In the event [`ClientExtensionsOutputs::cred_props`] is `Some(CredentialPropertiesOutput { rk: Some(false) })`
    823     /// and [`ResidentKeyRequirement::Required`] was sent, an error will happen regardless of this value.
    824     pub cred_props: Option<ExtensionReq>,
    825     /// [`credProtect`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-credProtect-extension).
    826     pub cred_protect: CredProtect,
    827     /// [`minPinLength`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-minpinlength-extension).
    828     ///
    829     /// When the value is enforced, that corresponds to
    830     /// [`minPinLength`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-minpinlength-extension)
    831     /// in [`extensions`](https://www.w3.org/TR/webauthn-3/#authdata-extensions) set to a value at least as large
    832     /// as the contained `FourToSixtyThree`.
    833     pub min_pin_length: Option<(FourToSixtyThree, ExtensionInfo)>,
    834     /// [`prf`](https://www.w3.org/TR/webauthn-3/#prf-extension).
    835     ///
    836     /// When the value is enforced, that corresponds to
    837     /// [`enabled`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-enabled) set to `true`.
    838     /// In contrast [`results`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-results)
    839     /// must not exist, be `null`, or be an
    840     /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues)
    841     /// such that [`first`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-first) is `null`
    842     /// and [`second`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-second) does not
    843     /// exist or is `null`. This is to ensure the decrypted outputs stay on the client.
    844     ///
    845     /// Note some authenticators can only enable `prf` during registration (e.g., CTAP authenticators that only
    846     /// support
    847     /// [`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)
    848     /// and not
    849     /// [`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));
    850     /// thus the value of `PrfInput` is ignored and only used as a signal to enable `prf`. For many such
    851     /// authenticators, not using this extension during registration will not preclude them from being used during
    852     /// authentication; however it is still encouraged to use the extension during registration since some
    853     /// authenticators actually require it.
    854     ///
    855     /// When the underlying credential is expected to be used during discoverable requests, it is likely that
    856     /// `'prf_first` will be `'static` and [`PrfInput::second`] is `None` since one will not be able to
    857     /// realistically rotate the underlying inputs and further the same input will likely be used for all credentials.
    858     /// For credentials intended to be used during non-discoverable requests, however, one is encouraged to rotate
    859     /// the inputs and have unique values for each credential.
    860     pub prf: Option<(PrfInput<'prf_first, 'prf_second>, ExtensionInfo)>,
    861 }
    862 impl<'prf_first, 'prf_second> Extension<'prf_first, 'prf_second> {
    863     /// Returns an empty `Extension`.
    864     #[inline]
    865     #[must_use]
    866     pub const fn none() -> Self {
    867         Self {
    868             cred_props: None,
    869             cred_protect: CredProtect::None,
    870             min_pin_length: None,
    871             prf: None,
    872         }
    873     }
    874     /// Same as [`Self::none`] except [`Self::cred_props`] is `Some` containing `req`.
    875     #[inline]
    876     #[must_use]
    877     pub const fn with_cred_props(req: ExtensionReq) -> Self {
    878         Self {
    879             cred_props: Some(req),
    880             ..Self::none()
    881         }
    882     }
    883     /// Same as [`Self::none`] except [`Self::cred_protect`] is `cred_protect`.
    884     #[inline]
    885     #[must_use]
    886     pub const fn with_cred_protect(cred_protect: CredProtect) -> Self {
    887         Self {
    888             cred_protect,
    889             ..Self::none()
    890         }
    891     }
    892     /// Same as [`Self::none`] except [`Self::min_pin_length`] is `Some` containing `min_len` and `info`.
    893     #[inline]
    894     #[must_use]
    895     pub const fn with_min_pin_length(min_len: FourToSixtyThree, info: ExtensionInfo) -> Self {
    896         Self {
    897             min_pin_length: Some((min_len, info)),
    898             ..Self::none()
    899         }
    900     }
    901     /// Same as [`Self::none`] except [`Self::prf`] is `Some` containing `input` and `info`.
    902     #[expect(single_use_lifetimes, reason = "false positive")]
    903     #[inline]
    904     #[must_use]
    905     pub const fn with_prf<'a: 'prf_first, 'b: 'prf_second>(
    906         input: PrfInput<'a, 'b>,
    907         info: ExtensionInfo,
    908     ) -> Self {
    909         Self {
    910             prf: Some((input, info)),
    911             ..Self::none()
    912         }
    913     }
    914 }
    915 impl Default for Extension<'_, '_> {
    916     /// Same as [`Self::none`].
    917     #[inline]
    918     fn default() -> Self {
    919         Self::none()
    920     }
    921 }
    922 #[cfg(test)]
    923 impl PartialEq for Extension<'_, '_> {
    924     fn eq(&self, other: &Self) -> bool {
    925         self.cred_props == other.cred_props
    926             && self.cred_protect == other.cred_protect
    927             && self.min_pin_length == other.min_pin_length
    928             && self.prf == other.prf
    929     }
    930 }
    931 /// The maximum number of bytes a [`UserHandle`] can be made of per
    932 /// [WebAuthn](https://www.w3.org/TR/webauthn-3/#user-handle).
    933 pub const USER_HANDLE_MAX_LEN: usize = 64;
    934 /// The minimum number of bytes a [`UserHandle`] can be made of per
    935 /// [WebAuthn](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentity-id).
    936 pub const USER_HANDLE_MIN_LEN: usize = 1;
    937 /// A [user handle](https://www.w3.org/TR/webauthn-3/#user-handle) that is made up of
    938 /// [`USER_HANDLE_MIN_LEN`]–[`USER_HANDLE_MAX_LEN`] bytes.
    939 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    940 pub struct UserHandle<const LEN: usize>([u8; LEN]);
    941 impl<const LEN: usize> UserHandle<LEN> {
    942     /// Returns the contained data as a `slice`.
    943     #[inline]
    944     #[must_use]
    945     pub const fn as_slice(&self) -> &[u8] {
    946         self.0.as_slice()
    947     }
    948     /// Returns the contained data as a shared reference to the array.
    949     #[inline]
    950     #[must_use]
    951     pub const fn as_array(&self) -> &[u8; LEN] {
    952         &self.0
    953     }
    954     /// Returns the contained data.
    955     #[inline]
    956     #[must_use]
    957     pub const fn into_array(self) -> [u8; LEN] {
    958         self.0
    959     }
    960 }
    961 /// Implements [`Default`] for [`UserHandle`] of array of length of the passed `usize` literal.
    962 ///
    963 /// Only [`USER_HANDLE_MIN_LEN`]–[`USER_HANDLE_MAX_LEN`] inclusively are allowed to be passed.
    964 macro_rules! user {
    965     ( $( $x:literal),* ) => {
    966         $(
    967 impl Default for UserHandle<$x> {
    968     #[inline]
    969     fn default() -> Self {
    970         let mut data = [0; $x];
    971         rand::fill(data.as_mut_slice());
    972         Self(data)
    973     }
    974 }
    975         )*
    976     };
    977 }
    978 // MUST only pass [`USER_HANDLE_MIN_LEN`]–[`USER_HANDLE_MAX_LEN`] inclusively.
    979 user!(
    980     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,
    981     27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
    982     51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
    983 );
    984 impl<const LEN: usize> UserHandle<LEN>
    985 where
    986     Self: Default,
    987 {
    988     /// Returns a new `UserHandle` based on `LEN` randomly-generated [`u8`]s.
    989     ///
    990     /// # Examples
    991     ///
    992     /// ```
    993     /// # use webauthn_rp::request::register::{UserHandle, UserHandle64, USER_HANDLE_MIN_LEN, USER_HANDLE_MAX_LEN};
    994     /// assert_eq!(
    995     ///     UserHandle::<USER_HANDLE_MIN_LEN>::new()
    996     ///         .as_ref()
    997     ///         .len(),
    998     ///     1
    999     /// );
   1000     /// // The probability of an all-zero `UserHandle` being generated (assuming a good entropy
   1001     /// // source) is 2^-512 ≈ 7.5 x 10^-155.
   1002     /// assert_ne!(
   1003     ///     UserHandle64::new().as_ref(),
   1004     ///     [0; USER_HANDLE_MAX_LEN]
   1005     /// );
   1006     /// ```
   1007     #[inline]
   1008     #[must_use]
   1009     pub fn new() -> Self {
   1010         Self::default()
   1011     }
   1012 }
   1013 impl<const LEN: usize> AsRef<[u8]> for UserHandle<LEN> {
   1014     #[inline]
   1015     fn as_ref(&self) -> &[u8] {
   1016         self.as_slice()
   1017     }
   1018 }
   1019 impl<const LEN: usize> Borrow<[u8]> for UserHandle<LEN> {
   1020     #[inline]
   1021     fn borrow(&self) -> &[u8] {
   1022         self.as_slice()
   1023     }
   1024 }
   1025 impl<const LEN: usize> PartialEq<&Self> for UserHandle<LEN> {
   1026     #[inline]
   1027     fn eq(&self, other: &&Self) -> bool {
   1028         *self == **other
   1029     }
   1030 }
   1031 impl<const LEN: usize> PartialEq<UserHandle<LEN>> for &UserHandle<LEN> {
   1032     #[inline]
   1033     fn eq(&self, other: &UserHandle<LEN>) -> bool {
   1034         **self == *other
   1035     }
   1036 }
   1037 impl<const LEN: usize> From<UserHandle<LEN>> for [u8; LEN] {
   1038     #[inline]
   1039     fn from(value: UserHandle<LEN>) -> Self {
   1040         value.into_array()
   1041     }
   1042 }
   1043 impl<'a: 'b, 'b, const LEN: usize> From<&'a UserHandle<LEN>> for &'b [u8; LEN] {
   1044     #[inline]
   1045     fn from(value: &'a UserHandle<LEN>) -> Self {
   1046         value.as_array()
   1047     }
   1048 }
   1049 /// `UserHandle` that is based on the [spec recommendation](https://www.w3.org/TR/webauthn-3/#user-handle).
   1050 pub type UserHandle64 = UserHandle<USER_HANDLE_MAX_LEN>;
   1051 /// `UserHandle` that is based on 16 bytes.
   1052 ///
   1053 /// While not the recommended size like [`UserHandle64`], 16 bytes is common for many deployments since
   1054 /// it's the same size as [Universally Unique IDentifiers (UUIDs)](https://www.rfc-editor.org/rfc/rfc9562).
   1055 pub type UserHandle16 = UserHandle<16>;
   1056 impl UserHandle16 {
   1057     /// Same as [`Self::new`] except 6 bits of metadata is encoded to conform with
   1058     /// [UUID Version 4](https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-4).
   1059     ///
   1060     /// # Examples
   1061     ///
   1062     /// ```
   1063     /// # use webauthn_rp::request::register::UserHandle16;
   1064     /// assert!(UserHandle16::new_uuid_v4().is_uuid_v4());
   1065     /// ```
   1066     #[inline]
   1067     #[must_use]
   1068     pub fn new_uuid_v4() -> Self {
   1069         let mut this = Self::new();
   1070         // The first 4 bits of the 6th octet (0-based index) represents the UUID version (i.e., 4).
   1071         // We first 0-out the version bits retaining the other 4 bits, then set the version bits to 4.
   1072         this.0[6] = (this.0[6] & 0x0F) | 0x40;
   1073         // The first 2 bits of the 8th octet (0-based index) represents the UUID variant (i.e., 8,9,A,B)
   1074         // which is defined to be 2.
   1075         // We first 0-out the variant bits retaining the other 6 bits, then set the variant bits to 2.
   1076         this.0[8] = (this.0[8] & 0x3F) | 0x80;
   1077         this
   1078     }
   1079     /// Returns `true` iff `self` is a valid
   1080     /// [UUID Version 4](https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-4).
   1081     ///
   1082     /// # Examples
   1083     ///
   1084     /// ```
   1085     /// # use webauthn_rp::request::register::UserHandle16;
   1086     /// assert!(UserHandle16::new_uuid_v4().is_uuid_v4());
   1087     /// let mut user = UserHandle16::new_uuid_v4().into_array();
   1088     /// user[6] = 255;
   1089     /// # #[cfg(feature = "custom")]
   1090     /// assert!(!UserHandle16::from(user).is_uuid_v4());
   1091     /// ```
   1092     #[inline]
   1093     #[must_use]
   1094     pub const fn is_uuid_v4(&self) -> bool {
   1095         // The first 4 bits of the 6th octet (0-based index) represents the UUID version (i.e., 4).
   1096         // The first 2 bits of the 8th octet (0-based index) represents the UUID variant (i.e., 8,9,A,B) which
   1097         // is defined to be 2.
   1098         self.0[6] >> 4 == 0x4 && self.0[8] >> 6 == 0x2
   1099     }
   1100     /// Returns `Some` containing `uuid_v4` iff `uuid_v4` is a valid
   1101     /// [UUID Version 4](https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-4).
   1102     ///
   1103     /// # Examples
   1104     ///
   1105     /// ```
   1106     /// # use webauthn_rp::request::register::UserHandle16;
   1107     /// let mut user = UserHandle16::new_uuid_v4().into_array();
   1108     /// assert!(UserHandle16::from_uuid_v4(user).is_some());
   1109     /// user[8] = 255;
   1110     /// assert!(UserHandle16::from_uuid_v4(user).is_none());
   1111     /// ```
   1112     #[cfg_attr(docsrs, doc(cfg(feature = "custom")))]
   1113     #[cfg(feature = "custom")]
   1114     #[inline]
   1115     #[must_use]
   1116     pub fn from_uuid_v4(uuid_v4: [u8; 16]) -> Option<Self> {
   1117         let this = Self(uuid_v4);
   1118         this.is_uuid_v4().then_some(this)
   1119     }
   1120 }
   1121 /// [The `PublicKeyCredentialUserEntity`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialuserentity)
   1122 /// sent to the client.
   1123 #[derive(Clone, Debug)]
   1124 pub struct PublicKeyCredentialUserEntity<'name, 'display_name, 'id, const LEN: usize> {
   1125     /// [`name`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialentity-name).
   1126     pub name: Username<'name>,
   1127     /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentity-id).
   1128     pub id: &'id UserHandle<LEN>,
   1129     /// [`displayName`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentity-displayname).
   1130     ///
   1131     /// `None` iff the display name should be the empty string.
   1132     pub display_name: Option<Nickname<'display_name>>,
   1133 }
   1134 impl<'a: 'b, 'b, const LEN: usize> From<&'a UserHandle<LEN>>
   1135     for PublicKeyCredentialUserEntity<'_, '_, 'b, LEN>
   1136 {
   1137     /// Returns a `PublicKeyCredentialUserEntity` with [`Self::name`] set to `"blank"`,
   1138     /// [`Self::id`] set to `value`, and [`Self::display_name`] set to `None`.
   1139     ///
   1140     /// One should let users set their own user name and user display name; however this can technically happen
   1141     /// _after_ successfully completing the registration ceremony since this information is not used during
   1142     /// [`RegistrationServerState::verify`]. For example the client can send such information along with
   1143     /// [`Registration`].
   1144     ///
   1145     /// # Examples
   1146     ///
   1147     /// ```
   1148     /// # use webauthn_rp::request::register::{PublicKeyCredentialUserEntity, UserHandle64};
   1149     /// let user_handle = UserHandle64::new();
   1150     /// let entity = PublicKeyCredentialUserEntity::from(&user_handle);
   1151     /// assert_eq!("blank", entity.name.as_ref());
   1152     /// assert_eq!(user_handle, *entity.id);
   1153     /// assert!(entity.display_name.is_none());
   1154     /// # Ok::<_, webauthn_rp::AggErr>(())
   1155     /// ```
   1156     #[inline]
   1157     fn from(value: &'a UserHandle<LEN>) -> Self {
   1158         Self {
   1159             name: Username::blank(),
   1160             id: value,
   1161             display_name: None,
   1162         }
   1163     }
   1164 }
   1165 /// `PublicKeyCredentialUserEntity` based on a [`UserHandle64`].
   1166 pub type PublicKeyCredentialUserEntity64<'name, 'display_name, 'id> =
   1167     PublicKeyCredentialUserEntity<'name, 'display_name, 'id, USER_HANDLE_MAX_LEN>;
   1168 /// `PublicKeyCredentialUserEntity` based on a [`UserHandle16`].
   1169 pub type PublicKeyCredentialUserEntity16<'name, 'display_name, 'id> =
   1170     PublicKeyCredentialUserEntity<'name, 'display_name, 'id, 16>;
   1171 /// [`ResidentKeyRequirement`](https://www.w3.org/TR/webauthn-3/#enumdef-residentkeyrequirement) sent to the client.
   1172 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
   1173 pub enum ResidentKeyRequirement {
   1174     /// [`required`](https://www.w3.org/TR/webauthn-3/#dom-residentkeyrequirement-required).
   1175     Required,
   1176     /// [`discouraged`](https://www.w3.org/TR/webauthn-3/#dom-residentkeyrequirement-discouraged).
   1177     Discouraged,
   1178     /// [`preferred`](https://www.w3.org/TR/webauthn-3/#dom-residentkeyrequirement-preferred).
   1179     Preferred,
   1180 }
   1181 /// [`PublicKeyCredentialHints`](https://www.w3.org/TR/webauthn-3/#enumdef-publickeycredentialhint)
   1182 /// for [`AuthenticatorAttachment::CrossPlatform`] authenticators.
   1183 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
   1184 pub enum CrossPlatformHint {
   1185     /// No hints.
   1186     #[default]
   1187     None,
   1188     /// [`security-key`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialhint-security-key).
   1189     SecurityKey,
   1190     /// [`hybrid`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialhint-hybrid).
   1191     Hybrid,
   1192     /// [`Self::SecurityKey`] and [`Self::Hybrid`].
   1193     SecurityKeyHybrid,
   1194     /// [`Self::Hybrid`] and [`Self::SecurityKey`].
   1195     HybridSecurityKey,
   1196 }
   1197 impl From<CrossPlatformHint> for Hint {
   1198     #[inline]
   1199     fn from(value: CrossPlatformHint) -> Self {
   1200         match value {
   1201             CrossPlatformHint::None => Self::None,
   1202             CrossPlatformHint::SecurityKey => Self::SecurityKey,
   1203             CrossPlatformHint::Hybrid => Self::Hybrid,
   1204             CrossPlatformHint::SecurityKeyHybrid => Self::SecurityKeyHybrid,
   1205             CrossPlatformHint::HybridSecurityKey => Self::HybridSecurityKey,
   1206         }
   1207     }
   1208 }
   1209 /// [`PublicKeyCredentialHints`](https://www.w3.org/TR/webauthn-3/#enumdef-publickeycredentialhint)
   1210 /// for [`AuthenticatorAttachment::Platform`] authenticators.
   1211 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
   1212 pub enum PlatformHint {
   1213     /// No hints.
   1214     #[default]
   1215     None,
   1216     /// [`client-device`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialhint-client-device).
   1217     ClientDevice,
   1218 }
   1219 impl From<PlatformHint> for Hint {
   1220     #[inline]
   1221     fn from(value: PlatformHint) -> Self {
   1222         match value {
   1223             PlatformHint::None => Self::None,
   1224             PlatformHint::ClientDevice => Self::ClientDevice,
   1225         }
   1226     }
   1227 }
   1228 /// [`AuthenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#enumdef-authenticatorattachment)
   1229 /// requirement with associated hints for further refinement.
   1230 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
   1231 pub enum AuthenticatorAttachmentReq {
   1232     /// No attachment information (i.e., any [`AuthenticatorAttachment`]).
   1233     None(Hint),
   1234     /// [`platform`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattachment-platform) is required
   1235     /// to be used.
   1236     Platform(PlatformHint),
   1237     /// [`cross-platform`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattachment-cross-platform) is
   1238     /// required to be used.
   1239     CrossPlatform(CrossPlatformHint),
   1240 }
   1241 impl Default for AuthenticatorAttachmentReq {
   1242     #[inline]
   1243     fn default() -> Self {
   1244         Self::None(Hint::default())
   1245     }
   1246 }
   1247 impl AuthenticatorAttachmentReq {
   1248     /// Validates `self` against `other` ignoring [`Self::immutable_attachment`].
   1249     const fn validate(
   1250         self,
   1251         require_response: bool,
   1252         other: AuthenticatorAttachment,
   1253     ) -> Result<(), RegCeremonyErr> {
   1254         match self {
   1255             Self::None(_) => {
   1256                 if require_response && matches!(other, AuthenticatorAttachment::None) {
   1257                     Err(RegCeremonyErr::MissingAuthenticatorAttachment)
   1258                 } else {
   1259                     Ok(())
   1260                 }
   1261             }
   1262             Self::Platform(_) => match other {
   1263                 AuthenticatorAttachment::None => {
   1264                     if require_response {
   1265                         Err(RegCeremonyErr::MissingAuthenticatorAttachment)
   1266                     } else {
   1267                         Ok(())
   1268                     }
   1269                 }
   1270                 AuthenticatorAttachment::Platform => Ok(()),
   1271                 AuthenticatorAttachment::CrossPlatform => {
   1272                     Err(RegCeremonyErr::AuthenticatorAttachmentMismatch)
   1273                 }
   1274             },
   1275             Self::CrossPlatform(_) => match other {
   1276                 AuthenticatorAttachment::None => {
   1277                     if require_response {
   1278                         Err(RegCeremonyErr::MissingAuthenticatorAttachment)
   1279                     } else {
   1280                         Ok(())
   1281                     }
   1282                 }
   1283                 AuthenticatorAttachment::CrossPlatform => Ok(()),
   1284                 AuthenticatorAttachment::Platform => {
   1285                     Err(RegCeremonyErr::AuthenticatorAttachmentMismatch)
   1286                 }
   1287             },
   1288         }
   1289     }
   1290 }
   1291 /// [`AuthenticatorSelectionCriteria`](https://www.w3.org/TR/webauthn-3/#dictionary-authenticatorSelection).
   1292 #[derive(Clone, Copy, Debug)]
   1293 pub struct AuthenticatorSelectionCriteria {
   1294     /// [`authenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-authenticatorattachment).
   1295     pub authenticator_attachment: AuthenticatorAttachmentReq,
   1296     /// [`residentKey`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-residentkey).
   1297     pub resident_key: ResidentKeyRequirement,
   1298     /// [`userVerification`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-userverification).
   1299     pub user_verification: UserVerificationRequirement,
   1300 }
   1301 impl AuthenticatorSelectionCriteria {
   1302     /// Returns an `AuthenticatorSelectionCriteria` useful for passkeys (i.e., [`Self::resident_key`] is set to
   1303     /// [`ResidentKeyRequirement::Required`] and [`Self::user_verification`] is set to
   1304     /// [`UserVerificationRequirement::Required`]).
   1305     ///
   1306     /// # Examples
   1307     ///
   1308     /// ```
   1309     /// # use webauthn_rp::request::{
   1310     /// #     register::{
   1311     /// #         AuthenticatorAttachmentReq, AuthenticatorSelectionCriteria, ResidentKeyRequirement,
   1312     /// #     },
   1313     /// #     Hint, UserVerificationRequirement,
   1314     /// # };
   1315     /// let crit = AuthenticatorSelectionCriteria::passkey();
   1316     /// assert!(
   1317     ///     matches!(crit.authenticator_attachment, AuthenticatorAttachmentReq::None(hint) if matches!(hint, Hint::None))
   1318     /// );
   1319     /// assert!(matches!(
   1320     ///     crit.resident_key,
   1321     ///     ResidentKeyRequirement::Required
   1322     /// ));
   1323     /// assert!(matches!(
   1324     ///     crit.user_verification,
   1325     ///     UserVerificationRequirement::Required
   1326     /// ));
   1327     /// ```
   1328     #[inline]
   1329     #[must_use]
   1330     pub fn passkey() -> Self {
   1331         Self {
   1332             authenticator_attachment: AuthenticatorAttachmentReq::default(),
   1333             resident_key: ResidentKeyRequirement::Required,
   1334             user_verification: UserVerificationRequirement::Required,
   1335         }
   1336     }
   1337     /// Returns an `AuthenticatorSelectionCriteria` useful for second-factor flows (i.e., [`Self::resident_key`]
   1338     /// is set to [`ResidentKeyRequirement::Discouraged`] and [`Self::user_verification`] is set to
   1339     /// [`UserVerificationRequirement::Discouraged`]).
   1340     ///
   1341     /// Note some authenticators require user verification during credential registration (e.g.,
   1342     /// [CTAP 2.0 authenticators](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#authenticatorMakeCredential)).
   1343     /// When an authenticator supports both CTAP 2.0 and
   1344     /// [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)
   1345     /// protocols, user agents will sometimes fall back to U2F when `UserVerificationRequirement::Discouraged`
   1346     /// is requested since the latter allows for registration without user verification. If the user agent does not
   1347     /// do this, then users will have an inconsistent experience when authenticating an already-registered
   1348     /// credential. If this is undesirable, one can use [`UserVerificationRequirement::Required`] for this and
   1349     /// [`PublicKeyCredentialRequestOptions::user_verification`] at the expense of requiring a user to verify
   1350     /// themselves twice: once for the first factor and again here.
   1351     ///
   1352     /// # Examples
   1353     ///
   1354     /// ```
   1355     /// # use webauthn_rp::request::{
   1356     /// #     register::{
   1357     /// #         AuthenticatorAttachmentReq, AuthenticatorSelectionCriteria, ResidentKeyRequirement,
   1358     /// #     },
   1359     /// #     Hint, UserVerificationRequirement,
   1360     /// # };
   1361     /// let crit = AuthenticatorSelectionCriteria::second_factor();
   1362     /// assert!(
   1363     ///     matches!(crit.authenticator_attachment, AuthenticatorAttachmentReq::None(hint) if matches!(hint, Hint::None))
   1364     /// );
   1365     /// assert!(matches!(
   1366     ///     crit.resident_key,
   1367     ///     ResidentKeyRequirement::Discouraged
   1368     /// ));
   1369     /// assert!(matches!(
   1370     ///     crit.user_verification,
   1371     ///     UserVerificationRequirement::Discouraged
   1372     /// ));
   1373     /// ```
   1374     #[inline]
   1375     #[must_use]
   1376     pub fn second_factor() -> Self {
   1377         Self {
   1378             authenticator_attachment: AuthenticatorAttachmentReq::default(),
   1379             resident_key: ResidentKeyRequirement::Discouraged,
   1380             user_verification: UserVerificationRequirement::Discouraged,
   1381         }
   1382     }
   1383     /// Ensures a client-side credential was created when applicable. Also enforces `auth_attachment` when
   1384     /// applicable.
   1385     const fn validate(
   1386         self,
   1387         require_auth_attachment: bool,
   1388         auth_attachment: AuthenticatorAttachment,
   1389     ) -> Result<(), RegCeremonyErr> {
   1390         self.authenticator_attachment
   1391             .validate(require_auth_attachment, auth_attachment)
   1392     }
   1393 }
   1394 #[cfg(test)]
   1395 impl PartialEq for AuthenticatorSelectionCriteria {
   1396     fn eq(&self, other: &Self) -> bool {
   1397         self.authenticator_attachment == other.authenticator_attachment
   1398             && self.resident_key == other.resident_key
   1399             && self.user_verification == other.user_verification
   1400     }
   1401 }
   1402 /// Helper that verifies the overlap of [`CredentialCreationOptions::start_ceremony`] and
   1403 /// [`RegistrationServerState::decode`].
   1404 const fn validate_options_helper(
   1405     auth_crit: AuthenticatorSelectionCriteria,
   1406     extensions: ServerExtensionInfo,
   1407 ) -> Result<(), CreationOptionsErr> {
   1408     if matches!(
   1409         auth_crit.user_verification,
   1410         UserVerificationRequirement::Required
   1411     ) {
   1412         Ok(())
   1413     } else if matches!(
   1414         extensions.cred_protect,
   1415         CredProtect::UserVerificationRequired(_, _)
   1416     ) {
   1417         Err(CreationOptionsErr::CredProtectRequiredWithoutUserVerification)
   1418     } else {
   1419         Ok(())
   1420     }
   1421 }
   1422 /// The [`CredentialCreationOptions`](https://www.w3.org/TR/credential-management-1/#dictdef-credentialcreationoptions)
   1423 /// to send to the client when registering a new credential.
   1424 ///
   1425 /// Upon saving the [`RegistrationServerState`] returned from [`Self::start_ceremony`], one MUST send
   1426 /// [`RegistrationClientState`] to the client ASAP. After receiving the newly created [`Registration`], it is
   1427 /// validated using [`RegistrationServerState::verify`].
   1428 #[derive(Debug)]
   1429 pub struct CredentialCreationOptions<
   1430     'rp_id,
   1431     'user_name,
   1432     'user_display_name,
   1433     'user_id,
   1434     'prf_first,
   1435     'prf_second,
   1436     const USER_LEN: usize,
   1437 > {
   1438     /// [`mediation`](https://www.w3.org/TR/credential-management-1/#dom-credentialcreationoptions-mediation).
   1439     ///
   1440     /// Note if this is [`CredentialMediationRequirement::Conditional`], one may want to ensure
   1441     /// [`AuthenticatorSelectionCriteria::user_verification`] is not [`UserVerificationRequirement::Required`]
   1442     /// since some authenticators cannot enforce user verification during registration ceremonies when conditional
   1443     /// mediation is used. Do note that in the event passkeys are to be created, one may want to set
   1444     /// [`AuthenticationVerificationOptions::update_uv`] to `true` since [`Flag::user_verified`] will
   1445     /// potentially be `false`.
   1446     pub mediation: CredentialMediationRequirement,
   1447     /// `public-key` [credential type](https://www.w3.org/TR/credential-management-1/#sctn-cred-type-registry).
   1448     pub public_key: PublicKeyCredentialCreationOptions<
   1449         'rp_id,
   1450         'user_name,
   1451         'user_display_name,
   1452         'user_id,
   1453         'prf_first,
   1454         'prf_second,
   1455         USER_LEN,
   1456     >,
   1457 }
   1458 impl<
   1459     'rp_id,
   1460     'user_name,
   1461     'user_display_name,
   1462     'user_id,
   1463     'prf_first,
   1464     'prf_second,
   1465     const USER_LEN: usize,
   1466 >
   1467     CredentialCreationOptions<
   1468         'rp_id,
   1469         'user_name,
   1470         'user_display_name,
   1471         'user_id,
   1472         'prf_first,
   1473         'prf_second,
   1474         USER_LEN,
   1475     >
   1476 {
   1477     /// Sets [`Self::mediation`] to [`CredentialMediationRequirement::default`] and
   1478     /// [`Self::public_key`] to [`PublicKeyCredentialCreationOptions::passkey`].
   1479     #[expect(single_use_lifetimes, reason = "false positive")]
   1480     #[inline]
   1481     #[must_use]
   1482     pub fn passkey<'a: 'rp_id, 'b: 'user_name, 'c: 'user_display_name, 'd: 'user_id>(
   1483         rp_id: &'a RpId,
   1484         user: PublicKeyCredentialUserEntity<'b, 'c, 'd, USER_LEN>,
   1485         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Vec<u8>>>,
   1486     ) -> Self {
   1487         Self {
   1488             mediation: CredentialMediationRequirement::default(),
   1489             public_key: PublicKeyCredentialCreationOptions::passkey(
   1490                 rp_id,
   1491                 user,
   1492                 exclude_credentials,
   1493             ),
   1494         }
   1495     }
   1496     /// Convenience function for [`Self::passkey`] passing an empty `Vec`.
   1497     ///
   1498     /// This MUST only be used when this is the first credential for a user.
   1499     #[expect(single_use_lifetimes, reason = "false positive")]
   1500     #[inline]
   1501     #[must_use]
   1502     pub fn first_passkey<'a: 'rp_id, 'b: 'user_name, 'c: 'user_display_name, 'd: 'user_id>(
   1503         rp_id: &'a RpId,
   1504         user: PublicKeyCredentialUserEntity<'b, 'c, 'd, USER_LEN>,
   1505     ) -> Self {
   1506         Self::passkey(rp_id, user, Vec::new())
   1507     }
   1508     /// Convenience function for [`Self::first_passkey`] passing [`PublicKeyCredentialUserEntity::from`] applied
   1509     /// to `user_id` for `user`.
   1510     ///
   1511     /// This MUST only be used when user information is provided _after_ registration (e.g., when the client
   1512     /// sends user name and user display name along with [`Registration`]).
   1513     ///
   1514     /// Because user information is likely known for existing accounts, this will often only be called during
   1515     /// greenfield deployments.
   1516     #[expect(single_use_lifetimes, reason = "false positive")]
   1517     #[inline]
   1518     #[must_use]
   1519     pub fn first_passkey_with_blank_user_info<'a: 'rp_id, 'b: 'user_id>(
   1520         rp_id: &'a RpId,
   1521         user_id: &'b UserHandle<USER_LEN>,
   1522     ) -> Self {
   1523         Self::first_passkey(rp_id, user_id.into())
   1524     }
   1525     /// Sets [`Self::mediation`] to [`CredentialMediationRequirement::default`] and
   1526     /// [`Self::public_key`] to [`PublicKeyCredentialCreationOptions::second_factor`].
   1527     #[expect(single_use_lifetimes, reason = "false positive")]
   1528     #[inline]
   1529     #[must_use]
   1530     pub fn second_factor<'a: 'rp_id, 'b: 'user_name, 'c: 'user_display_name, 'd: 'user_id>(
   1531         rp_id: &'a RpId,
   1532         user: PublicKeyCredentialUserEntity<'b, 'c, 'd, USER_LEN>,
   1533         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Vec<u8>>>,
   1534     ) -> Self {
   1535         let mut opts = Self::passkey(rp_id, user, exclude_credentials);
   1536         opts.public_key.authenticator_selection = AuthenticatorSelectionCriteria::second_factor();
   1537         opts.public_key.extensions.cred_props = Some(ExtensionReq::Allow);
   1538         opts.public_key.extensions.cred_protect =
   1539             CredProtect::UserVerificationOptionalWithCredentialIdList(
   1540                 false,
   1541                 ExtensionInfo::AllowEnforceValue,
   1542             );
   1543         opts
   1544     }
   1545     /// Convenience function for [`Self::second_factor`] passing an empty `Vec`.
   1546     ///
   1547     /// This MUST only be used when this is the first credential for a user.
   1548     #[expect(single_use_lifetimes, reason = "false positive")]
   1549     #[inline]
   1550     #[must_use]
   1551     pub fn first_second_factor<'a: 'rp_id, 'b: 'user_name, 'c: 'user_display_name, 'd: 'user_id>(
   1552         rp_id: &'a RpId,
   1553         user: PublicKeyCredentialUserEntity<'b, 'c, 'd, USER_LEN>,
   1554     ) -> Self {
   1555         Self::second_factor(rp_id, user, Vec::new())
   1556     }
   1557     /// Begins the [registration ceremony](https://www.w3.org/TR/webauthn-3/#registration-ceremony) consuming
   1558     /// `self`. Note that the expiration [`Instant`]/[`SystemTime`] is saved, so `RegistrationClientState` MUST be
   1559     /// sent ASAP. In order to complete registration, the returned `RegistrationServerState` MUST be saved so that
   1560     /// it can later be used to verify the new credential with [`RegistrationServerState::verify`].
   1561     ///
   1562     /// # Errors
   1563     ///
   1564     /// Errors iff `self` contains incompatible configuration.
   1565     ///
   1566     /// # Examples
   1567     ///
   1568     /// ```
   1569     /// # #[cfg(not(feature = "serializable_server_state"))]
   1570     /// # use std::time::Instant;
   1571     /// # #[cfg(not(feature = "serializable_server_state"))]
   1572     /// # use webauthn_rp::request::TimedCeremony as _;
   1573     /// # use webauthn_rp::request::{
   1574     /// #     register::{CredentialCreationOptions, PublicKeyCredentialUserEntity, UserHandle64},
   1575     /// #     AsciiDomain, RpId
   1576     /// # };
   1577     /// # #[cfg(not(feature = "serializable_server_state"))]
   1578     /// assert!(
   1579     ///     CredentialCreationOptions::passkey(
   1580     ///         &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
   1581     ///         PublicKeyCredentialUserEntity {
   1582     ///             name: "bernard.riemann".try_into()?,
   1583     ///             id: &UserHandle64::new(),
   1584     ///             display_name: Some("Georg Friedrich Bernhard Riemann".try_into()?)
   1585     ///         },
   1586     ///         Vec::new()
   1587     ///     ).start_ceremony()?.0.expiration() > Instant::now()
   1588     /// );
   1589     /// # Ok::<_, webauthn_rp::AggErr>(())
   1590     /// ```
   1591     #[inline]
   1592     pub fn start_ceremony(
   1593         mut self,
   1594     ) -> Result<
   1595         (
   1596             RegistrationServerState<USER_LEN>,
   1597             RegistrationClientState<
   1598                 'rp_id,
   1599                 'user_name,
   1600                 'user_display_name,
   1601                 'user_id,
   1602                 'prf_first,
   1603                 'prf_second,
   1604                 USER_LEN,
   1605             >,
   1606         ),
   1607         CreationOptionsErr,
   1608     > {
   1609         let extensions = self.public_key.extensions.into();
   1610         validate_options_helper(self.public_key.authenticator_selection, extensions).and_then(
   1611             |()| {
   1612                 #[cfg(not(feature = "serializable_server_state"))]
   1613                 let now = Instant::now();
   1614                 #[cfg(feature = "serializable_server_state")]
   1615                 let now = SystemTime::now();
   1616                 now.checked_add(Duration::from_millis(
   1617                     NonZeroU64::from(self.public_key.timeout).get(),
   1618                 ))
   1619                 .ok_or(CreationOptionsErr::InvalidTimeout)
   1620                 .map(|expiration| {
   1621                     // We remove duplicates. The order has no significance, so this is OK.
   1622                     self.public_key
   1623                         .exclude_credentials
   1624                         .sort_unstable_by(|a, b| a.id.as_ref().cmp(b.id.as_ref()));
   1625                     self.public_key
   1626                         .exclude_credentials
   1627                         .dedup_by(|a, b| a.id.as_ref() == b.id.as_ref());
   1628                     (
   1629                         RegistrationServerState {
   1630                             mediation: self.mediation,
   1631                             challenge: SentChallenge(self.public_key.challenge.0),
   1632                             pub_key_cred_params: self.public_key.pub_key_cred_params,
   1633                             authenticator_selection: self.public_key.authenticator_selection,
   1634                             extensions,
   1635                             expiration,
   1636                             user_id: *self.public_key.user.id,
   1637                         },
   1638                         RegistrationClientState(self),
   1639                     )
   1640                 })
   1641             },
   1642         )
   1643     }
   1644 }
   1645 /// `CredentialCreationOptions` based on a [`UserHandle64`].
   1646 pub type CredentialCreationOptions64<
   1647     'rp_id,
   1648     'user_name,
   1649     'user_display_name,
   1650     'user_id,
   1651     'prf_first,
   1652     'prf_second,
   1653 > = CredentialCreationOptions<
   1654     'rp_id,
   1655     'user_name,
   1656     'user_display_name,
   1657     'user_id,
   1658     'prf_first,
   1659     'prf_second,
   1660     USER_HANDLE_MAX_LEN,
   1661 >;
   1662 /// `CredentialCreationOptions` based on a [`UserHandle16`].
   1663 pub type CredentialCreationOptions16<
   1664     'rp_id,
   1665     'user_name,
   1666     'user_display_name,
   1667     'user_id,
   1668     'prf_first,
   1669     'prf_second,
   1670 > = CredentialCreationOptions<
   1671     'rp_id,
   1672     'user_name,
   1673     'user_display_name,
   1674     'user_id,
   1675     'prf_first,
   1676     'prf_second,
   1677     16,
   1678 >;
   1679 /// The [`PublicKeyCredentialCreationOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialcreationoptions)
   1680 /// to send to the client when registering a new credential.
   1681 #[derive(Debug)]
   1682 pub struct PublicKeyCredentialCreationOptions<
   1683     'rp_id,
   1684     'user_name,
   1685     'user_display_name,
   1686     'user_id,
   1687     'prf_first,
   1688     'prf_second,
   1689     const USER_LEN: usize,
   1690 > {
   1691     /// [`rp`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-rp).
   1692     pub rp_id: &'rp_id RpId,
   1693     /// [`user`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-user).
   1694     pub user: PublicKeyCredentialUserEntity<'user_name, 'user_display_name, 'user_id, USER_LEN>,
   1695     /// [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-challenge).
   1696     pub challenge: Challenge,
   1697     /// [`pubKeyCredParams`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-pubkeycredparams).
   1698     pub pub_key_cred_params: CoseAlgorithmIdentifiers,
   1699     /// [`timeout`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-timeout).
   1700     ///
   1701     /// Note we require a positive value despite the spec allowing an optional nonnegative value. This jives
   1702     /// with the fact that in-memory storage is required when `serializable_server_state` is not enabled
   1703     /// when attesting credentials as no timeout would make out-of-memory (OOM) conditions more likely.
   1704     pub timeout: NonZeroU32,
   1705     /// [`excludeCredentials`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-excludecredentials).
   1706     pub exclude_credentials: Vec<PublicKeyCredentialDescriptor<Vec<u8>>>,
   1707     /// [`authenticatorSelection`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-authenticatorselection).
   1708     pub authenticator_selection: AuthenticatorSelectionCriteria,
   1709     /// [`extensions`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-extensions).
   1710     pub extensions: Extension<'prf_first, 'prf_second>,
   1711 }
   1712 impl<'rp_id, 'user_name, 'user_display_name, 'user_id, const USER_LEN: usize>
   1713     PublicKeyCredentialCreationOptions<
   1714         'rp_id,
   1715         'user_name,
   1716         'user_display_name,
   1717         'user_id,
   1718         '_,
   1719         '_,
   1720         USER_LEN,
   1721     >
   1722 {
   1723     /// Most deployments of passkeys should use this function. Specifically deployments that are both userless and
   1724     /// passwordless and desire multi-factor authentication (MFA) to be done entirely on the authenticator. It
   1725     /// is important `exclude_credentials` contains the information for _all_ [`RegisteredCredential`]s registered to
   1726     /// [`PublicKeyCredentialUserEntity::id`] to avoid accidentally overwriting existing credentials that
   1727     /// have been previously registered.
   1728     ///
   1729     /// Creates a `PublicKeyCredentialCreationOptions` that requires the authenticator to create a client-side
   1730     /// discoverable credential enforcing any form of user verification. [`Self::timeout`] is [`FIVE_MINUTES`].
   1731     /// [`Extension::cred_protect`] with [`CredProtect::UserVerificationRequired`] with `false` and
   1732     /// [`ExtensionInfo::AllowEnforceValue`] is used.
   1733     ///
   1734     /// # Examples
   1735     ///
   1736     /// ```
   1737     /// # use webauthn_rp::request::{
   1738     /// #     register::{
   1739     /// #         PublicKeyCredentialCreationOptions, PublicKeyCredentialUserEntity, UserHandle64
   1740     /// #     },
   1741     /// #     AsciiDomain, RpId, UserVerificationRequirement
   1742     /// # };
   1743     /// assert!(matches!(
   1744     ///     PublicKeyCredentialCreationOptions::passkey(
   1745     ///         &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
   1746     ///         PublicKeyCredentialUserEntity {
   1747     ///             name: "archimedes.of.syracuse".try_into()?,
   1748     ///             id: &UserHandle64::new(),
   1749     ///             display_name: Some("Αρχιμήδης ο Συρακούσιος".try_into()?),
   1750     ///         },
   1751     ///         Vec::new()
   1752     ///     )
   1753     ///     .authenticator_selection.user_verification, UserVerificationRequirement::Required
   1754     /// ));
   1755     /// # Ok::<_, webauthn_rp::AggErr>(())
   1756     /// ```
   1757     #[expect(single_use_lifetimes, reason = "false positive")]
   1758     #[inline]
   1759     #[must_use]
   1760     pub fn passkey<'a: 'rp_id, 'b: 'user_name, 'c: 'user_display_name, 'd: 'user_id>(
   1761         rp_id: &'a RpId,
   1762         user: PublicKeyCredentialUserEntity<'b, 'c, 'd, USER_LEN>,
   1763         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Vec<u8>>>,
   1764     ) -> Self {
   1765         Self {
   1766             rp_id,
   1767             user,
   1768             challenge: Challenge::new(),
   1769             pub_key_cred_params: CoseAlgorithmIdentifiers::default(),
   1770             timeout: FIVE_MINUTES,
   1771             exclude_credentials,
   1772             authenticator_selection: AuthenticatorSelectionCriteria::passkey(),
   1773             extensions: Extension {
   1774                 cred_props: None,
   1775                 cred_protect: CredProtect::UserVerificationRequired(
   1776                     false,
   1777                     ExtensionInfo::AllowEnforceValue,
   1778                 ),
   1779                 min_pin_length: None,
   1780                 prf: None,
   1781             },
   1782         }
   1783     }
   1784     /// Convenience function for [`Self::passkey`] passing an empty `Vec`.
   1785     ///
   1786     /// This MUST only be used when this is the first credential for a user.
   1787     #[expect(single_use_lifetimes, reason = "false positive")]
   1788     #[inline]
   1789     #[must_use]
   1790     pub fn first_passkey<'a: 'rp_id, 'b: 'user_name, 'c: 'user_display_name, 'd: 'user_id>(
   1791         rp_id: &'a RpId,
   1792         user: PublicKeyCredentialUserEntity<'b, 'c, 'd, USER_LEN>,
   1793     ) -> Self {
   1794         Self::passkey(rp_id, user, Vec::new())
   1795     }
   1796     /// Convenience function for [`Self::first_passkey`] passing [`PublicKeyCredentialUserEntity::from`] applied
   1797     /// to `user_id` for `user`.
   1798     ///
   1799     /// This MUST only be used when user information is provided _after_ registration (e.g., when the client
   1800     /// sends user name and user display name along with [`Registration`]).
   1801     ///
   1802     /// Because user information is likely known for existing accounts, this will often only be called during
   1803     /// greenfield deployments.
   1804     #[expect(single_use_lifetimes, reason = "false positive")]
   1805     #[inline]
   1806     #[must_use]
   1807     pub fn first_passkey_with_blank_user_info<'a: 'rp_id, 'b: 'user_id>(
   1808         rp_id: &'a RpId,
   1809         user_id: &'b UserHandle<USER_LEN>,
   1810     ) -> Self {
   1811         Self::first_passkey(rp_id, user_id.into())
   1812     }
   1813     /// Deployments that want to incorporate a "something a user has" factor into a larger multi-factor
   1814     /// authentication (MFA) setup. Specifically deployments that are _not_ userless or passwordless. It
   1815     /// is important `exclude_credentials` contains the information for _all_ [`RegisteredCredential`]s registered
   1816     /// to [`PublicKeyCredentialUserEntity::id`] to avoid accidentally overwriting existing credentials that
   1817     /// have been previously registered.
   1818     ///
   1819     /// Creates a `PublicKeyCredentialCreationOptions` that prefers the authenticator to create a server-side
   1820     /// credential without requiring user verification. [`Self::timeout`] is [`FIVE_MINUTES`].
   1821     /// [`Extension::cred_props`] is [`ExtensionReq::Allow`]. [`Extension::cred_protect`] is
   1822     /// [`CredProtect::UserVerificationOptionalWithCredentialIdList`] with `false` and
   1823     /// [`ExtensionInfo::AllowEnforceValue`].
   1824     ///
   1825     /// Note some authenticators require user verification during credential registration (e.g.,
   1826     /// [CTAP 2.0 authenticators](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html#authenticatorMakeCredential)).
   1827     /// When an authenticator supports both CTAP 2.0 and
   1828     /// [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)
   1829     /// protocols, user agents will sometimes fall back to U2F when [`UserVerificationRequirement::Discouraged`]
   1830     /// is requested since the latter allows for registration without user verification. If the user agent does not
   1831     /// do this, then users will have an inconsistent experience when authenticating an already-registered
   1832     /// credential. If this is undesirable, one can use [`UserVerificationRequirement::Required`] for this and
   1833     /// [`PublicKeyCredentialRequestOptions::user_verification`] at the expense of requiring a user to verify
   1834     /// themselves twice: once for the first factor and again here.
   1835     ///
   1836     /// # Examples
   1837     ///
   1838     /// ```
   1839     /// # use webauthn_rp::request::{register::{
   1840     /// #     PublicKeyCredentialCreationOptions, PublicKeyCredentialUserEntity, UserHandle64
   1841     /// # }, AsciiDomain, RpId};
   1842     /// assert_eq!(
   1843     ///     PublicKeyCredentialCreationOptions::second_factor(
   1844     ///         &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
   1845     ///         PublicKeyCredentialUserEntity {
   1846     ///             name: "carl.gauss".try_into()?,
   1847     ///             id: &UserHandle64::new(),
   1848     ///             display_name: Some("Johann Carl Friedrich Gauß".try_into()?),
   1849     ///         },
   1850     ///         Vec::new()
   1851     ///     )
   1852     ///     .timeout
   1853     ///     .get(),
   1854     ///     300_000
   1855     /// );
   1856     /// # Ok::<_, webauthn_rp::AggErr>(())
   1857     /// ```
   1858     #[expect(single_use_lifetimes, reason = "false positive")]
   1859     #[inline]
   1860     #[must_use]
   1861     pub fn second_factor<'a: 'rp_id, 'b: 'user_name, 'c: 'user_display_name, 'd: 'user_id>(
   1862         rp_id: &'a RpId,
   1863         user: PublicKeyCredentialUserEntity<'b, 'c, 'd, USER_LEN>,
   1864         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Vec<u8>>>,
   1865     ) -> Self {
   1866         let mut opts = Self::passkey(rp_id, user, exclude_credentials);
   1867         opts.authenticator_selection = AuthenticatorSelectionCriteria::second_factor();
   1868         opts.extensions.cred_props = Some(ExtensionReq::Allow);
   1869         opts.extensions.cred_protect = CredProtect::UserVerificationOptionalWithCredentialIdList(
   1870             false,
   1871             ExtensionInfo::AllowEnforceValue,
   1872         );
   1873         opts
   1874     }
   1875     /// Convenience function for [`Self::second_factor`] passing an empty `Vec`.
   1876     ///
   1877     /// This MUST only be used when this is the first credential for a user.
   1878     #[expect(single_use_lifetimes, reason = "false positive")]
   1879     #[inline]
   1880     #[must_use]
   1881     pub fn first_second_factor<'a: 'rp_id, 'b: 'user_name, 'c: 'user_display_name, 'd: 'user_id>(
   1882         rp_id: &'a RpId,
   1883         user: PublicKeyCredentialUserEntity<'b, 'c, 'd, USER_LEN>,
   1884     ) -> Self {
   1885         Self::second_factor(rp_id, user, Vec::new())
   1886     }
   1887 }
   1888 /// `PublicKeyCredentialCreationOptions` based on a [`UserHandle64`].
   1889 pub type PublicKeyCredentialCreationOptions64<
   1890     'rp_id,
   1891     'user_name,
   1892     'user_display_name,
   1893     'user_id,
   1894     'prf_first,
   1895     'prf_second,
   1896 > = PublicKeyCredentialCreationOptions<
   1897     'rp_id,
   1898     'user_name,
   1899     'user_display_name,
   1900     'user_id,
   1901     'prf_first,
   1902     'prf_second,
   1903     USER_HANDLE_MAX_LEN,
   1904 >;
   1905 /// `PublicKeyCredentialCreationOptions` based on a [`UserHandle16`].
   1906 pub type PublicKeyCredentialCreationOptions16<
   1907     'rp_id,
   1908     'user_name,
   1909     'user_display_name,
   1910     'user_id,
   1911     'prf_first,
   1912     'prf_second,
   1913 > = PublicKeyCredentialCreationOptions<
   1914     'rp_id,
   1915     'user_name,
   1916     'user_display_name,
   1917     'user_id,
   1918     'prf_first,
   1919     'prf_second,
   1920     16,
   1921 >;
   1922 /// Container of a [`CredentialCreationOptions`] that has been used to start the registration ceremony.
   1923 /// This gets sent to the client ASAP.
   1924 #[derive(Debug)]
   1925 pub struct RegistrationClientState<
   1926     'rp_id,
   1927     'user_name,
   1928     'user_display_name,
   1929     'user_id,
   1930     'prf_first,
   1931     'prf_second,
   1932     const USER_LEN: usize,
   1933 >(
   1934     CredentialCreationOptions<
   1935         'rp_id,
   1936         'user_name,
   1937         'user_display_name,
   1938         'user_id,
   1939         'prf_first,
   1940         'prf_second,
   1941         USER_LEN,
   1942     >,
   1943 );
   1944 impl<
   1945     'rp_id,
   1946     'user_name,
   1947     'user_display_name,
   1948     'user_id,
   1949     'prf_first,
   1950     'prf_second,
   1951     const USER_LEN: usize,
   1952 >
   1953     RegistrationClientState<
   1954         'rp_id,
   1955         'user_name,
   1956         'user_display_name,
   1957         'user_id,
   1958         'prf_first,
   1959         'prf_second,
   1960         USER_LEN,
   1961     >
   1962 {
   1963     /// Returns the `CredentialCreationOptions` that was used to start a registration ceremony.
   1964     ///
   1965     /// # Examples
   1966     ///
   1967     /// ```
   1968     /// # use webauthn_rp::request::{register::{
   1969     /// #     CoseAlgorithmIdentifiers, CredentialCreationOptions,
   1970     /// #     PublicKeyCredentialUserEntity, UserHandle64,
   1971     /// # }, AsciiDomain, RpId};
   1972     /// assert_eq!(
   1973     ///     CredentialCreationOptions::passkey(
   1974     ///         &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
   1975     ///         PublicKeyCredentialUserEntity {
   1976     ///             name: "david.hilbert".try_into()?,
   1977     ///             id: &UserHandle64::new(),
   1978     ///             display_name: Some("David Hilbert".try_into()?)
   1979     ///         },
   1980     ///         Vec::new()
   1981     ///     )
   1982     ///     .start_ceremony()?
   1983     ///     .1
   1984     ///     .options()
   1985     ///     .public_key
   1986     ///     .rp_id.as_ref(),
   1987     ///     "example.com"
   1988     /// );
   1989     /// # Ok::<_, webauthn_rp::AggErr>(())
   1990     /// ```
   1991     #[inline]
   1992     #[must_use]
   1993     pub const fn options(
   1994         &self,
   1995     ) -> &CredentialCreationOptions<
   1996         'rp_id,
   1997         'user_name,
   1998         'user_display_name,
   1999         'user_id,
   2000         'prf_first,
   2001         'prf_second,
   2002         USER_LEN,
   2003     > {
   2004         &self.0
   2005     }
   2006 }
   2007 /// `RegistrationClientState` based on a [`UserHandle64`].
   2008 pub type RegistrationClientState64<
   2009     'rp_id,
   2010     'user_name,
   2011     'user_display_name,
   2012     'user_id,
   2013     'prf_first,
   2014     'prf_second,
   2015 > = RegistrationClientState<
   2016     'rp_id,
   2017     'user_name,
   2018     'user_display_name,
   2019     'user_id,
   2020     'prf_first,
   2021     'prf_second,
   2022     USER_HANDLE_MAX_LEN,
   2023 >;
   2024 /// `RegistrationClientState` based on a [`UserHandle16`].
   2025 pub type RegistrationClientState16<
   2026     'rp_id,
   2027     'user_name,
   2028     'user_display_name,
   2029     'user_id,
   2030     'prf_first,
   2031     'prf_second,
   2032 > = RegistrationClientState<
   2033     'rp_id,
   2034     'user_name,
   2035     'user_display_name,
   2036     'user_id,
   2037     'prf_first,
   2038     'prf_second,
   2039     16,
   2040 >;
   2041 /// Additional verification options to perform in [`RegistrationServerState::verify`].
   2042 #[derive(Clone, Copy, Debug)]
   2043 pub struct RegistrationVerificationOptions<'origins, 'top_origins, O, T> {
   2044     /// Origins to use for [origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin).
   2045     ///
   2046     /// When this is empty, the origin that will be used will be based on
   2047     /// the [`RpId`] passed to [`RegistrationServerState::verify`]. If [`RpId::Domain`] or [`RpId::StaticDomain`],
   2048     /// then the [`DomainOrigin`] returned from passing [`AsciiDomain::as_ref`] and [`AsciiDomainStatic::as_str`]
   2049     /// to [`DomainOrigin::new`] respectively will be used; otherwise the [`Url`] in [`RpId::Url`] will be used.
   2050     pub allowed_origins: &'origins [O],
   2051     /// [Top-level origins](https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-top-level-origin)
   2052     /// to use for [origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin).
   2053     ///
   2054     /// When this is `Some`, [`CollectedClientData::cross_origin`] is allowed to be `true`. When the contained
   2055     /// `slice` is empty, [`CollectedClientData::top_origin`] must be `None`. When this is `None`,
   2056     /// `CollectedClientData::cross_origin` must be `false` and `CollectedClientData::top_origin` must be `None`.
   2057     pub allowed_top_origins: Option<&'top_origins [T]>,
   2058     /// The required [`Backup`] state of the credential.
   2059     pub backup_requirement: BackupReq,
   2060     /// Error when unsolicited extensions are sent back iff `true`.
   2061     pub error_on_unsolicited_extensions: bool,
   2062     /// [`AuthenticatorAttachment`] must be sent iff `true`.
   2063     pub require_authenticator_attachment: bool,
   2064     /// [`CollectedClientData::from_client_data_json_relaxed`] is used to extract [`CollectedClientData`] iff `true`.
   2065     #[cfg_attr(docsrs, doc(cfg(feature = "serde_relaxed")))]
   2066     #[cfg(feature = "serde_relaxed")]
   2067     pub client_data_json_relaxed: bool,
   2068 }
   2069 impl<O, T> RegistrationVerificationOptions<'_, '_, O, T> {
   2070     /// Returns `Self` such that [`Self::allowed_origins`] is empty, [`Self::allowed_top_origins`] is `None`,
   2071     /// [`Self::backup_requirement`] is [`BackupReq::None`], [`Self::error_on_unsolicited_extensions`] is `true`,
   2072     /// [`Self::require_authenticator_attachment`] is `false`, and [`Self::client_data_json_relaxed`] is
   2073     /// `true`.
   2074     ///
   2075     /// Note `O` and `T` should implement `PartialEq<Origin<'_>>` (e.g., `&str`).
   2076     #[inline]
   2077     #[must_use]
   2078     pub const fn new() -> Self {
   2079         Self {
   2080             allowed_origins: [].as_slice(),
   2081             allowed_top_origins: None,
   2082             backup_requirement: BackupReq::None,
   2083             error_on_unsolicited_extensions: true,
   2084             require_authenticator_attachment: false,
   2085             #[cfg(feature = "serde_relaxed")]
   2086             client_data_json_relaxed: true,
   2087         }
   2088     }
   2089 }
   2090 impl<O, T> Default for RegistrationVerificationOptions<'_, '_, O, T> {
   2091     /// Same as [`Self::new`].
   2092     #[inline]
   2093     fn default() -> Self {
   2094         Self {
   2095             allowed_origins: &[],
   2096             allowed_top_origins: None,
   2097             backup_requirement: BackupReq::default(),
   2098             error_on_unsolicited_extensions: true,
   2099             require_authenticator_attachment: false,
   2100             #[cfg(feature = "serde_relaxed")]
   2101             client_data_json_relaxed: true,
   2102         }
   2103     }
   2104 }
   2105 /// `PrfInput` without the actual data sent to reduce memory usage when storing [`RegistrationServerState`] in an
   2106 /// in-memory collection.
   2107 #[derive(Clone, Copy, Debug)]
   2108 enum ServerPrfInfo {
   2109     /// No `PrfInput`.
   2110     None,
   2111     /// `PrfInput::second` was `None`.
   2112     One(ExtensionInfo),
   2113     /// `PrfInput::second` was `Some`.
   2114     Two(ExtensionInfo),
   2115 }
   2116 #[cfg(test)]
   2117 impl PartialEq for ServerPrfInfo {
   2118     fn eq(&self, other: &Self) -> bool {
   2119         match *self {
   2120             Self::None => matches!(*other, Self::None),
   2121             Self::One(info) => matches!(*other, Self::One(info2) if info == info2),
   2122             Self::Two(info) => matches!(*other, Self::Two(info2) if info == info2),
   2123         }
   2124     }
   2125 }
   2126 impl From<Option<(PrfInput<'_, '_>, ExtensionInfo)>> for ServerPrfInfo {
   2127     fn from(value: Option<(PrfInput<'_, '_>, ExtensionInfo)>) -> Self {
   2128         value.map_or(Self::None, |val| {
   2129             val.0
   2130                 .second
   2131                 .map_or_else(|| Self::One(val.1), |_| Self::Two(val.1))
   2132         })
   2133     }
   2134 }
   2135 /// `Extension` without the actual data sent to reduce memory usage when storing [`AuthenticationServerState`]
   2136 /// in an in-memory collection.
   2137 #[derive(Clone, Copy, Debug)]
   2138 struct ServerExtensionInfo {
   2139     /// `Extension::cred_props`.
   2140     cred_props: Option<ExtensionReq>,
   2141     /// `Extension::cred_protect`.
   2142     cred_protect: CredProtect,
   2143     /// `Extension::min_pin_length`.
   2144     min_pin_length: Option<(FourToSixtyThree, ExtensionInfo)>,
   2145     /// `Extension::prf`.
   2146     prf: ServerPrfInfo,
   2147 }
   2148 impl ServerExtensionInfo {
   2149     /// Validates the extensions.
   2150     fn validate(
   2151         self,
   2152         client_ext: ClientExtensionsOutputs,
   2153         auth_ext: AuthenticatorExtensionOutput,
   2154         error_unsolicited: bool,
   2155     ) -> Result<(), ExtensionErr> {
   2156         if error_unsolicited {
   2157             self.validate_unsolicited(client_ext, auth_ext)
   2158         } else {
   2159             Ok(())
   2160         }
   2161         .and_then(|()| {
   2162             self.validate_required(client_ext, auth_ext)
   2163                 .and_then(|()| self.validate_value(client_ext, auth_ext))
   2164         })
   2165     }
   2166     /// Validates if there are any unsolicited extensions.
   2167     ///
   2168     /// Note no distinction is made between an extension that is empty and one that is not (i.e., we are checking
   2169     ///  purely for the existence of extension keys).
   2170     fn validate_unsolicited(
   2171         mut self,
   2172         client_ext: ClientExtensionsOutputs,
   2173         auth_ext: AuthenticatorExtensionOutput,
   2174     ) -> Result<(), ExtensionErr> {
   2175         // For simpler code, we artificially set non-requested extensions after verifying there was not an error
   2176         // and recursively call this function. There are so few extensions and the checks are fast that there
   2177         // should be no worry of stack overflow or performance overhead.
   2178         if self.cred_props.is_some() {
   2179             if !matches!(self.cred_protect, CredProtect::None) {
   2180                 if self.min_pin_length.is_some() {
   2181                     // This is the last extension, so recursion stops here.
   2182                     if !matches!(self.prf, ServerPrfInfo::None) {
   2183                         Ok(())
   2184                     } else if client_ext.prf.is_some() {
   2185                         Err(ExtensionErr::ForbiddenPrf)
   2186                     } else if !matches!(auth_ext.hmac_secret, HmacSecret::None) {
   2187                         Err(ExtensionErr::ForbiddenHmacSecret)
   2188                     } else {
   2189                         Ok(())
   2190                     }
   2191                 } else if auth_ext.min_pin_length.is_some() {
   2192                     Err(ExtensionErr::ForbiddenMinPinLength)
   2193                 } else {
   2194                     // Pretend to set `minPinLength`, so we can check `prf`.
   2195                     self.min_pin_length =
   2196                         Some((FourToSixtyThree::Four, ExtensionInfo::RequireEnforceValue));
   2197                     self.validate_unsolicited(client_ext, auth_ext)
   2198                 }
   2199             } else if !matches!(auth_ext.cred_protect, CredentialProtectionPolicy::None) {
   2200                 Err(ExtensionErr::ForbiddenCredProtect)
   2201             } else {
   2202                 // Pretend to set `credProtect`, so we can check `minPinLength` and `prf` extensions.
   2203                 self.cred_protect = CredProtect::UserVerificationOptional(
   2204                     false,
   2205                     ExtensionInfo::RequireEnforceValue,
   2206                 );
   2207                 self.validate_unsolicited(client_ext, auth_ext)
   2208             }
   2209         } else if client_ext.cred_props.is_some() {
   2210             Err(ExtensionErr::ForbiddenCredProps)
   2211         } else {
   2212             // Pretend to set `credProps`; so we can check `credProtect`, `minPinLength`, and `prf` extensions.
   2213             self.cred_props = Some(ExtensionReq::Require);
   2214             self.validate_unsolicited(client_ext, auth_ext)
   2215         }
   2216     }
   2217     /// Validates if any required extensions don't have a corresponding response.
   2218     ///
   2219     /// Note empty extensions are treated as missing. For example when requiring the `credProps` extension,
   2220     /// all of the following responses would lead to a failure:
   2221     /// `{"clientExtensionResults":{}}`: no extensions.
   2222     /// `{"clientExtensionResults":{"prf":true}}`: only the `prf` extension.
   2223     /// `{"clientExtensionResults":{"credProps":{}}}`: empty `credProps` extension.
   2224     /// `{"clientExtensionResults":{"credProps":{"foo":false}}}`: `credProps` extension doesn't contain at least one
   2225     /// expected field (i.e., still "empty").
   2226     fn validate_required(
   2227         self,
   2228         client_ext: ClientExtensionsOutputs,
   2229         auth_ext: AuthenticatorExtensionOutput,
   2230     ) -> Result<(), ExtensionErr> {
   2231         // We don't check `self.cred_protect` since `CredProtect::validate` checks for both a required response
   2232         // and value enforcement; thus it only needs to be checked once (which it is in `Self::validate_value`).
   2233         self.cred_props
   2234             .map_or(Ok(()), |info| {
   2235                 if matches!(info, ExtensionReq::Require) {
   2236                     if client_ext
   2237                         .cred_props
   2238                         .is_some_and(|props| props.rk.is_some())
   2239                     {
   2240                         Ok(())
   2241                     } else {
   2242                         Err(ExtensionErr::MissingCredProps)
   2243                     }
   2244                 } else {
   2245                     Ok(())
   2246                 }
   2247             })
   2248             .and_then(|()| {
   2249                 self.min_pin_length
   2250                     .map_or(Ok(()), |info| {
   2251                         if matches!(
   2252                             info.1,
   2253                             ExtensionInfo::RequireEnforceValue
   2254                                 | ExtensionInfo::RequireDontEnforceValue
   2255                         ) {
   2256                             auth_ext
   2257                                 .min_pin_length
   2258                                 .ok_or(ExtensionErr::MissingMinPinLength)
   2259                                 .map(|_| ())
   2260                         } else {
   2261                             Ok(())
   2262                         }
   2263                     })
   2264                     .and_then(|()| match self.prf {
   2265                         ServerPrfInfo::None => Ok(()),
   2266                         ServerPrfInfo::One(info) | ServerPrfInfo::Two(info) => {
   2267                             if matches!(
   2268                                 info,
   2269                                 ExtensionInfo::RequireEnforceValue
   2270                                     | ExtensionInfo::RequireDontEnforceValue
   2271                             ) {
   2272                                 if client_ext.prf.is_some() {
   2273                                     Ok(())
   2274                                 } else {
   2275                                     Err(ExtensionErr::MissingPrf)
   2276                                 }
   2277                             } else {
   2278                                 Ok(())
   2279                             }
   2280                         }
   2281                     })
   2282             })
   2283     }
   2284     /// Validates the value of any extensions sent from the client.
   2285     ///
   2286     /// Note missing and empty extensions are always OK.
   2287     fn validate_value(
   2288         self,
   2289         client_ext: ClientExtensionsOutputs,
   2290         auth_ext: AuthenticatorExtensionOutput,
   2291     ) -> Result<(), ExtensionErr> {
   2292         // This also checks for a missing response. Instead of duplicating that check, we only call
   2293         // `self.cred_protect.validate` once here and not also in `Self::validate_required`.
   2294         self.cred_protect
   2295             .validate(auth_ext.cred_protect)
   2296             .and_then(|()| {
   2297                 self.min_pin_length
   2298                     .map_or(Ok(()), |info| {
   2299                         if matches!(
   2300                             info.1,
   2301                             ExtensionInfo::RequireEnforceValue | ExtensionInfo::AllowEnforceValue
   2302                         ) {
   2303                             auth_ext.min_pin_length.map_or(Ok(()), |pin| {
   2304                                 if pin >= info.0 {
   2305                                     Ok(())
   2306                                 } else {
   2307                                     Err(ExtensionErr::InvalidMinPinLength(info.0, pin))
   2308                                 }
   2309                             })
   2310                         } else {
   2311                             Ok(())
   2312                         }
   2313                     })
   2314                     .and_then(|()| match self.prf {
   2315                         ServerPrfInfo::None => Ok(()),
   2316                         ServerPrfInfo::One(info) | ServerPrfInfo::Two(info) => {
   2317                             if matches!(
   2318                                 info,
   2319                                 ExtensionInfo::RequireEnforceValue
   2320                                     | ExtensionInfo::AllowEnforceValue
   2321                             ) {
   2322                                 client_ext
   2323                                     .prf
   2324                                     .map_or(Ok(()), |prf| {
   2325                                         if prf.enabled {
   2326                                             Ok(())
   2327                                         } else {
   2328                                             Err(ExtensionErr::InvalidPrfValue)
   2329                                         }
   2330                                     })
   2331                                     .and({
   2332                                         if matches!(auth_ext.hmac_secret, HmacSecret::NotEnabled) {
   2333                                             Err(ExtensionErr::InvalidHmacSecretValue)
   2334                                         } else {
   2335                                             Ok(())
   2336                                         }
   2337                                     })
   2338                             } else {
   2339                                 Ok(())
   2340                             }
   2341                         }
   2342                     })
   2343             })
   2344     }
   2345 }
   2346 impl From<Extension<'_, '_>> for ServerExtensionInfo {
   2347     fn from(value: Extension<'_, '_>) -> Self {
   2348         Self {
   2349             cred_props: value.cred_props,
   2350             cred_protect: value.cred_protect,
   2351             min_pin_length: value.min_pin_length,
   2352             prf: value.prf.into(),
   2353         }
   2354     }
   2355 }
   2356 #[cfg(test)]
   2357 impl PartialEq for ServerExtensionInfo {
   2358     fn eq(&self, other: &Self) -> bool {
   2359         self.prf == other.prf
   2360     }
   2361 }
   2362 // This is essentially the `PublicKeyCredentialCreationOptions` used to create it; however to reduce
   2363 // memory usage, we remove all unnecessary data making an instance of this 48 bytes in size on
   2364 // `x86_64-unknown-linux-gnu` platforms when `USER_LEN` is `USER_HANDLE_MIN_LEN`.
   2365 /// State needed to be saved when beginning the registration ceremony.
   2366 ///
   2367 /// Saves the necessary information associated with the [`CredentialCreationOptions`] used to create it
   2368 /// via [`CredentialCreationOptions::start_ceremony`] so that registration of a new credential can be
   2369 /// performed with [`Self::verify`].
   2370 ///
   2371 /// `RegistrationServerState` implements [`Borrow`] of [`SentChallenge`]; thus to obtain the correct
   2372 /// `RegistrationServerState` associated with a [`Registration`], one should use its corresponding
   2373 /// [`Registration::challenge`].
   2374 #[derive(Debug)]
   2375 pub struct RegistrationServerState<const USER_LEN: usize> {
   2376     /// [`mediation`](https://www.w3.org/TR/credential-management-1/#dom-credentialcreationoptions-mediation).
   2377     mediation: CredentialMediationRequirement,
   2378     // This is a `SentChallenge` since we need `RegistrationServerState` to be fetchable after receiving the
   2379     // response from the client. This response must obviously be constructable; thus its challenge is a
   2380     // `SentChallenge`.
   2381     //
   2382     // This must never be mutated since we want to ensure it is actually a `Challenge` (which
   2383     // can only be constructed via `Challenge::new`). This is guaranteed to be true iff
   2384     // `serializable_server_state` is not enabled. We avoid implementing `trait`s like `Hash` when that
   2385     // is enabled.
   2386     /// [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-challenge).
   2387     challenge: SentChallenge,
   2388     /// [`pubKeyCredParams`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-pubkeycredparams).
   2389     pub_key_cred_params: CoseAlgorithmIdentifiers,
   2390     /// [`authenticatorSelection`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-authenticatorselection).
   2391     authenticator_selection: AuthenticatorSelectionCriteria,
   2392     /// [`extensions`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptions-extensions).
   2393     extensions: ServerExtensionInfo,
   2394     /// `Instant` the ceremony expires.
   2395     #[cfg(not(feature = "serializable_server_state"))]
   2396     expiration: Instant,
   2397     /// `SystemTime` the ceremony expires.
   2398     #[cfg(feature = "serializable_server_state")]
   2399     expiration: SystemTime,
   2400     /// User handle.
   2401     user_id: UserHandle<USER_LEN>,
   2402 }
   2403 impl<const USER_LEN: usize> RegistrationServerState<USER_LEN> {
   2404     /// Verifies `response` is valid based on `self` consuming `self` and returning a `RegisteredCredential` that
   2405     /// borrows the necessary data from `response`.
   2406     ///
   2407     /// `rp_id` MUST be the same as the [`PublicKeyCredentialCreationOptions::rp_id`] used when starting the
   2408     /// ceremony.
   2409     ///
   2410     /// It is _essential_ to ensure [`RegisteredCredential::id`] has not been previously registered; if
   2411     /// so, the ceremony SHOULD be aborted and a failure reported. When saving `RegisteredCredential`, one may
   2412     /// want to save the [`RpId`] and [`PublicKeyCredentialUserEntity`] information; however since [`RpId`] is
   2413     /// likely static, that may not be necessary. User information is also likely static for a given [`UserHandle`]
   2414     /// (which is saved in `RegisteredCredential`); so if such info is saved, one may want to save it once per
   2415     /// `UserHandle` and not per `RegisteredCredential`.
   2416     ///
   2417     /// # Errors
   2418     ///
   2419     /// Errors iff `response` is not valid according to the
   2420     /// [registration ceremony criteria](https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential)
   2421     /// or violates any of the settings in `options`.
   2422     #[inline]
   2423     pub fn verify<'a, O: PartialEq<Origin<'a>>, T: PartialEq<Origin<'a>>>(
   2424         self,
   2425         rp_id: &RpId,
   2426         response: &'a Registration,
   2427         options: &RegistrationVerificationOptions<'_, '_, O, T>,
   2428     ) -> Result<RegisteredCredential<'a, USER_LEN>, RegCeremonyErr> {
   2429         // [Registration ceremony](https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential)
   2430         // is handled by:
   2431         //
   2432         // 1. Calling code.
   2433         // 2. Client code and the construction of `resp` (hopefully via [`Registration::deserialize`]).
   2434         // 3. Client code and the construction of `resp` (hopefully via [`AuthenticatorAttestation::deserialize`]).
   2435         // 4. Client code and the construction of `resp` (hopefully via [`ClientExtensionsOutputs::deserialize`]).
   2436         // 5. [`Self::partial_validate`].
   2437         // 6. [`Self::partial_validate`].
   2438         // 7. [`Self::partial_validate`].
   2439         // 8. [`Self::partial_validate`].
   2440         // 9. [`Self::partial_validate`].
   2441         // 10. [`Self::partial_validate`].
   2442         // 11. [`Self::partial_validate`].
   2443         // 12. [`Self::partial_validate`].
   2444         // 13. [`Self::partial_validate`].
   2445         // 14. [`Self::partial_validate`].
   2446         // 15. Below.
   2447         // 16. [`Self::partial_validate`].
   2448         // 17. [`Self::partial_validate`].
   2449         // 18. [`Self::partial_validate`].
   2450         // 19. [`Self::partial_validate`].
   2451         // 20. Below.
   2452         // 21. [`Self::partial_validate`].
   2453         // 22. [`Self::partial_validate`].
   2454         // 23. N/A since only none and self attestations are supported.
   2455         // 24. Always satisfied since only none and self attestations are supported (Item 3 is N/A).
   2456         // 25. [`Self::partial_validate`].
   2457         // 26. Calling code.
   2458         // 27. Below.
   2459         // 28. N/A since only none and self attestations are supported.
   2460         // 29. Below.
   2461 
   2462         // Steps 5–14, 16–19, 21–22, and 25.
   2463         self.partial_validate(rp_id, response, (), &options.into())
   2464             .map_err(RegCeremonyErr::from)
   2465             .and_then(|attestation_object| {
   2466                 let auth_data = attestation_object.auth_data();
   2467                 let flags = auth_data.flags();
   2468                 // Step 15.
   2469                 if matches!(self.mediation, CredentialMediationRequirement::Conditional)
   2470                     || flags.user_present
   2471                 {
   2472                     self.authenticator_selection
   2473                         // Verify any required authenticator attachment modality.
   2474                         .validate(
   2475                             options.require_authenticator_attachment,
   2476                             response.authenticator_attachment,
   2477                         )
   2478                         .and_then(|()| {
   2479                             let attested_credential_data = auth_data.attested_credential_data();
   2480                             self.pub_key_cred_params
   2481                                 // Step 20.
   2482                                 .validate(attested_credential_data.credential_public_key)
   2483                                 .and_then(|()| {
   2484                                     let extensions = auth_data.extensions();
   2485                                     // Step 27.
   2486                                     self.extensions
   2487                                         .validate(
   2488                                             response.client_extension_results,
   2489                                             extensions,
   2490                                             options.error_on_unsolicited_extensions,
   2491                                         )
   2492                                         .map_err(RegCeremonyErr::Extension)
   2493                                         .and_then(|()| {
   2494                                             // Step 29.
   2495                                             RegisteredCredential::new(
   2496                                                 attested_credential_data.credential_id,
   2497                                                 response.response.transports(),
   2498                                                 self.user_id,
   2499                                                 StaticState {
   2500                                                     credential_public_key: attested_credential_data
   2501                                                         .credential_public_key,
   2502                                                     extensions: extensions.into(),
   2503                                                     client_extension_results: response
   2504                                                         .client_extension_results
   2505                                                         .into(),
   2506                                                 },
   2507                                                 DynamicState {
   2508                                                     user_verified: flags.user_verified,
   2509                                                     backup: flags.backup,
   2510                                                     sign_count: auth_data.sign_count(),
   2511                                                     authenticator_attachment: response
   2512                                                         .authenticator_attachment,
   2513                                                 },
   2514                                                 Metadata {
   2515                                                     attestation: match attestation_object
   2516                                                         .attestation()
   2517                                                     {
   2518                                                         AttestationFormat::None => {
   2519                                                             Attestation::None
   2520                                                         }
   2521                                                         AttestationFormat::Packed(_) => {
   2522                                                             Attestation::Surrogate
   2523                                                         }
   2524                                                     },
   2525                                                     aaguid: attested_credential_data.aaguid,
   2526                                                     extensions: extensions.into(),
   2527                                                     client_extension_results: response
   2528                                                         .client_extension_results
   2529                                                         .into(),
   2530                                                     resident_key: self
   2531                                                         .authenticator_selection
   2532                                                         .resident_key,
   2533                                                 },
   2534                                             )
   2535                                             .map_err(RegCeremonyErr::Credential)
   2536                                         })
   2537                                 })
   2538                         })
   2539                 } else {
   2540                     Err(RegCeremonyErr::UserNotPresent)
   2541                 }
   2542             })
   2543     }
   2544 }
   2545 #[cfg(all(test, feature = "custom", feature = "serializable_server_state"))]
   2546 impl<const USER_LEN: usize> RegistrationServerState<USER_LEN> {
   2547     fn is_eq(&self, other: &Self) -> bool {
   2548         self.mediation == other.mediation
   2549             && self.challenge == other.challenge
   2550             && self.pub_key_cred_params == other.pub_key_cred_params
   2551             && self.authenticator_selection == other.authenticator_selection
   2552             && self.extensions == other.extensions
   2553             && self.expiration == other.expiration
   2554             && self.user_id == other.user_id
   2555     }
   2556 }
   2557 impl<const USER_LEN: usize> TimedCeremony for RegistrationServerState<USER_LEN> {
   2558     #[cfg(any(doc, not(feature = "serializable_server_state")))]
   2559     #[inline]
   2560     fn expiration(&self) -> Instant {
   2561         self.expiration
   2562     }
   2563     #[cfg(all(not(doc), feature = "serializable_server_state"))]
   2564     #[inline]
   2565     fn expiration(&self) -> SystemTime {
   2566         self.expiration
   2567     }
   2568 }
   2569 impl<const USER_LEN: usize> Ceremony<USER_LEN, false> for RegistrationServerState<USER_LEN> {
   2570     type R = Registration;
   2571     fn rand_challenge(&self) -> SentChallenge {
   2572         self.challenge
   2573     }
   2574     #[cfg(not(feature = "serializable_server_state"))]
   2575     fn expiry(&self) -> Instant {
   2576         self.expiration
   2577     }
   2578     #[cfg(feature = "serializable_server_state")]
   2579     fn expiry(&self) -> SystemTime {
   2580         self.expiration
   2581     }
   2582     fn user_verification(&self) -> UserVerificationRequirement {
   2583         self.authenticator_selection.user_verification
   2584     }
   2585 }
   2586 impl<const USER_LEN: usize> Borrow<SentChallenge> for RegistrationServerState<USER_LEN> {
   2587     #[inline]
   2588     fn borrow(&self) -> &SentChallenge {
   2589         &self.challenge
   2590     }
   2591 }
   2592 impl<const USER_LEN: usize> PartialEq for RegistrationServerState<USER_LEN> {
   2593     #[inline]
   2594     fn eq(&self, other: &Self) -> bool {
   2595         self.challenge == other.challenge
   2596     }
   2597 }
   2598 impl<const USER_LEN: usize> PartialEq<&Self> for RegistrationServerState<USER_LEN> {
   2599     #[inline]
   2600     fn eq(&self, other: &&Self) -> bool {
   2601         *self == **other
   2602     }
   2603 }
   2604 impl<const USER_LEN: usize> PartialEq<RegistrationServerState<USER_LEN>>
   2605     for &RegistrationServerState<USER_LEN>
   2606 {
   2607     #[inline]
   2608     fn eq(&self, other: &RegistrationServerState<USER_LEN>) -> bool {
   2609         **self == *other
   2610     }
   2611 }
   2612 impl<const USER_LEN: usize> Eq for RegistrationServerState<USER_LEN> {}
   2613 impl<const USER_LEN: usize> Hash for RegistrationServerState<USER_LEN> {
   2614     #[inline]
   2615     fn hash<H: Hasher>(&self, state: &mut H) {
   2616         self.challenge.hash(state);
   2617     }
   2618 }
   2619 impl<const USER_LEN: usize> PartialOrd for RegistrationServerState<USER_LEN> {
   2620     #[inline]
   2621     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
   2622         Some(self.cmp(other))
   2623     }
   2624 }
   2625 impl<const USER_LEN: usize> Ord for RegistrationServerState<USER_LEN> {
   2626     #[inline]
   2627     fn cmp(&self, other: &Self) -> Ordering {
   2628         self.challenge.cmp(&other.challenge)
   2629     }
   2630 }
   2631 /// `RegistrationServerState` based on a [`UserHandle64`].
   2632 pub type RegistrationServerState64 = RegistrationServerState<USER_HANDLE_MAX_LEN>;
   2633 /// `RegistrationServerState` based on a [`UserHandle16`].
   2634 pub type RegistrationServerState16 = RegistrationServerState<16>;
   2635 #[cfg(test)]
   2636 mod tests {
   2637     #[cfg(all(
   2638         feature = "custom",
   2639         any(
   2640             feature = "serializable_server_state",
   2641             not(any(feature = "bin", feature = "serde"))
   2642         )
   2643     ))]
   2644     use super::{
   2645         super::{super::AggErr, ExtensionInfo},
   2646         Challenge, CredProtect, CredentialCreationOptions, FourToSixtyThree, PrfInput, RpId,
   2647         UserHandle,
   2648     };
   2649     #[cfg(all(feature = "custom", feature = "serializable_server_state"))]
   2650     use super::{
   2651         super::{
   2652             super::bin::{Decode as _, Encode as _},
   2653             AsciiDomain,
   2654         },
   2655         Extension, PublicKeyCredentialUserEntity, RegistrationServerState,
   2656     };
   2657     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   2658     use super::{
   2659         super::{
   2660             super::{
   2661                 CredentialErr,
   2662                 response::register::{
   2663                     AuthenticationExtensionsPrfOutputs, AuthenticatorAttestation,
   2664                     ClientExtensionsOutputs, CredentialPropertiesOutput,
   2665                     CredentialProtectionPolicy, HmacSecret,
   2666                 },
   2667             },
   2668             AuthTransports,
   2669         },
   2670         AuthenticatorAttachment, BackupReq, ExtensionErr, ExtensionReq, RegCeremonyErr,
   2671         Registration, RegistrationVerificationOptions, UserVerificationRequirement,
   2672     };
   2673     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   2674     use rsa::sha2::{Digest as _, Sha256};
   2675     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   2676     const CBOR_UINT: u8 = 0b000_00000;
   2677     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   2678     const CBOR_NEG: u8 = 0b001_00000;
   2679     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   2680     const CBOR_BYTES: u8 = 0b010_00000;
   2681     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   2682     const CBOR_TEXT: u8 = 0b011_00000;
   2683     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   2684     const CBOR_MAP: u8 = 0b101_00000;
   2685     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   2686     const CBOR_SIMPLE: u8 = 0b111_00000;
   2687     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   2688     const CBOR_FALSE: u8 = CBOR_SIMPLE | 20;
   2689     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   2690     const CBOR_TRUE: u8 = CBOR_SIMPLE | 21;
   2691     #[test]
   2692     #[cfg(all(feature = "custom", feature = "serializable_server_state"))]
   2693     fn eddsa_reg_ser() -> Result<(), AggErr> {
   2694         let rp_id = RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?);
   2695         let id = UserHandle::from([0; 1]);
   2696         let mut opts = CredentialCreationOptions::passkey(
   2697             &rp_id,
   2698             PublicKeyCredentialUserEntity {
   2699                 name: "foo".try_into()?,
   2700                 id: &id,
   2701                 display_name: None,
   2702             },
   2703             Vec::new(),
   2704         );
   2705         opts.public_key.challenge = Challenge(0);
   2706         opts.public_key.extensions = Extension {
   2707             cred_props: None,
   2708             cred_protect: CredProtect::UserVerificationRequired(
   2709                 false,
   2710                 ExtensionInfo::RequireEnforceValue,
   2711             ),
   2712             min_pin_length: Some((FourToSixtyThree::Ten, ExtensionInfo::RequireEnforceValue)),
   2713             prf: Some((
   2714                 PrfInput {
   2715                     first: [0].as_slice(),
   2716                     second: None,
   2717                 },
   2718                 ExtensionInfo::RequireEnforceValue,
   2719             )),
   2720         };
   2721         let server = opts.start_ceremony()?.0;
   2722         let enc_data = server
   2723             .encode()
   2724             .map_err(AggErr::EncodeRegistrationServerState)?;
   2725         assert_eq!(enc_data.capacity(), enc_data.len());
   2726         assert_eq!(enc_data.len(), 1 + 16 + 1 + 4 + (1 + 3 + 3 + 2) + 12 + 1);
   2727         assert!(server.is_eq(&RegistrationServerState::decode(enc_data.as_slice())?));
   2728         Ok(())
   2729     }
   2730     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   2731     #[derive(Clone, Copy)]
   2732     struct TestResponseOptions {
   2733         user_verified: bool,
   2734         cred_protect: CredentialProtectionPolicy,
   2735         prf: Option<bool>,
   2736         hmac: HmacSecret,
   2737         min_pin: Option<FourToSixtyThree>,
   2738         cred_props: Option<Option<bool>>,
   2739     }
   2740     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   2741     #[derive(Clone, Copy)]
   2742     enum PrfUvOptions {
   2743         /// `true` iff `UserVerificationRequirement::Required` should be used; otherwise
   2744         /// `UserVerificationRequirement::Preferred` is used.
   2745         None(bool),
   2746         Prf(ExtensionInfo),
   2747     }
   2748     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   2749     #[derive(Clone, Copy)]
   2750     struct TestRequestOptions {
   2751         error_unsolicited: bool,
   2752         protect: CredProtect,
   2753         prf_uv: PrfUvOptions,
   2754         props: Option<ExtensionReq>,
   2755         pin: Option<(FourToSixtyThree, ExtensionInfo)>,
   2756     }
   2757     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   2758     #[derive(Clone, Copy)]
   2759     struct TestOptions {
   2760         request: TestRequestOptions,
   2761         response: TestResponseOptions,
   2762     }
   2763     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   2764     fn generate_client_data_json() -> Vec<u8> {
   2765         let mut json = Vec::with_capacity(256);
   2766         json.extend_from_slice(br#"{"type":"webauthn.create","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.as_slice());
   2767         json
   2768     }
   2769     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   2770     fn generate_attestation_object(options: TestResponseOptions) -> Vec<u8> {
   2771         let mut attestation_object = Vec::with_capacity(256);
   2772         attestation_object.extend_from_slice(
   2773             [
   2774                 CBOR_MAP | 3,
   2775                 CBOR_TEXT | 3,
   2776                 b'f',
   2777                 b'm',
   2778                 b't',
   2779                 CBOR_TEXT | 4,
   2780                 b'n',
   2781                 b'o',
   2782                 b'n',
   2783                 b'e',
   2784                 CBOR_TEXT | 7,
   2785                 b'a',
   2786                 b't',
   2787                 b't',
   2788                 b'S',
   2789                 b't',
   2790                 b'm',
   2791                 b't',
   2792                 CBOR_MAP,
   2793                 CBOR_TEXT | 8,
   2794                 b'a',
   2795                 b'u',
   2796                 b't',
   2797                 b'h',
   2798                 b'D',
   2799                 b'a',
   2800                 b't',
   2801                 b'a',
   2802                 CBOR_BYTES | 24,
   2803                 // Length.
   2804                 113 + if matches!(options.cred_protect, CredentialProtectionPolicy::None) {
   2805                     if matches!(options.hmac, HmacSecret::None) {
   2806                         options.min_pin.map_or(0, |_| 15)
   2807                     } else {
   2808                         14 + options.min_pin.map_or(0, |_| 14)
   2809                             + match options.hmac {
   2810                                 HmacSecret::None => unreachable!("bug"),
   2811                                 HmacSecret::NotEnabled | HmacSecret::Enabled => 0,
   2812                                 HmacSecret::One => 65,
   2813                                 HmacSecret::Two => 97,
   2814                             }
   2815                     }
   2816                 } else {
   2817                     14 + if matches!(options.hmac, HmacSecret::None) {
   2818                         0
   2819                     } else {
   2820                         13
   2821                     } + options.min_pin.map_or(0, |_| 14)
   2822                         + match options.hmac {
   2823                             HmacSecret::None | HmacSecret::NotEnabled | HmacSecret::Enabled => 0,
   2824                             HmacSecret::One => 65,
   2825                             HmacSecret::Two => 97,
   2826                         }
   2827                 },
   2828                 // RP ID HASH.
   2829                 // This will be overwritten later.
   2830                 0,
   2831                 0,
   2832                 0,
   2833                 0,
   2834                 0,
   2835                 0,
   2836                 0,
   2837                 0,
   2838                 0,
   2839                 0,
   2840                 0,
   2841                 0,
   2842                 0,
   2843                 0,
   2844                 0,
   2845                 0,
   2846                 0,
   2847                 0,
   2848                 0,
   2849                 0,
   2850                 0,
   2851                 0,
   2852                 0,
   2853                 0,
   2854                 0,
   2855                 0,
   2856                 0,
   2857                 0,
   2858                 0,
   2859                 0,
   2860                 0,
   2861                 0,
   2862                 // FLAGS.
   2863                 // UP, UV, AT, and ED (right-to-left).
   2864                 0b0100_0001
   2865                     | if options.user_verified {
   2866                         0b0000_0100
   2867                     } else {
   2868                         0b0000_0000
   2869                     }
   2870                     | if matches!(options.cred_protect, CredentialProtectionPolicy::None)
   2871                         && matches!(options.hmac, HmacSecret::None)
   2872                         && options.min_pin.is_none()
   2873                     {
   2874                         0
   2875                     } else {
   2876                         0b1000_0000
   2877                     },
   2878                 // COUNTER.
   2879                 // 0 as 32-bit big endian.
   2880                 0,
   2881                 0,
   2882                 0,
   2883                 0,
   2884                 // AAGUID.
   2885                 0,
   2886                 0,
   2887                 0,
   2888                 0,
   2889                 0,
   2890                 0,
   2891                 0,
   2892                 0,
   2893                 0,
   2894                 0,
   2895                 0,
   2896                 0,
   2897                 0,
   2898                 0,
   2899                 0,
   2900                 0,
   2901                 // L.
   2902                 // CREDENTIAL ID length is 16 as 16-bit big endian.
   2903                 0,
   2904                 16,
   2905                 // CREDENTIAL ID.
   2906                 0,
   2907                 0,
   2908                 0,
   2909                 0,
   2910                 0,
   2911                 0,
   2912                 0,
   2913                 0,
   2914                 0,
   2915                 0,
   2916                 0,
   2917                 0,
   2918                 0,
   2919                 0,
   2920                 0,
   2921                 0,
   2922                 CBOR_MAP | 4,
   2923                 // COSE kty.
   2924                 CBOR_UINT | 1,
   2925                 // COSE OKP.
   2926                 CBOR_UINT | 1,
   2927                 // COSE alg.
   2928                 CBOR_UINT | 3,
   2929                 // COSE Eddsa.
   2930                 CBOR_NEG | 7,
   2931                 // COSE OKP crv.
   2932                 CBOR_NEG,
   2933                 // COSE Ed25519.
   2934                 CBOR_UINT | 6,
   2935                 // COSE OKP x.
   2936                 CBOR_NEG | 1,
   2937                 CBOR_BYTES | 24,
   2938                 // Length is 32.
   2939                 32,
   2940                 // Compressed-y coordinate.
   2941                 59,
   2942                 106,
   2943                 39,
   2944                 188,
   2945                 206,
   2946                 182,
   2947                 164,
   2948                 45,
   2949                 98,
   2950                 163,
   2951                 168,
   2952                 208,
   2953                 42,
   2954                 111,
   2955                 13,
   2956                 115,
   2957                 101,
   2958                 50,
   2959                 21,
   2960                 119,
   2961                 29,
   2962                 226,
   2963                 67,
   2964                 166,
   2965                 58,
   2966                 192,
   2967                 72,
   2968                 161,
   2969                 139,
   2970                 89,
   2971                 218,
   2972                 41,
   2973             ]
   2974             .as_slice(),
   2975         );
   2976         attestation_object[30..62]
   2977             .copy_from_slice(Sha256::digest("example.com".as_bytes()).as_slice());
   2978         if matches!(options.cred_protect, CredentialProtectionPolicy::None) {
   2979             if matches!(options.hmac, HmacSecret::None) {
   2980                 if options.min_pin.is_some() {
   2981                     attestation_object.push(CBOR_MAP | 1)
   2982                 }
   2983             } else if options.min_pin.is_some() {
   2984                 attestation_object.push(
   2985                     CBOR_MAP
   2986                         | 2 + u8::from(matches!(options.hmac, HmacSecret::One | HmacSecret::Two)),
   2987                 );
   2988             } else {
   2989                 attestation_object.push(
   2990                     CBOR_MAP
   2991                         | 1 + u8::from(matches!(options.hmac, HmacSecret::One | HmacSecret::Two)),
   2992                 );
   2993             }
   2994         } else {
   2995             attestation_object.extend_from_slice(
   2996                 [
   2997                     CBOR_MAP | 1 + match options.hmac { HmacSecret::None => 0, HmacSecret::NotEnabled | HmacSecret::Enabled => 1, HmacSecret::One | HmacSecret::Two => 2, } + u8::from(options.min_pin.is_some()),
   2998                     // CBOR text of length 11.
   2999                     CBOR_TEXT | 11,
   3000                     b'c',
   3001                     b'r',
   3002                     b'e',
   3003                     b'd',
   3004                     b'P',
   3005                     b'r',
   3006                     b'o',
   3007                     b't',
   3008                     b'e',
   3009                     b'c',
   3010                     b't',
   3011                     match options.cred_protect { CredentialProtectionPolicy::None => unreachable!("bug"), CredentialProtectionPolicy::UserVerificationOptional => 1, CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList => 2, CredentialProtectionPolicy::UserVerificationRequired => 3, },
   3012                 ].as_slice()
   3013             )
   3014         }
   3015         if !matches!(options.hmac, HmacSecret::None) {
   3016             attestation_object.extend_from_slice(
   3017                 [
   3018                     // CBOR text of length 11.
   3019                     CBOR_TEXT | 11,
   3020                     b'h',
   3021                     b'm',
   3022                     b'a',
   3023                     b'c',
   3024                     b'-',
   3025                     b's',
   3026                     b'e',
   3027                     b'c',
   3028                     b'r',
   3029                     b'e',
   3030                     b't',
   3031                     if matches!(options.hmac, HmacSecret::NotEnabled) {
   3032                         CBOR_FALSE
   3033                     } else {
   3034                         CBOR_TRUE
   3035                     },
   3036                 ]
   3037                 .as_slice(),
   3038             );
   3039         }
   3040         _ = options.min_pin.map(|p| {
   3041             assert!(p <= FourToSixtyThree::TwentyThree, "bug");
   3042             attestation_object.extend_from_slice(
   3043                 [
   3044                     // CBOR text of length 12.
   3045                     CBOR_TEXT | 12,
   3046                     b'm',
   3047                     b'i',
   3048                     b'n',
   3049                     b'P',
   3050                     b'i',
   3051                     b'n',
   3052                     b'L',
   3053                     b'e',
   3054                     b'n',
   3055                     b'g',
   3056                     b't',
   3057                     b'h',
   3058                     CBOR_UINT | p.into_u8(),
   3059                 ]
   3060                 .as_slice(),
   3061             );
   3062         });
   3063         if matches!(options.hmac, HmacSecret::One | HmacSecret::Two) {
   3064             attestation_object.extend_from_slice(
   3065                 [
   3066                     // CBOR text of length 14.
   3067                     CBOR_TEXT | 14,
   3068                     b'h',
   3069                     b'm',
   3070                     b'a',
   3071                     b'c',
   3072                     b'-',
   3073                     b's',
   3074                     b'e',
   3075                     b'c',
   3076                     b'r',
   3077                     b'e',
   3078                     b't',
   3079                     b'-',
   3080                     b'm',
   3081                     b'c',
   3082                     CBOR_BYTES | 24,
   3083                 ]
   3084                 .as_slice(),
   3085             );
   3086             if matches!(options.hmac, HmacSecret::One) {
   3087                 attestation_object.push(48);
   3088                 attestation_object.extend_from_slice([1; 48].as_slice());
   3089             } else {
   3090                 attestation_object.push(80);
   3091                 attestation_object.extend_from_slice([1; 80].as_slice());
   3092             }
   3093         }
   3094         attestation_object
   3095     }
   3096     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   3097     fn validate(options: TestOptions) -> Result<(), AggErr> {
   3098         let rp_id = RpId::Domain("example.com".to_owned().try_into()?);
   3099         let registration = Registration::new(
   3100             AuthenticatorAttestation::new(
   3101                 generate_client_data_json(),
   3102                 generate_attestation_object(options.response),
   3103                 AuthTransports::NONE,
   3104             ),
   3105             AuthenticatorAttachment::None,
   3106             ClientExtensionsOutputs {
   3107                 cred_props: options
   3108                     .response
   3109                     .cred_props
   3110                     .map(|rk| CredentialPropertiesOutput { rk }),
   3111                 prf: options
   3112                     .response
   3113                     .prf
   3114                     .map(|enabled| AuthenticationExtensionsPrfOutputs { enabled }),
   3115             },
   3116         );
   3117         let reg_opts = RegistrationVerificationOptions::<'static, 'static, &str, &str> {
   3118             allowed_origins: [].as_slice(),
   3119             allowed_top_origins: None,
   3120             backup_requirement: BackupReq::None,
   3121             error_on_unsolicited_extensions: options.request.error_unsolicited,
   3122             require_authenticator_attachment: false,
   3123             #[cfg(feature = "serde_relaxed")]
   3124             client_data_json_relaxed: false,
   3125         };
   3126         let user = UserHandle::from([0; 1]);
   3127         let mut opts = CredentialCreationOptions::first_passkey_with_blank_user_info(&rp_id, &user);
   3128         opts.public_key.challenge = Challenge(0);
   3129         opts.public_key.authenticator_selection.user_verification =
   3130             UserVerificationRequirement::Preferred;
   3131         match options.request.prf_uv {
   3132             PrfUvOptions::None(required) => {
   3133                 if required
   3134                     || matches!(
   3135                         options.request.protect,
   3136                         CredProtect::UserVerificationRequired(_, _)
   3137                     )
   3138                 {
   3139                     opts.public_key.authenticator_selection.user_verification =
   3140                         UserVerificationRequirement::Required;
   3141                 }
   3142             }
   3143             PrfUvOptions::Prf(info) => {
   3144                 opts.public_key.authenticator_selection.user_verification =
   3145                     UserVerificationRequirement::Required;
   3146                 opts.public_key.extensions.prf = Some((
   3147                     PrfInput {
   3148                         first: [0].as_slice(),
   3149                         second: None,
   3150                     },
   3151                     info,
   3152                 ));
   3153             }
   3154         }
   3155         opts.public_key.extensions.cred_protect = options.request.protect;
   3156         opts.public_key.extensions.cred_props = options.request.props;
   3157         opts.public_key.extensions.min_pin_length = options.request.pin;
   3158         opts.start_ceremony()?
   3159             .0
   3160             .verify(&rp_id, &registration, &reg_opts)
   3161             .map_err(AggErr::RegCeremony)
   3162             .map(|_| ())
   3163     }
   3164     /// Test all, and only, possible `UserNotVerified` errors.
   3165     /// 4 * 3 * 5 * 2 * 13 * 5 * 3 * 5 * 4 * 4 = 1,872,000 tests.
   3166     /// We ignore this due to how long it takes (around 30 seconds or so).
   3167     #[test]
   3168     #[ignore]
   3169     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   3170     fn test_uv_required_err() {
   3171         const ALL_CRED_PROTECTION_OPTIONS: [CredentialProtectionPolicy; 4] = [
   3172             CredentialProtectionPolicy::None,
   3173             CredentialProtectionPolicy::UserVerificationOptional,
   3174             CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList,
   3175             CredentialProtectionPolicy::UserVerificationRequired,
   3176         ];
   3177         const ALL_PRF_OPTIONS: [Option<bool>; 3] = [None, Some(false), Some(true)];
   3178         const ALL_HMAC_OPTIONS: [HmacSecret; 5] = [
   3179             HmacSecret::None,
   3180             HmacSecret::NotEnabled,
   3181             HmacSecret::Enabled,
   3182             HmacSecret::One,
   3183             HmacSecret::Two,
   3184         ];
   3185         const ALL_UNSOLICIT_OPTIONS: [bool; 2] = [false, true];
   3186         const ALL_CRED_PROTECT_OPTIONS: [CredProtect; 13] = [
   3187             CredProtect::None,
   3188             CredProtect::UserVerificationOptional(false, ExtensionInfo::RequireEnforceValue),
   3189             CredProtect::UserVerificationOptional(true, ExtensionInfo::RequireDontEnforceValue),
   3190             CredProtect::UserVerificationOptional(false, ExtensionInfo::AllowEnforceValue),
   3191             CredProtect::UserVerificationOptional(true, ExtensionInfo::AllowDontEnforceValue),
   3192             CredProtect::UserVerificationOptionalWithCredentialIdList(
   3193                 false,
   3194                 ExtensionInfo::RequireEnforceValue,
   3195             ),
   3196             CredProtect::UserVerificationOptionalWithCredentialIdList(
   3197                 true,
   3198                 ExtensionInfo::RequireDontEnforceValue,
   3199             ),
   3200             CredProtect::UserVerificationOptionalWithCredentialIdList(
   3201                 false,
   3202                 ExtensionInfo::AllowEnforceValue,
   3203             ),
   3204             CredProtect::UserVerificationOptionalWithCredentialIdList(
   3205                 true,
   3206                 ExtensionInfo::AllowDontEnforceValue,
   3207             ),
   3208             CredProtect::UserVerificationRequired(false, ExtensionInfo::RequireEnforceValue),
   3209             CredProtect::UserVerificationRequired(true, ExtensionInfo::RequireDontEnforceValue),
   3210             CredProtect::UserVerificationRequired(false, ExtensionInfo::AllowEnforceValue),
   3211             CredProtect::UserVerificationRequired(true, ExtensionInfo::AllowDontEnforceValue),
   3212         ];
   3213         const ALL_NOT_FALSE_PRF_UV_OPTIONS: [PrfUvOptions; 5] = [
   3214             PrfUvOptions::None(true),
   3215             PrfUvOptions::Prf(ExtensionInfo::RequireEnforceValue),
   3216             PrfUvOptions::Prf(ExtensionInfo::RequireDontEnforceValue),
   3217             PrfUvOptions::Prf(ExtensionInfo::AllowEnforceValue),
   3218             PrfUvOptions::Prf(ExtensionInfo::AllowDontEnforceValue),
   3219         ];
   3220         const ALL_PROPS_OPTIONS: [Option<ExtensionReq>; 3] =
   3221             [None, Some(ExtensionReq::Require), Some(ExtensionReq::Allow)];
   3222         const ALL_PIN_OPTIONS: [Option<(FourToSixtyThree, ExtensionInfo)>; 5] = [
   3223             None,
   3224             Some((FourToSixtyThree::Five, ExtensionInfo::RequireEnforceValue)),
   3225             Some((
   3226                 FourToSixtyThree::Five,
   3227                 ExtensionInfo::RequireDontEnforceValue,
   3228             )),
   3229             Some((FourToSixtyThree::Five, ExtensionInfo::AllowEnforceValue)),
   3230             Some((FourToSixtyThree::Five, ExtensionInfo::AllowDontEnforceValue)),
   3231         ];
   3232         const ALL_CRED_PROPS_OPTIONS: [Option<Option<bool>>; 4] =
   3233             [None, Some(None), Some(Some(false)), Some(Some(true))];
   3234         const ALL_MIN_PIN_OPTIONS: [Option<FourToSixtyThree>; 4] = [
   3235             None,
   3236             Some(FourToSixtyThree::Four),
   3237             Some(FourToSixtyThree::Five),
   3238             Some(FourToSixtyThree::Six),
   3239         ];
   3240         for cred_protect in ALL_CRED_PROTECTION_OPTIONS {
   3241             for prf in ALL_PRF_OPTIONS {
   3242                 for hmac in ALL_HMAC_OPTIONS {
   3243                     for cred_props in ALL_CRED_PROPS_OPTIONS {
   3244                         for min_pin in ALL_MIN_PIN_OPTIONS {
   3245                             for error_unsolicited in ALL_UNSOLICIT_OPTIONS {
   3246                                 for protect in ALL_CRED_PROTECT_OPTIONS {
   3247                                     for prf_uv in ALL_NOT_FALSE_PRF_UV_OPTIONS {
   3248                                         for props in ALL_PROPS_OPTIONS {
   3249                                             for pin in ALL_PIN_OPTIONS {
   3250                                                 assert!(validate(TestOptions {
   3251                                                     request: TestRequestOptions {
   3252                                                         error_unsolicited,
   3253                                                         protect,
   3254                                                         prf_uv,
   3255                                                         props,
   3256                                                         pin,
   3257                                                     },
   3258                                                     response: TestResponseOptions {
   3259                                                         user_verified: false,
   3260                                                         hmac,
   3261                                                         cred_protect,
   3262                                                         prf,
   3263                                                         min_pin,
   3264                                                         cred_props,
   3265                                                     },
   3266                                                 }).map_or_else(|err| matches!(err, AggErr::RegCeremony(reg_err) if matches!(reg_err, RegCeremonyErr::UserNotVerified)), |_| false));
   3267                                             }
   3268                                         }
   3269                                     }
   3270                                 }
   3271                             }
   3272                         }
   3273                     }
   3274                 }
   3275             }
   3276         }
   3277     }
   3278     /// Test all, and only, possible `ForbiddenCredProps` errors.
   3279     /// 4 * 3 * 5 * 2 * 13 * 6 * 5 * 3 * 4 = 561,600
   3280     /// -
   3281     /// 4 * 3 * 5 * 4 * 6 * 5 * 3 * 4 = 86,400
   3282     /// -
   3283     /// 4 * 3 * 5 * 13 * 5 * 5 * 3 * 4 = 234,000
   3284     /// +
   3285     /// 4 * 3 * 5 * 4 * 5 * 5 * 3 * 4 = 72,000
   3286     /// =
   3287     /// 313,200 total tests.
   3288     /// We ignore this due to how long it takes (around 6 seconds or so).
   3289     #[test]
   3290     #[ignore]
   3291     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   3292     fn test_forbidden_cred_props() {
   3293         const ALL_CRED_PROTECTION_OPTIONS: [CredentialProtectionPolicy; 4] = [
   3294             CredentialProtectionPolicy::None,
   3295             CredentialProtectionPolicy::UserVerificationOptional,
   3296             CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList,
   3297             CredentialProtectionPolicy::UserVerificationRequired,
   3298         ];
   3299         const ALL_PRF_OPTIONS: [Option<bool>; 3] = [None, Some(false), Some(true)];
   3300         const ALL_HMAC_OPTIONS: [HmacSecret; 5] = [
   3301             HmacSecret::None,
   3302             HmacSecret::NotEnabled,
   3303             HmacSecret::Enabled,
   3304             HmacSecret::One,
   3305             HmacSecret::Two,
   3306         ];
   3307         const ALL_UV_OPTIONS: [bool; 2] = [false, true];
   3308         const ALL_CRED_PROTECT_OPTIONS: [CredProtect; 13] = [
   3309             CredProtect::None,
   3310             CredProtect::UserVerificationOptional(false, ExtensionInfo::RequireEnforceValue),
   3311             CredProtect::UserVerificationOptional(true, ExtensionInfo::RequireDontEnforceValue),
   3312             CredProtect::UserVerificationOptional(false, ExtensionInfo::AllowEnforceValue),
   3313             CredProtect::UserVerificationOptional(true, ExtensionInfo::AllowDontEnforceValue),
   3314             CredProtect::UserVerificationOptionalWithCredentialIdList(
   3315                 false,
   3316                 ExtensionInfo::RequireEnforceValue,
   3317             ),
   3318             CredProtect::UserVerificationOptionalWithCredentialIdList(
   3319                 true,
   3320                 ExtensionInfo::RequireDontEnforceValue,
   3321             ),
   3322             CredProtect::UserVerificationOptionalWithCredentialIdList(
   3323                 false,
   3324                 ExtensionInfo::AllowEnforceValue,
   3325             ),
   3326             CredProtect::UserVerificationOptionalWithCredentialIdList(
   3327                 true,
   3328                 ExtensionInfo::AllowDontEnforceValue,
   3329             ),
   3330             CredProtect::UserVerificationRequired(false, ExtensionInfo::RequireEnforceValue),
   3331             CredProtect::UserVerificationRequired(true, ExtensionInfo::RequireDontEnforceValue),
   3332             CredProtect::UserVerificationRequired(false, ExtensionInfo::AllowEnforceValue),
   3333             CredProtect::UserVerificationRequired(true, ExtensionInfo::AllowDontEnforceValue),
   3334         ];
   3335         const ALL_PRF_UV_OPTIONS: [PrfUvOptions; 6] = [
   3336             PrfUvOptions::None(false),
   3337             PrfUvOptions::None(true),
   3338             PrfUvOptions::Prf(ExtensionInfo::RequireEnforceValue),
   3339             PrfUvOptions::Prf(ExtensionInfo::RequireDontEnforceValue),
   3340             PrfUvOptions::Prf(ExtensionInfo::AllowEnforceValue),
   3341             PrfUvOptions::Prf(ExtensionInfo::AllowDontEnforceValue),
   3342         ];
   3343         const ALL_PIN_OPTIONS: [Option<(FourToSixtyThree, ExtensionInfo)>; 5] = [
   3344             None,
   3345             Some((FourToSixtyThree::Five, ExtensionInfo::RequireEnforceValue)),
   3346             Some((
   3347                 FourToSixtyThree::Five,
   3348                 ExtensionInfo::RequireDontEnforceValue,
   3349             )),
   3350             Some((FourToSixtyThree::Five, ExtensionInfo::AllowEnforceValue)),
   3351             Some((FourToSixtyThree::Five, ExtensionInfo::AllowDontEnforceValue)),
   3352         ];
   3353         const ALL_NON_EMPTY_CRED_PROPS_OPTIONS: [Option<Option<bool>>; 3] =
   3354             [Some(None), Some(Some(false)), Some(Some(true))];
   3355         const ALL_MIN_PIN_OPTIONS: [Option<FourToSixtyThree>; 4] = [
   3356             None,
   3357             Some(FourToSixtyThree::Four),
   3358             Some(FourToSixtyThree::Five),
   3359             Some(FourToSixtyThree::Six),
   3360         ];
   3361         for cred_protect in ALL_CRED_PROTECTION_OPTIONS {
   3362             for prf in ALL_PRF_OPTIONS {
   3363                 for hmac in ALL_HMAC_OPTIONS {
   3364                     for cred_props in ALL_NON_EMPTY_CRED_PROPS_OPTIONS {
   3365                         for min_pin in ALL_MIN_PIN_OPTIONS {
   3366                             for user_verified in ALL_UV_OPTIONS {
   3367                                 for protect in ALL_CRED_PROTECT_OPTIONS {
   3368                                     for prf_uv in ALL_PRF_UV_OPTIONS {
   3369                                         for pin in ALL_PIN_OPTIONS {
   3370                                             if user_verified
   3371                                                 || (!matches!(
   3372                                                     protect,
   3373                                                     CredProtect::UserVerificationRequired(_, _)
   3374                                                 ) && matches!(prf_uv, PrfUvOptions::None(uv) if !uv))
   3375                                             {
   3376                                                 assert!(validate(TestOptions {
   3377                                                     request: TestRequestOptions {
   3378                                                         error_unsolicited: true,
   3379                                                         protect,
   3380                                                         prf_uv,
   3381                                                         props: None,
   3382                                                         pin,
   3383                                                     },
   3384                                                     response: TestResponseOptions {
   3385                                                         user_verified,
   3386                                                         hmac,
   3387                                                         cred_protect,
   3388                                                         prf,
   3389                                                         min_pin,
   3390                                                         cred_props,
   3391                                                     },
   3392                                                 }).map_or_else(|err| matches!(err, AggErr::RegCeremony(reg_err) if matches!(reg_err, RegCeremonyErr::Extension(ext_err) if matches!(ext_err, ExtensionErr::ForbiddenCredProps))), |_| false));
   3393                                             }
   3394                                         }
   3395                                     }
   3396                                 }
   3397                             }
   3398                         }
   3399                     }
   3400                 }
   3401             }
   3402         }
   3403     }
   3404     #[test]
   3405     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   3406     fn test_prf() -> Result<(), AggErr> {
   3407         let mut opts = TestOptions {
   3408             request: TestRequestOptions {
   3409                 error_unsolicited: false,
   3410                 protect: CredProtect::None,
   3411                 prf_uv: PrfUvOptions::Prf(ExtensionInfo::RequireEnforceValue),
   3412                 props: None,
   3413                 pin: None,
   3414             },
   3415             response: TestResponseOptions {
   3416                 user_verified: true,
   3417                 hmac: HmacSecret::None,
   3418                 cred_protect: CredentialProtectionPolicy::None,
   3419                 prf: Some(true),
   3420                 min_pin: None,
   3421                 cred_props: None,
   3422             },
   3423         };
   3424         validate(opts)?;
   3425         opts.response.prf = Some(false);
   3426         assert!(validate(opts).map_or_else(|e| matches!(e, AggErr::RegCeremony(err) if matches!(err, RegCeremonyErr::Extension(ext_err) if matches!(ext_err, ExtensionErr::InvalidPrfValue))), |_| false));
   3427         opts.response.hmac = HmacSecret::NotEnabled;
   3428         opts.response.prf = Some(true);
   3429         assert!(validate(opts).map_or_else(|e| matches!(e, AggErr::RegCeremony(err) if matches!(err, RegCeremonyErr::Extension(ext_err) if matches!(ext_err, ExtensionErr::InvalidHmacSecretValue))), |_| false));
   3430         opts.request.prf_uv = PrfUvOptions::Prf(ExtensionInfo::AllowDontEnforceValue);
   3431         opts.response.hmac = HmacSecret::Enabled;
   3432         opts.response.prf = None;
   3433         assert!(validate(opts).map_or_else(|e| matches!(e, AggErr::RegCeremony(err) if matches!(err, RegCeremonyErr::Credential(cred_err) if matches!(cred_err, CredentialErr::HmacSecretWithoutPrf))), |_| false));
   3434         opts.response.hmac = HmacSecret::NotEnabled;
   3435         assert!(validate(opts).map_or_else(|e| matches!(e, AggErr::RegCeremony(err) if matches!(err, RegCeremonyErr::Credential(cred_err) if matches!(cred_err, CredentialErr::HmacSecretWithoutPrf))), |_| false));
   3436         opts.response.prf = Some(true);
   3437         assert!(validate(opts).map_or_else(|e| matches!(e, AggErr::RegCeremony(err) if matches!(err, RegCeremonyErr::Credential(cred_err) if matches!(cred_err, CredentialErr::PrfWithoutHmacSecret))), |_| false));
   3438         opts.response.prf = Some(false);
   3439         validate(opts)?;
   3440         opts.request.prf_uv = PrfUvOptions::None(false);
   3441         opts.response.user_verified = false;
   3442         opts.response.hmac = HmacSecret::Enabled;
   3443         opts.response.prf = Some(true);
   3444         assert!(validate(opts).map_or_else(|e| matches!(e, AggErr::RegCeremony(err) if matches!(err, RegCeremonyErr::Credential(cred_err) if matches!(cred_err, CredentialErr::HmacSecretWithoutUserVerified))), |_| false));
   3445         opts.response.prf = None;
   3446         opts.response.hmac = HmacSecret::None;
   3447         validate(opts)?;
   3448         Ok(())
   3449     }
   3450     #[test]
   3451     #[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
   3452     fn test_cred_protect() -> Result<(), AggErr> {
   3453         let mut opts = TestOptions {
   3454             request: TestRequestOptions {
   3455                 error_unsolicited: false,
   3456                 protect: CredProtect::UserVerificationRequired(
   3457                     false,
   3458                     ExtensionInfo::RequireEnforceValue,
   3459                 ),
   3460                 prf_uv: PrfUvOptions::None(false),
   3461                 props: None,
   3462                 pin: None,
   3463             },
   3464             response: TestResponseOptions {
   3465                 user_verified: true,
   3466                 hmac: HmacSecret::None,
   3467                 cred_protect: CredentialProtectionPolicy::UserVerificationRequired,
   3468                 prf: None,
   3469                 min_pin: None,
   3470                 cred_props: None,
   3471             },
   3472         };
   3473         validate(opts)?;
   3474         opts.response.cred_protect =
   3475             CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList;
   3476         assert!(validate(opts).map_or_else(|e| matches!(e, AggErr::RegCeremony(err) if matches!(err, RegCeremonyErr::Extension(ext_err) if matches!(ext_err, ExtensionErr::InvalidCredProtectValue(CredProtect::UserVerificationRequired(false, ExtensionInfo::RequireEnforceValue), CredentialProtectionPolicy::UserVerificationOptionalWithCredentialIdList)))), |_| false));
   3477         opts.request.protect =
   3478             CredProtect::UserVerificationOptional(true, ExtensionInfo::RequireEnforceValue);
   3479         opts.response.user_verified = false;
   3480         opts.response.cred_protect = CredentialProtectionPolicy::UserVerificationRequired;
   3481         assert!(validate(opts).map_or_else(|e| matches!(e, AggErr::RegCeremony(err) if matches!(err, RegCeremonyErr::Credential(cred_err) if matches!(cred_err, CredentialErr::CredProtectUserVerificationRequiredWithoutUserVerified))), |_| false));
   3482         Ok(())
   3483     }
   3484 }