webauthn_rp

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

request.rs (142633B)


      1 #[cfg(doc)]
      2 use super::{
      3     request::{
      4         auth::{
      5             AllowedCredential, AllowedCredentials, CredentialSpecificExtension,
      6             DiscoverableAuthenticationServerState, DiscoverableCredentialRequestOptions,
      7             NonDiscoverableAuthenticationServerState, NonDiscoverableCredentialRequestOptions,
      8             PublicKeyCredentialRequestOptions,
      9         },
     10         register::{PublicKeyCredentialCreationOptions, RegistrationServerState},
     11     },
     12     response::register::ClientExtensionsOutputs,
     13 };
     14 use crate::{
     15     request::{
     16         error::{AsciiDomainErr, DomainOriginParseErr, PortParseErr, SchemeParseErr, UrlErr},
     17         register::RegistrationVerificationOptions,
     18     },
     19     response::{
     20         AuthData as _, AuthDataContainer, AuthResponse, AuthTransports, Backup, CeremonyErr,
     21         CredentialId, Origin, Response, SentChallenge,
     22     },
     23 };
     24 #[cfg(any(doc, not(feature = "serializable_server_state")))]
     25 use core::hash::{BuildHasher, Hash, Hasher};
     26 use core::{
     27     borrow::Borrow,
     28     fmt::{self, Display, Formatter},
     29     num::NonZeroU32,
     30     str::FromStr,
     31 };
     32 use rsa::sha2::{Digest as _, Sha256};
     33 #[cfg(feature = "serializable_server_state")]
     34 use std::time::SystemTime;
     35 #[cfg(any(doc, not(feature = "serializable_server_state")))]
     36 use std::{collections::HashSet, time::Instant};
     37 use url::Url as Uri;
     38 /// Contains functionality for beginning the
     39 /// [authentication ceremony](https://www.w3.org/TR/webauthn-3/#authentication-ceremony).
     40 ///
     41 /// # Examples
     42 ///
     43 /// ```
     44 /// # #[cfg(not(feature = "serializable_server_state"))]
     45 /// # use webauthn_rp::request::{FixedCapHashSet, InsertResult};
     46 /// # use webauthn_rp::{
     47 /// #     request::{
     48 /// #         auth::{AllowedCredentials, DiscoverableCredentialRequestOptions, NonDiscoverableCredentialRequestOptions},
     49 /// #         register::UserHandle64,
     50 /// #         AsciiDomain, Credentials, PublicKeyCredentialDescriptor, RpId,
     51 /// #     },
     52 /// #     response::{AuthTransports, CredentialId, CRED_ID_MIN_LEN},
     53 /// #     AggErr,
     54 /// # };
     55 /// # #[cfg(not(feature = "serializable_server_state"))]
     56 /// let mut ceremonies = FixedCapHashSet::new(128);
     57 /// let rp_id = RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?);
     58 /// let (server, client) = DiscoverableCredentialRequestOptions::passkey(&rp_id).start_ceremony()?;
     59 /// # #[cfg(not(feature = "serializable_server_state"))]
     60 /// assert!(matches!(
     61 ///     ceremonies.insert_or_replace_all_expired(server),
     62 ///     InsertResult::Success
     63 /// ));
     64 /// # #[cfg(all(feature = "custom", not(feature = "serializable_server_state")))]
     65 /// let mut ceremonies_2 = FixedCapHashSet::new(128);
     66 /// # #[cfg(feature = "serde")]
     67 /// assert!(serde_json::to_string(&client).is_ok());
     68 /// let user_handle = get_user_handle();
     69 /// # #[cfg(feature = "custom")]
     70 /// let creds = get_registered_credentials(&user_handle)?;
     71 /// # #[cfg(feature = "custom")]
     72 /// let (server_2, client_2) =
     73 ///     NonDiscoverableCredentialRequestOptions::second_factor(&rp_id, creds)?.start_ceremony()?;
     74 /// # #[cfg(all(feature = "custom", not(feature = "serializable_server_state")))]
     75 /// assert!(matches!(
     76 ///     ceremonies_2.insert_or_replace_all_expired(server_2),
     77 ///     InsertResult::Success
     78 /// ));
     79 /// # #[cfg(all(feature = "custom", feature = "serde"))]
     80 /// assert!(serde_json::to_string(&client_2).is_ok());
     81 /// /// Extract `UserHandle` from session cookie.
     82 /// fn get_user_handle() -> UserHandle64 {
     83 ///     // ⋮
     84 /// #     UserHandle64::new()
     85 /// }
     86 /// # #[cfg(feature = "custom")]
     87 /// /// Fetch the `AllowedCredentials` associated with `user`.
     88 /// fn get_registered_credentials(user: &UserHandle64) -> Result<AllowedCredentials, AggErr> {
     89 ///     // ⋮
     90 /// #     let mut creds = AllowedCredentials::new();
     91 /// #     creds.push(
     92 /// #         PublicKeyCredentialDescriptor {
     93 /// #             id: CredentialId::try_from(vec![0; CRED_ID_MIN_LEN])?,
     94 /// #             transports: AuthTransports::NONE,
     95 /// #         }
     96 /// #         .into(),
     97 /// #     );
     98 /// #     Ok(creds)
     99 /// }
    100 /// # Ok::<_, AggErr>(())
    101 /// ```
    102 pub mod auth;
    103 /// Contains error types.
    104 pub mod error;
    105 /// Contains functionality for beginning the
    106 /// [registration ceremony](https://www.w3.org/TR/webauthn-3/#registration-ceremony).
    107 ///
    108 /// # Examples
    109 ///
    110 /// ```
    111 /// # #[cfg(not(feature = "serializable_server_state"))]
    112 /// # use webauthn_rp::request::{FixedCapHashSet, InsertResult};
    113 /// # use webauthn_rp::{
    114 /// #     request::{
    115 /// #         register::{
    116 /// #             PublicKeyCredentialCreationOptions, PublicKeyCredentialUserEntity, UserHandle, USER_HANDLE_MAX_LEN, UserHandle64,
    117 /// #         },
    118 /// #         AsciiDomain, PublicKeyCredentialDescriptor, RpId
    119 /// #     },
    120 /// #     response::{AuthTransports, CredentialId, CRED_ID_MIN_LEN},
    121 /// #     AggErr,
    122 /// # };
    123 /// # #[cfg(all(not(feature = "serializable_server_state"), feature = "custom"))]
    124 /// let mut ceremonies = FixedCapHashSet::new(128);
    125 /// let rp_id = RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?);
    126 /// # #[cfg(feature = "custom")]
    127 /// let user_handle = get_user_handle();
    128 /// # #[cfg(feature = "custom")]
    129 /// let user = get_user_entity(&user_handle)?;
    130 /// # #[cfg(feature = "custom")]
    131 /// let creds = get_registered_credentials(&user_handle)?;
    132 /// # #[cfg(feature = "custom")]
    133 /// let (server, client) = PublicKeyCredentialCreationOptions::passkey(&rp_id, user.clone(), creds)
    134 ///     .start_ceremony()?;
    135 /// # #[cfg(all(not(feature = "serializable_server_state"), feature = "custom"))]
    136 /// assert!(matches!(
    137 ///     ceremonies.insert_or_replace_all_expired(server),
    138 ///     InsertResult::Success
    139 /// ));
    140 /// # #[cfg(all(feature = "serde", feature = "custom"))]
    141 /// assert!(serde_json::to_string(&client).is_ok());
    142 /// # #[cfg(feature = "custom")]
    143 /// let creds_2 = get_registered_credentials(&user_handle)?;
    144 /// # #[cfg(feature = "custom")]
    145 /// let (server_2, client_2) =
    146 ///     PublicKeyCredentialCreationOptions::second_factor(&rp_id, user, creds_2).start_ceremony()?;
    147 /// # #[cfg(all(not(feature = "serializable_server_state"), feature = "custom"))]
    148 /// assert!(matches!(
    149 ///     ceremonies.insert_or_replace_all_expired(server_2),
    150 ///     InsertResult::Success
    151 /// ));
    152 /// # #[cfg(all(feature = "serde", feature = "custom"))]
    153 /// assert!(serde_json::to_string(&client_2).is_ok());
    154 /// /// Extract `UserHandle` from session cookie or storage if this is not the first credential registered.
    155 /// # #[cfg(feature = "custom")]
    156 /// fn get_user_handle() -> UserHandle64 {
    157 ///     // ⋮
    158 /// #     [0; USER_HANDLE_MAX_LEN].into()
    159 /// }
    160 /// /// Fetch `PublicKeyCredentialUserEntity` info associated with `user`.
    161 /// ///
    162 /// /// If this is the first time a credential is being registered, then `PublicKeyCredentialUserEntity`
    163 /// /// will need to be constructed with `name` and `display_name` passed from the client and `UserHandle::new`
    164 /// /// used for `id`. Once created, this info can be stored such that the entity information
    165 /// /// does not need to be requested for subsequent registrations.
    166 /// # #[cfg(feature = "custom")]
    167 /// fn get_user_entity(user: &UserHandle64) -> Result<PublicKeyCredentialUserEntity<'_, '_, '_, USER_HANDLE_MAX_LEN>, AggErr> {
    168 ///     // ⋮
    169 /// #     Ok(PublicKeyCredentialUserEntity {
    170 /// #         name: "foo".try_into()?,
    171 /// #         id: user,
    172 /// #         display_name: None,
    173 /// #     })
    174 /// }
    175 /// /// Fetch the `PublicKeyCredentialDescriptor`s associated with `user`.
    176 /// ///
    177 /// /// This doesn't need to be called when this is the first credential registered for `user`; instead
    178 /// /// an empty `Vec` should be passed.
    179 /// fn get_registered_credentials(
    180 ///     user: &UserHandle64,
    181 /// ) -> Result<Vec<PublicKeyCredentialDescriptor<Vec<u8>>>, AggErr> {
    182 ///     // ⋮
    183 /// #     Ok(Vec::new())
    184 /// }
    185 /// # Ok::<_, AggErr>(())
    186 /// ```
    187 pub mod register;
    188 /// Contains functionality to serialize data to a client.
    189 #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
    190 #[cfg(feature = "serde")]
    191 mod ser;
    192 /// Contains functionality to (de)serialize data needed for [`RegistrationServerState`],
    193 /// [`DiscoverableAuthenticationServerState`], and [`NonDiscoverableAuthenticationServerState`] to a data store.
    194 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))]
    195 #[cfg(feature = "serializable_server_state")]
    196 pub(super) mod ser_server_state;
    197 // `Challenge` must _never_ be constructable directly or indirectly; thus its tuple field must always be private,
    198 // and it must never implement `trait`s (e.g., `Clone`) that would allow indirect creation. It must only ever
    199 // be constructed via `Self::new` or `Self::default`. In contrast downstream code must be able to construct
    200 // `SentChallenge` since it is used during ceremony validation; thus we must keep `Challenge` and `SentChallenge`
    201 // as separate types.
    202 /// [Cryptographic challenge](https://www.w3.org/TR/webauthn-3/#sctn-cryptographic-challenges).
    203 #[derive(Debug)]
    204 pub struct Challenge(u128);
    205 impl Challenge {
    206     // This won't `panic` since 4/3 of 16 is less than `usize::MAX`.
    207     /// The number of bytes a `Challenge` takes to encode in base64url.
    208     #[expect(clippy::unwrap_used, reason = "we want to crash when there is a bug")]
    209     pub(super) const BASE64_LEN: usize = super::base64url_nopad_len(16).unwrap();
    210     /// Generates a random `Challenge`.
    211     ///
    212     /// # Examples
    213     ///
    214     /// ```
    215     /// # use webauthn_rp::request::Challenge;
    216     /// // The probability of a `Challenge` being 0 (assuming a good entropy
    217     /// // source) is 2^-128 ≈ 2.9 x 10^-39.
    218     /// assert_ne!(Challenge::new().into_data(), 0);
    219     /// ```
    220     #[inline]
    221     #[must_use]
    222     pub fn new() -> Self {
    223         Self(rand::random())
    224     }
    225     /// Returns the contained `u128` consuming `self`.
    226     #[inline]
    227     #[must_use]
    228     pub const fn into_data(self) -> u128 {
    229         self.0
    230     }
    231     /// Returns the contained `u128`.
    232     #[inline]
    233     #[must_use]
    234     pub const fn as_data(&self) -> u128 {
    235         self.0
    236     }
    237 }
    238 impl Default for Challenge {
    239     /// Same as [`Self::new`].
    240     #[inline]
    241     fn default() -> Self {
    242         Self::new()
    243     }
    244 }
    245 impl From<Challenge> for u128 {
    246     #[inline]
    247     fn from(value: Challenge) -> Self {
    248         value.0
    249     }
    250 }
    251 impl From<&Challenge> for u128 {
    252     #[inline]
    253     fn from(value: &Challenge) -> Self {
    254         value.0
    255     }
    256 }
    257 /// A [domain](https://url.spec.whatwg.org/#concept-domain) in representation format consisting of only and any
    258 /// ASCII.
    259 ///
    260 /// The only ASCII character disallowed in a label is `'.'` since it is used exclusively as a separator. Every
    261 /// label must have length inclusively between 1 and 63, and the total length of the domain must be at most 253
    262 /// when a trailing `'.'` does not exist; otherwise the max length is 254. The root domain (i.e., `'.'`) is not
    263 /// allowed.
    264 #[derive(Clone, Debug, Eq, PartialEq)]
    265 pub struct AsciiDomain(String);
    266 impl AsciiDomain {
    267     /// Removes a trailing `'.'` if it exists.
    268     ///
    269     /// # Examples
    270     ///
    271     /// ```
    272     /// # use webauthn_rp::request::{error::AsciiDomainErr, AsciiDomain};
    273     /// let mut dom = AsciiDomain::try_from("example.com.".to_owned())?;
    274     /// dom.remove_trailing_dot();
    275     /// assert_eq!(dom.as_ref(), "example.com");
    276     /// # Ok::<_, AsciiDomainErr>(())
    277     /// ```
    278     #[expect(clippy::unreachable, reason = "want to crash when there is a bug")]
    279     #[inline]
    280     pub fn remove_trailing_dot(&mut self) {
    281         if *self
    282             .0
    283             .as_bytes()
    284             .last()
    285             .unwrap_or_else(|| unreachable!("there is a bug in AsciiDomain::try_from"))
    286             == b'.'
    287         {
    288             self.0.pop();
    289         }
    290     }
    291 }
    292 impl AsRef<str> for AsciiDomain {
    293     #[inline]
    294     fn as_ref(&self) -> &str {
    295         self.0.as_str()
    296     }
    297 }
    298 impl Borrow<str> for AsciiDomain {
    299     #[inline]
    300     fn borrow(&self) -> &str {
    301         self.0.as_str()
    302     }
    303 }
    304 impl From<AsciiDomain> for String {
    305     #[inline]
    306     fn from(value: AsciiDomain) -> Self {
    307         value.0
    308     }
    309 }
    310 impl PartialEq<&Self> for AsciiDomain {
    311     #[inline]
    312     fn eq(&self, other: &&Self) -> bool {
    313         *self == **other
    314     }
    315 }
    316 impl PartialEq<AsciiDomain> for &AsciiDomain {
    317     #[inline]
    318     fn eq(&self, other: &AsciiDomain) -> bool {
    319         **self == *other
    320     }
    321 }
    322 impl TryFrom<String> for AsciiDomain {
    323     type Error = AsciiDomainErr;
    324     /// Verifies `value` is an ASCII domain in representation format converting any uppercase ASCII into
    325     /// lowercase.
    326     ///
    327     /// Note it is _strongly_ encouraged for `value` to only contain letters, numbers, hyphens, and underscores;
    328     /// otherwise certain applications may consider it not a domain. If the original domain contains non-ASCII, then
    329     /// one must encode it in Punycode _before_ calling this function. Domains that have a trailing `'.'` will be
    330     /// considered differently than domains without it; thus one will likely want to trim it if it does exist
    331     /// (e.g., [`AsciiDomain::remove_trailing_dot`]). Because this allows any ASCII, one may want to ensure `value`
    332     /// is not an IP address.
    333     ///
    334     /// # Examples
    335     ///
    336     /// ```
    337     /// # use webauthn_rp::request::{error::AsciiDomainErr, AsciiDomain};
    338     /// // Root `'.'` is not removed if it exists.
    339     /// assert_ne!("example.com", AsciiDomain::try_from("example.com.".to_owned())?.as_ref());
    340     /// // Root domain (i.e., `'.'`) is not allowed.
    341     /// assert!(AsciiDomain::try_from(".".to_owned()).is_err());
    342     /// // Uppercase is transformed into lowercase.
    343     /// assert_eq!("example.com", AsciiDomain::try_from("ExAmPle.CoM".to_owned())?.as_ref());
    344     /// // The only ASCII character not allowed in a domain label is `'.'` as it is used exclusively to delimit
    345     /// // labels.
    346     /// assert_eq!("\x00", AsciiDomain::try_from("\x00".to_owned())?.as_ref());
    347     /// // Empty labels are not allowed.
    348     /// assert!(AsciiDomain::try_from("example..com".to_owned()).is_err());
    349     /// // Labels cannot have length greater than 63.
    350     /// let mut long_label = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".to_owned();
    351     /// assert_eq!(long_label.len(), 64);
    352     /// assert!(AsciiDomain::try_from(long_label.clone()).is_err());
    353     /// long_label.pop();
    354     /// assert_eq!(long_label, AsciiDomain::try_from(long_label.clone())?.as_ref());
    355     /// // The maximum length of a domain is 254 if a trailing `'.'` exists; otherwise the max length is 253.
    356     /// let mut long_domain = format!("{long_label}.{long_label}.{long_label}.{long_label}");
    357     /// long_domain.pop();
    358     /// long_domain.push('.');
    359     /// assert_eq!(long_domain.len(), 255);
    360     /// assert!(AsciiDomain::try_from(long_domain.clone()).is_err());
    361     /// long_domain.pop();
    362     /// long_domain.pop();
    363     /// long_domain.push('.');
    364     /// assert_eq!(long_domain.len(), 254);
    365     /// assert_eq!(long_domain, AsciiDomain::try_from(long_domain.clone())?.as_ref());
    366     /// long_domain.pop();
    367     /// long_domain.push('a');
    368     /// assert_eq!(long_domain.len(), 254);
    369     /// assert!(AsciiDomain::try_from(long_domain.clone()).is_err());
    370     /// long_domain.pop();
    371     /// assert_eq!(long_domain.len(), 253);
    372     /// assert_eq!(long_domain, AsciiDomain::try_from(long_domain.clone())?.as_ref());
    373     /// // Only ASCII is allowed; thus if a domain needs to be Punycode-encoded, then it must be _before_ calling
    374     /// // this function.
    375     /// assert!(AsciiDomain::try_from("λ.com".to_owned()).is_err());
    376     /// assert_eq!("xn--wxa.com", AsciiDomain::try_from("xn--wxa.com".to_owned())?.as_ref());
    377     /// # Ok::<_, AsciiDomainErr>(())
    378     /// ```
    379     #[expect(
    380         unsafe_code,
    381         reason = "need to transform uppercase ASCII into lowercase"
    382     )]
    383     #[expect(
    384         clippy::arithmetic_side_effects,
    385         reason = "comment justifies its correctness"
    386     )]
    387     #[inline]
    388     fn try_from(mut value: String) -> Result<Self, Self::Error> {
    389         value
    390             .as_bytes()
    391             .last()
    392             .ok_or(AsciiDomainErr::Empty)
    393             .and_then(|b| {
    394                 let len = value.len();
    395                 if *b == b'.' {
    396                     if len == 1 {
    397                         Err(AsciiDomainErr::RootDomain)
    398                     } else if len > 254 {
    399                         Err(AsciiDomainErr::Len)
    400                     } else {
    401                         Ok(())
    402                     }
    403                 } else if len > 253 {
    404                     Err(AsciiDomainErr::Len)
    405                 } else {
    406                     Ok(())
    407                 }
    408             })
    409             .and_then(|()| {
    410                 // SAFETY:
    411                 // The only possible mutation we perform is converting uppercase ASCII into lowercase which
    412                 // is entirely safe since ASCII is a subset of UTF-8, and ASCII characters are always encoded
    413                 // as a single UTF-8 code unit.
    414                 let utf8 = unsafe { value.as_bytes_mut() };
    415                 utf8.iter_mut()
    416                     .try_fold(0u8, |label_len, byt| {
    417                         let b = *byt;
    418                         if b == b'.' {
    419                             if label_len == 0 {
    420                                 Err(AsciiDomainErr::EmptyLabel)
    421                             } else {
    422                                 Ok(0)
    423                             }
    424                         } else if label_len == 63 {
    425                             Err(AsciiDomainErr::LabelLen)
    426                         } else if b.is_ascii() {
    427                             *byt = b.to_ascii_lowercase();
    428                             // This won't overflow since `label_len < 63`.
    429                             Ok(label_len + 1)
    430                         } else {
    431                             Err(AsciiDomainErr::NotAscii)
    432                         }
    433                     })
    434                     .map(|_| Self(value))
    435             })
    436     }
    437 }
    438 /// The output of the [URL serializer](https://url.spec.whatwg.org/#concept-url-serializer).
    439 ///
    440 /// The returned URL must consist of a [scheme](https://url.spec.whatwg.org/#concept-url-scheme) and
    441 /// optional [path](https://url.spec.whatwg.org/#url-path) but nothing else.
    442 #[derive(Clone, Debug, Eq, PartialEq)]
    443 pub struct Url(String);
    444 impl AsRef<str> for Url {
    445     #[inline]
    446     fn as_ref(&self) -> &str {
    447         self.0.as_str()
    448     }
    449 }
    450 impl Borrow<str> for Url {
    451     #[inline]
    452     fn borrow(&self) -> &str {
    453         self.0.as_str()
    454     }
    455 }
    456 impl From<Url> for String {
    457     #[inline]
    458     fn from(value: Url) -> Self {
    459         value.0
    460     }
    461 }
    462 impl PartialEq<&Self> for Url {
    463     #[inline]
    464     fn eq(&self, other: &&Self) -> bool {
    465         *self == **other
    466     }
    467 }
    468 impl PartialEq<Url> for &Url {
    469     #[inline]
    470     fn eq(&self, other: &Url) -> bool {
    471         **self == *other
    472     }
    473 }
    474 impl FromStr for Url {
    475     type Err = UrlErr;
    476     #[inline]
    477     fn from_str(s: &str) -> Result<Self, Self::Err> {
    478         Uri::from_str(s).map_err(|_e| UrlErr).and_then(|url| {
    479             if url.scheme().is_empty()
    480                 || url.has_host()
    481                 || url.query().is_some()
    482                 || url.fragment().is_some()
    483             {
    484                 Err(UrlErr)
    485             } else {
    486                 Ok(Self(url.into()))
    487             }
    488         })
    489     }
    490 }
    491 /// [RP ID](https://w3c.github.io/webauthn/#rp-id).
    492 #[derive(Clone, Debug, Eq, PartialEq)]
    493 pub enum RpId {
    494     /// An ASCII domain.
    495     ///
    496     /// Note web platforms MUST use this variant; and if possible, non-web platforms should too. Also despite
    497     /// the spec currently requiring RP IDs to be
    498     /// [valid domain strings](https://url.spec.whatwg.org/#valid-domain-string), this is unnecessarily strict
    499     /// and will likely be relaxed in a [future version](https://github.com/w3c/webauthn/issues/2206); thus
    500     /// any ASCII domain is allowed.
    501     Domain(AsciiDomain),
    502     /// A URL with only scheme and path.
    503     Url(Url),
    504 }
    505 impl RpId {
    506     /// Validates `hash` is the same as the SHA-256 hash of `self`.
    507     fn validate_rp_id_hash<E>(&self, hash: &[u8]) -> Result<(), CeremonyErr<E>> {
    508         if hash == Sha256::digest(self.as_ref()).as_slice() {
    509             Ok(())
    510         } else {
    511             Err(CeremonyErr::RpIdHashMismatch)
    512         }
    513     }
    514 }
    515 impl AsRef<str> for RpId {
    516     #[inline]
    517     fn as_ref(&self) -> &str {
    518         match *self {
    519             Self::Domain(ref dom) => dom.as_ref(),
    520             Self::Url(ref url) => url.as_ref(),
    521         }
    522     }
    523 }
    524 impl Borrow<str> for RpId {
    525     #[inline]
    526     fn borrow(&self) -> &str {
    527         match *self {
    528             Self::Domain(ref dom) => dom.borrow(),
    529             Self::Url(ref url) => url.borrow(),
    530         }
    531     }
    532 }
    533 impl From<RpId> for String {
    534     #[inline]
    535     fn from(value: RpId) -> Self {
    536         match value {
    537             RpId::Domain(dom) => dom.into(),
    538             RpId::Url(url) => url.into(),
    539         }
    540     }
    541 }
    542 impl PartialEq<&Self> for RpId {
    543     #[inline]
    544     fn eq(&self, other: &&Self) -> bool {
    545         *self == **other
    546     }
    547 }
    548 impl PartialEq<RpId> for &RpId {
    549     #[inline]
    550     fn eq(&self, other: &RpId) -> bool {
    551         **self == *other
    552     }
    553 }
    554 /// A URI scheme. This can be used to make
    555 /// [origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin) more convenient.
    556 #[derive(Clone, Copy, Debug, Default)]
    557 pub enum Scheme<'a> {
    558     /// A scheme must not exist when validating the origin.
    559     None,
    560     /// Any scheme, or no scheme at all, is allowed to exist when validating the origin.
    561     Any,
    562     /// The HTTPS scheme must exist when validating the origin.
    563     #[default]
    564     Https,
    565     /// The SSH scheme must exist when validating the origin.
    566     Ssh,
    567     /// The contained `str` scheme must exist when validating the origin.
    568     Other(&'a str),
    569     /// [`Self::None`] or [`Self::Https`].
    570     NoneHttps,
    571     /// [`Self::None`] or [`Self::Ssh`].
    572     NoneSsh,
    573     /// [`Self::None`] or [`Self::Other`].
    574     NoneOther(&'a str),
    575 }
    576 impl Scheme<'_> {
    577     /// `self` is any `Scheme`; however `other` is assumed to only be a `Scheme` from a `DomainOrigin` returned
    578     /// from `DomainOrigin::try_from`. The latter implies that `other` is only `Scheme::None`, `Scheme::Https`,
    579     /// `Scheme::Ssh`, or `Scheme::Other`; furthermore when `Scheme::Other`, it won't contain a `str` that is
    580     /// empty or equal to "https" or "ssh".
    581     #[expect(clippy::unreachable, reason = "there is a bug, so we want to crash")]
    582     fn is_equal_to_origin_scheme(self, other: Self) -> bool {
    583         match self {
    584             Self::None => matches!(other, Self::None),
    585             Self::Any => true,
    586             Self::Https => matches!(other, Self::Https),
    587             Self::Ssh => matches!(other, Self::Ssh),
    588             Self::Other(scheme) => match other {
    589                 Self::None => false,
    590                 // We want to crash and burn since there is a bug in code.
    591                 Self::Any | Self::NoneHttps | Self::NoneSsh | Self::NoneOther(_) => {
    592                     unreachable!("there is a bug in DomainOrigin::try_from")
    593                 }
    594                 Self::Https => scheme == "https",
    595                 Self::Ssh => scheme == "ssh",
    596                 Self::Other(scheme_other) => scheme == scheme_other,
    597             },
    598             Self::NoneHttps => match other {
    599                 Self::None | Self::Https => true,
    600                 Self::Ssh | Self::Other(_) => false,
    601                 // We want to crash and burn since there is a bug in code.
    602                 Self::Any | Self::NoneHttps | Self::NoneSsh | Self::NoneOther(_) => {
    603                     unreachable!("there is a bug in DomainOrigin::try_from")
    604                 }
    605             },
    606             Self::NoneSsh => match other {
    607                 Self::None | Self::Ssh => true,
    608                 // We want to crash and burn since there is a bug in code.
    609                 Self::Any | Self::NoneHttps | Self::NoneSsh | Self::NoneOther(_) => {
    610                     unreachable!("there is a bug in DomainOrigin::try_from")
    611                 }
    612                 Self::Https | Self::Other(_) => false,
    613             },
    614             Self::NoneOther(scheme) => match other {
    615                 Self::None => true,
    616                 // We want to crash and burn since there is a bug in code.
    617                 Self::Any | Self::NoneHttps | Self::NoneSsh | Self::NoneOther(_) => {
    618                     unreachable!("there is a bug in DomainOrigin::try_from")
    619                 }
    620                 Self::Https => scheme == "https",
    621                 Self::Ssh => scheme == "ssh",
    622                 Self::Other(scheme_other) => scheme == scheme_other,
    623             },
    624         }
    625     }
    626 }
    627 impl<'a: 'b, 'b> TryFrom<&'a str> for Scheme<'b> {
    628     type Error = SchemeParseErr;
    629     /// `"https"` and `"ssh"` get mapped to [`Self::Https`] and [`Self::Ssh`] respectively. All other
    630     /// values get mapped to [`Self::Other`].
    631     ///
    632     /// # Errors
    633     ///
    634     /// Errors iff `s` is empty.
    635     ///
    636     /// # Examples
    637     ///
    638     /// ```
    639     /// # use webauthn_rp::request::Scheme;
    640     /// assert!(matches!(Scheme::try_from("https")?, Scheme::Https));
    641     /// assert!(matches!(Scheme::try_from("https ")?, Scheme::Other(scheme) if scheme == "https "));
    642     /// assert!(matches!(Scheme::try_from("ssh")?, Scheme::Ssh));
    643     /// assert!(matches!(Scheme::try_from("Ssh")?, Scheme::Other(scheme) if scheme == "Ssh"));
    644     /// // Even though one can construct an empty `Scheme` via `Scheme::Other` or `Scheme::NoneOther`,
    645     /// // one cannot parse one.
    646     /// assert!(Scheme::try_from("").is_err());
    647     /// # Ok::<_, webauthn_rp::AggErr>(())
    648     /// ```
    649     #[inline]
    650     fn try_from(value: &'a str) -> Result<Self, Self::Error> {
    651         match value {
    652             "" => Err(SchemeParseErr),
    653             "https" => Ok(Self::Https),
    654             "ssh" => Ok(Self::Ssh),
    655             _ => Ok(Self::Other(value)),
    656         }
    657     }
    658 }
    659 /// A TCP/UDP port. This can be used to make
    660 /// [origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin) more convenient.
    661 #[derive(Clone, Copy, Debug, Default)]
    662 pub enum Port {
    663     /// A port must not exist when validating the origin.
    664     #[default]
    665     None,
    666     /// Any port, or no port at all, is allowed to exist when validating the origin.
    667     Any,
    668     /// The contained `u16` port must exist when validating the origin.
    669     Val(u16),
    670     /// [`Self::None`] or [`Self::Val`].
    671     NoneVal(u16),
    672 }
    673 impl Port {
    674     /// `self` is any `Port`; however `other` is assumed to only be a `Port` from a `DomainOrigin` returned
    675     /// from `DomainOrigin::try_from`. The latter implies that `other` is only `Port::None` or `Port::Val`.
    676     #[expect(clippy::unreachable, reason = "there is a bug, so we want to crash")]
    677     fn is_equal_to_origin_port(self, other: Self) -> bool {
    678         match self {
    679             Self::None => matches!(other, Self::None),
    680             Self::Any => true,
    681             Self::Val(port) => match other {
    682                 Self::None => false,
    683                 // There is a bug in code so we want to crash and burn.
    684                 Self::Any | Self::NoneVal(_) => {
    685                     unreachable!("there is a bug in DomainOrigin::try_from")
    686                 }
    687                 Self::Val(port_other) => port == port_other,
    688             },
    689             Self::NoneVal(port) => match other {
    690                 Self::None => true,
    691                 // There is a bug in code so we want to crash and burn.
    692                 Self::Any | Self::NoneVal(_) => {
    693                     unreachable!("there is a bug in DomainOrigin::try_from")
    694                 }
    695                 Self::Val(port_other) => port == port_other,
    696             },
    697         }
    698     }
    699 }
    700 impl FromStr for Port {
    701     type Err = PortParseErr;
    702     /// Parses `s` as a 16-bit unsigned integer without leading 0s returning [`Self::Val`] with the contained
    703     /// `u16`.
    704     ///
    705     /// # Errors
    706     ///
    707     /// Errors iff `s` is not a valid 16-bit unsigned integer in decimal notation without leading 0s.
    708     ///
    709     /// # Examples
    710     ///
    711     /// ```
    712     /// # use webauthn_rp::request::{error::PortParseErr, Port};
    713     /// assert!(matches!("443".parse()?, Port::Val(443)));
    714     /// // TCP/UDP ports have to be in canonical form:
    715     /// assert!("022"
    716     ///     .parse::<Port>()
    717     ///     .map_or_else(|err| matches!(err, PortParseErr::NotCanonical), |_| false));
    718     /// # Ok::<_, webauthn_rp::AggErr>(())
    719     /// ```
    720     #[inline]
    721     fn from_str(s: &str) -> Result<Self, Self::Err> {
    722         s.parse().map_err(PortParseErr::ParseInt).and_then(|port| {
    723             if s.len()
    724                 == match port {
    725                     ..=9 => 1,
    726                     10..=99 => 2,
    727                     100..=999 => 3,
    728                     1_000..=9_999 => 4,
    729                     10_000.. => 5,
    730                 }
    731             {
    732                 Ok(Self::Val(port))
    733             } else {
    734                 Err(PortParseErr::NotCanonical)
    735             }
    736         })
    737     }
    738 }
    739 /// A [`tuple origin`](https://html.spec.whatwg.org/multipage/browsers.html#concept-origin-tuple).
    740 ///
    741 /// This can be used to make [origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin)
    742 /// more convenient.
    743 #[derive(Clone, Copy, Debug)]
    744 pub struct DomainOrigin<'a, 'b> {
    745     /// The scheme.
    746     pub scheme: Scheme<'a>,
    747     /// The host.
    748     pub host: &'b str,
    749     /// The TCP/UDP port.
    750     pub port: Port,
    751 }
    752 impl<'b> DomainOrigin<'_, 'b> {
    753     /// Returns a `DomainOrigin` with [`Self::scheme`] as [`Scheme::Https`], [`Self::host`] as `host`, and
    754     /// [`Self::port`] as [`Port::None`].
    755     ///
    756     /// # Examples
    757     ///
    758     /// ```
    759     /// # extern crate alloc;
    760     /// # use alloc::borrow::Cow;
    761     /// # use webauthn_rp::{request::DomainOrigin, response::Origin};
    762     /// assert_eq!(
    763     ///     DomainOrigin::new("www.example.com"),
    764     ///     Origin(Cow::Borrowed("https://www.example.com"))
    765     /// );
    766     /// // `DomainOrigin::new` does not allow _any_ port to exist.
    767     /// assert_ne!(
    768     ///     DomainOrigin::new("www.example.com"),
    769     ///     Origin(Cow::Borrowed("https://www.example.com:443"))
    770     /// );
    771     /// ```
    772     #[must_use]
    773     #[inline]
    774     pub const fn new<'c: 'b>(host: &'c str) -> Self {
    775         Self {
    776             scheme: Scheme::Https,
    777             host,
    778             port: Port::None,
    779         }
    780     }
    781     /// Returns a `DomainOrigin` with [`Self::scheme`] as [`Scheme::Https`], [`Self::host`] as `host`, and
    782     /// [`Self::port`] as [`Port::Any`].
    783     ///
    784     /// # Examples
    785     ///
    786     /// ```
    787     /// # extern crate alloc;
    788     /// # use alloc::borrow::Cow;
    789     /// # use webauthn_rp::{request::DomainOrigin, response::Origin};
    790     /// // Any port is allowed to exist.
    791     /// assert_eq!(
    792     ///     DomainOrigin::new_ignore_port("www.example.com"),
    793     ///     Origin(Cow::Borrowed("https://www.example.com:1234"))
    794     /// );
    795     /// // A port doesn't have to exist at all either.
    796     /// assert_eq!(
    797     ///     DomainOrigin::new_ignore_port("www.example.com"),
    798     ///     Origin(Cow::Borrowed("https://www.example.com"))
    799     /// );
    800     /// ```
    801     #[must_use]
    802     #[inline]
    803     pub const fn new_ignore_port<'c: 'b>(host: &'c str) -> Self {
    804         Self {
    805             scheme: Scheme::Https,
    806             host,
    807             port: Port::Any,
    808         }
    809     }
    810 }
    811 impl PartialEq<Origin<'_>> for DomainOrigin<'_, '_> {
    812     /// Returns `true` iff [`DomainOrigin::scheme`], [`DomainOrigin::host`], and [`DomainOrigin::port`] are the
    813     /// same after calling [`DomainOrigin::try_from`] on `other.0.as_str()`.
    814     ///
    815     /// Note that [`Scheme`] and [`Port`] need not be the same variant. For example [`Scheme::Https`] and
    816     /// [`Scheme::Other`] containing `"https"` will be treated the same.
    817     #[inline]
    818     fn eq(&self, other: &Origin<'_>) -> bool {
    819         DomainOrigin::try_from(other.0.as_ref()).is_ok_and(|dom| {
    820             self.scheme.is_equal_to_origin_scheme(dom.scheme)
    821                 && self.host == dom.host
    822                 && self.port.is_equal_to_origin_port(dom.port)
    823         })
    824     }
    825 }
    826 impl PartialEq<Origin<'_>> for &DomainOrigin<'_, '_> {
    827     #[inline]
    828     fn eq(&self, other: &Origin<'_>) -> bool {
    829         **self == *other
    830     }
    831 }
    832 impl PartialEq<&Origin<'_>> for DomainOrigin<'_, '_> {
    833     #[inline]
    834     fn eq(&self, other: &&Origin<'_>) -> bool {
    835         *self == **other
    836     }
    837 }
    838 impl PartialEq<DomainOrigin<'_, '_>> for Origin<'_> {
    839     #[inline]
    840     fn eq(&self, other: &DomainOrigin<'_, '_>) -> bool {
    841         *other == *self
    842     }
    843 }
    844 impl PartialEq<DomainOrigin<'_, '_>> for &Origin<'_> {
    845     #[inline]
    846     fn eq(&self, other: &DomainOrigin<'_, '_>) -> bool {
    847         *other == **self
    848     }
    849 }
    850 impl PartialEq<&DomainOrigin<'_, '_>> for Origin<'_> {
    851     #[inline]
    852     fn eq(&self, other: &&DomainOrigin<'_, '_>) -> bool {
    853         **other == *self
    854     }
    855 }
    856 impl<'a: 'b + 'c, 'b, 'c> TryFrom<&'a str> for DomainOrigin<'b, 'c> {
    857     type Error = DomainOriginParseErr;
    858     /// `value` is parsed according to the following extended regex:
    859     ///
    860     /// `^([^:]*:\/\/)?[^:]*(:.*)?$`
    861     ///
    862     /// where the `[^:]*` of the first capturing group is parsed according to [`Scheme::try_from`], and
    863     /// the `.*` of the second capturing group is parsed according to [`Port::from_str`].
    864     ///
    865     /// # Errors
    866     ///
    867     /// Errors iff `Scheme::try_from` or `Port::from_str` fail when applicable.
    868     ///
    869     /// # Examples
    870     ///
    871     /// ```
    872     /// # use webauthn_rp::request::{DomainOrigin, Port, Scheme};
    873     /// assert!(
    874     ///     DomainOrigin::try_from("https://www.example.com:443").map_or(false, |dom| matches!(
    875     ///         dom.scheme,
    876     ///         Scheme::Https
    877     ///     ) && dom.host
    878     ///         == "www.example.com"
    879     ///         && matches!(dom.port, Port::Val(port) if port == 443))
    880     /// );
    881     /// // Parsing is done in a case sensitive way.
    882     /// assert!(DomainOrigin::try_from("Https://www.EXample.com").map_or(
    883     ///     false,
    884     ///     |dom| matches!(dom.scheme, Scheme::Other(scheme) if scheme == "Https")
    885     ///         && dom.host == "www.EXample.com"
    886     ///         && matches!(dom.port, Port::None)
    887     /// ));
    888     /// ```
    889     #[inline]
    890     fn try_from(value: &'a str) -> Result<Self, Self::Error> {
    891         // Any string that contains `':'` is not a [valid domain](https://url.spec.whatwg.org/#valid-domain), and
    892         // and `"//"` never exists in a `Port`; thus if `"://"` exists, it's either invalid or delimits the scheme
    893         // from the rest of the origin.
    894         match value.split_once("://") {
    895             None => Ok((Scheme::None, value)),
    896             Some((poss_scheme, rem)) => Scheme::try_from(poss_scheme)
    897                 .map_err(DomainOriginParseErr::Scheme)
    898                 .map(|scheme| (scheme, rem)),
    899         }
    900         .and_then(|(scheme, rem)| {
    901             // `':'` never exists in a valid domain; thus if it exists, it's either invalid or
    902             // separates the domain from the port.
    903             rem.split_once(':')
    904                 .map_or_else(
    905                     || Ok((rem, Port::None)),
    906                     |(rem2, poss_port)| {
    907                         Port::from_str(poss_port)
    908                             .map_err(DomainOriginParseErr::Port)
    909                             .map(|port| (rem2, port))
    910                     },
    911                 )
    912                 .map(|(host, port)| Self { scheme, host, port })
    913         })
    914     }
    915 }
    916 /// [`PublicKeyCredentialDescriptor`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialdescriptor)
    917 /// associated with a registered credential.
    918 #[derive(Debug)]
    919 pub struct PublicKeyCredentialDescriptor<T> {
    920     /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-id).
    921     pub id: CredentialId<T>,
    922     /// [`transports`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-transports).
    923     pub transports: AuthTransports,
    924 }
    925 /// [`UserVerificationRequirement`](https://www.w3.org/TR/webauthn-3/#enumdef-userverificationrequirement).
    926 #[derive(Clone, Copy, Debug)]
    927 pub enum UserVerificationRequirement {
    928     /// [`required`](https://www.w3.org/TR/webauthn-3/#dom-userverificationrequirement-required).
    929     Required,
    930     /// [`discouraged`](https://www.w3.org/TR/webauthn-3/#dom-userverificationrequirement-discouraged).
    931     ///
    932     /// Note some authenticators always require user verification when registering a credential (e.g.,
    933     /// [CTAP 2.0](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html)
    934     /// authenticators that have had a PIN enabled).
    935     Discouraged,
    936     /// [`preferred`](https://www.w3.org/TR/webauthn-3/#dom-userverificationrequirement-preferred).
    937     Preferred,
    938 }
    939 #[cfg(test)]
    940 impl PartialEq for UserVerificationRequirement {
    941     fn eq(&self, other: &Self) -> bool {
    942         match *self {
    943             Self::Required => matches!(other, Self::Required),
    944             Self::Discouraged => matches!(other, Self::Discouraged),
    945             Self::Preferred => matches!(other, Self::Preferred),
    946         }
    947     }
    948 }
    949 /// [`PublicKeyCredentialHints`](https://www.w3.org/TR/webauthn-3/#enumdef-publickeycredentialhint).
    950 #[derive(Clone, Copy, Debug, Default)]
    951 pub enum Hint {
    952     /// No hints.
    953     #[default]
    954     None,
    955     /// [`security-key`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialhint-security-key).
    956     SecurityKey,
    957     /// [`client-device`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialhint-client-device).
    958     ClientDevice,
    959     /// [`hybrid`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialhint-hybrid).
    960     Hybrid,
    961     /// [`Self::SecurityKey`] and [`Self::ClientDevice`].
    962     SecurityKeyClientDevice,
    963     /// [`Self::ClientDevice`] and [`Self::SecurityKey`].
    964     ClientDeviceSecurityKey,
    965     /// [`Self::SecurityKey`] and [`Self::Hybrid`].
    966     SecurityKeyHybrid,
    967     /// [`Self::Hybrid`] and [`Self::SecurityKey`].
    968     HybridSecurityKey,
    969     /// [`Self::ClientDevice`] and [`Self::Hybrid`].
    970     ClientDeviceHybrid,
    971     /// [`Self::Hybrid`] and [`Self::ClientDevice`].
    972     HybridClientDevice,
    973     /// [`Self::SecurityKeyClientDevice`] and [`Self::Hybrid`].
    974     SecurityKeyClientDeviceHybrid,
    975     /// [`Self::SecurityKeyHybrid`] and [`Self::ClientDevice`].
    976     SecurityKeyHybridClientDevice,
    977     /// [`Self::ClientDeviceSecurityKey`] and [`Self::Hybrid`].
    978     ClientDeviceSecurityKeyHybrid,
    979     /// [`Self::ClientDeviceHybrid`] and [`Self::SecurityKey`].
    980     ClientDeviceHybridSecurityKey,
    981     /// [`Self::HybridSecurityKey`] and [`Self::ClientDevice`].
    982     HybridSecurityKeyClientDevice,
    983     /// [`Self::HybridClientDevice`] and [`Self::SecurityKey`].
    984     HybridClientDeviceSecurityKey,
    985 }
    986 #[cfg(test)]
    987 impl PartialEq for Hint {
    988     fn eq(&self, other: &Self) -> bool {
    989         match *self {
    990             Self::None => matches!(other, Self::None),
    991             Self::SecurityKey => matches!(other, Self::SecurityKey),
    992             Self::ClientDevice => matches!(other, Self::ClientDevice),
    993             Self::Hybrid => matches!(other, Self::Hybrid),
    994             Self::SecurityKeyClientDevice => matches!(other, Self::SecurityKeyClientDevice),
    995             Self::ClientDeviceSecurityKey => matches!(other, Self::ClientDeviceSecurityKey),
    996             Self::SecurityKeyHybrid => matches!(other, Self::SecurityKeyHybrid),
    997             Self::HybridSecurityKey => matches!(other, Self::HybridSecurityKey),
    998             Self::ClientDeviceHybrid => matches!(other, Self::ClientDeviceHybrid),
    999             Self::HybridClientDevice => matches!(other, Self::HybridClientDevice),
   1000             Self::SecurityKeyClientDeviceHybrid => {
   1001                 matches!(other, Self::SecurityKeyClientDeviceHybrid)
   1002             }
   1003             Self::SecurityKeyHybridClientDevice => {
   1004                 matches!(other, Self::SecurityKeyHybridClientDevice)
   1005             }
   1006             Self::ClientDeviceSecurityKeyHybrid => {
   1007                 matches!(other, Self::ClientDeviceSecurityKeyHybrid)
   1008             }
   1009             Self::ClientDeviceHybridSecurityKey => {
   1010                 matches!(other, Self::ClientDeviceHybridSecurityKey)
   1011             }
   1012             Self::HybridSecurityKeyClientDevice => {
   1013                 matches!(other, Self::HybridSecurityKeyClientDevice)
   1014             }
   1015             Self::HybridClientDeviceSecurityKey => {
   1016                 matches!(other, Self::HybridClientDeviceSecurityKey)
   1017             }
   1018         }
   1019     }
   1020 }
   1021 /// Controls if the response to a requested extension is required to be sent back.
   1022 ///
   1023 /// Note when requiring an extension, the extension must not only be sent back but also
   1024 /// contain at least one expected field
   1025 /// (e.g., [`ClientExtensionsOutputs::cred_props`] must be
   1026 /// `Some(CredentialPropertiesOutput { rk: Some(_) })`.
   1027 ///
   1028 /// If one wants to additionally control the values of an extension, use [`ExtensionInfo`].
   1029 #[derive(Clone, Copy, Debug)]
   1030 pub enum ExtensionReq {
   1031     /// The response to a requested extension is required to be sent back.
   1032     Require,
   1033     /// The response to a requested extension is allowed, but not required, to be sent back.
   1034     Allow,
   1035 }
   1036 #[cfg(test)]
   1037 impl PartialEq for ExtensionReq {
   1038     fn eq(&self, other: &Self) -> bool {
   1039         match *self {
   1040             Self::Require => matches!(other, Self::Require),
   1041             Self::Allow => matches!(other, Self::Allow),
   1042         }
   1043     }
   1044 }
   1045 /// Dictates how an extension should be processed.
   1046 ///
   1047 /// If one wants to only control if the extension should be returned, use [`ExtensionReq`].
   1048 #[derive(Clone, Copy, Debug)]
   1049 pub enum ExtensionInfo {
   1050     /// Require the associated extension and enforce its value.
   1051     RequireEnforceValue,
   1052     /// Require the associated extension but don't enforce its value.
   1053     RequireDontEnforceValue,
   1054     /// Allow the associated extension to exist and enforce its value when it does exist.
   1055     AllowEnforceValue,
   1056     /// Allow the associated extension to exist but don't enforce its value.
   1057     AllowDontEnforceValue,
   1058 }
   1059 #[cfg(test)]
   1060 impl PartialEq for ExtensionInfo {
   1061     fn eq(&self, other: &Self) -> bool {
   1062         match *self {
   1063             Self::RequireEnforceValue => matches!(other, Self::RequireEnforceValue),
   1064             Self::RequireDontEnforceValue => matches!(other, Self::RequireDontEnforceValue),
   1065             Self::AllowEnforceValue => matches!(other, Self::AllowEnforceValue),
   1066             Self::AllowDontEnforceValue => matches!(other, Self::AllowDontEnforceValue),
   1067         }
   1068     }
   1069 }
   1070 impl Display for ExtensionInfo {
   1071     #[inline]
   1072     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
   1073         f.write_str(match *self {
   1074             Self::RequireEnforceValue => "require the corresponding extension response and enforce its value",
   1075             Self::RequireDontEnforceValue => "require the corresponding extension response but don't enforce its value",
   1076             Self::AllowEnforceValue => "don't require the corresponding extension response; but if sent, enforce its value",
   1077             Self::AllowDontEnforceValue => "don't require the corresponding extension response; and if sent, don't enforce its value",
   1078         })
   1079     }
   1080 }
   1081 /// [`CredentialMediationRequirement`](https://www.w3.org/TR/credential-management-1/#enumdef-credentialmediationrequirement).
   1082 #[derive(Clone, Copy, Debug)]
   1083 pub enum CredentialMediationRequirement {
   1084     /// [`silent`](https://www.w3.org/TR/credential-management-1/#dom-credentialmediationrequirement-silent).
   1085     Silent,
   1086     /// [`optional`](https://www.w3.org/TR/credential-management-1/#dom-credentialmediationrequirement-optional)
   1087     Optional,
   1088     /// [`conditional`](https://www.w3.org/TR/credential-management-1/#dom-credentialmediationrequirement-conditional)
   1089     ///
   1090     /// Note that when registering a new credential with [`PublicKeyCredentialCreationOptions::mediation`] set to
   1091     /// `Self::Conditional`, [`UserVerificationRequirement::Discouraged`] MUST be used unless user verification
   1092     /// can be explicitly performed during the ceremony.
   1093     Conditional,
   1094     /// [`required`](https://www.w3.org/TR/credential-management-1/#dom-credentialmediationrequirement-required)
   1095     Required,
   1096 }
   1097 #[cfg(test)]
   1098 impl PartialEq for CredentialMediationRequirement {
   1099     fn eq(&self, other: &Self) -> bool {
   1100         match *self {
   1101             Self::Silent => matches!(other, Self::Silent),
   1102             Self::Optional => matches!(other, Self::Optional),
   1103             Self::Conditional => matches!(other, Self::Conditional),
   1104             Self::Required => matches!(other, Self::Required),
   1105         }
   1106     }
   1107 }
   1108 /// Backup requirements for the credential.
   1109 #[derive(Clone, Copy, Debug, Default)]
   1110 pub enum BackupReq {
   1111     #[default]
   1112     /// No requirements (i.e., any [`Backup`] is allowed).
   1113     None,
   1114     /// Credential must not be eligible for backup.
   1115     NotEligible,
   1116     /// Credential must be eligible for backup.
   1117     ///
   1118     /// Note the existence of a backup is ignored. If a backup must exist, then use [`Self::Exists`].
   1119     Eligible,
   1120     /// Credential must be backed up.
   1121     Exists,
   1122 }
   1123 impl From<Backup> for BackupReq {
   1124     /// One may want to create `BackupReq` based on the previous `Backup` such that the subsequent `Backup` is
   1125     /// essentially unchanged.
   1126     ///
   1127     /// Specifically this transforms [`Backup::NotEligible`] to [`Self::NotEligible`] and [`Backup::Eligible`] and
   1128     /// [`Backup::Exists`] to [`Self::Eligible`]. Note this means that a credential that
   1129     /// is eligible to be backed up but currently does not have a backup will be allowed to change such that it
   1130     /// is backed up. Similarly, a credential that is backed up is allowed to change such that a backup no longer
   1131     /// exists.
   1132     #[inline]
   1133     fn from(value: Backup) -> Self {
   1134         if matches!(value, Backup::NotEligible) {
   1135             Self::NotEligible
   1136         } else {
   1137             Self::Eligible
   1138         }
   1139     }
   1140 }
   1141 /// A container of "credentials".
   1142 ///
   1143 /// This is mainly a way to unify [`Vec`] of [`PublicKeyCredentialDescriptor`]
   1144 /// and [`AllowedCredentials`]. This can be useful in situations when one only
   1145 /// deals with [`AllowedCredential`]s with empty [`CredentialSpecificExtension`]s
   1146 /// essentially making them the same as [`PublicKeyCredentialDescriptor`]s.
   1147 ///
   1148 /// # Examples
   1149 ///
   1150 /// ```
   1151 /// # use webauthn_rp::{
   1152 /// #     request::{
   1153 /// #         auth::AllowedCredentials, register::UserHandle, Credentials, PublicKeyCredentialDescriptor,
   1154 /// #     },
   1155 /// #     response::{AuthTransports, CredentialId},
   1156 /// # };
   1157 /// /// Fetches all credentials under `user_handle` to be allowed during authentication for non-discoverable
   1158 /// /// requests.
   1159 /// # #[cfg(feature = "custom")]
   1160 /// fn get_allowed_credentials<const LEN: usize>(user_handle: &UserHandle<LEN>) -> AllowedCredentials {
   1161 ///     get_credentials(user_handle)
   1162 /// }
   1163 /// /// Fetches all credentials under `user_handle` to be excluded during registration.
   1164 /// # #[cfg(feature = "custom")]
   1165 /// fn get_excluded_credentials<const LEN: usize>(
   1166 ///     user_handle: &UserHandle<LEN>,
   1167 /// ) -> Vec<PublicKeyCredentialDescriptor<Vec<u8>>> {
   1168 ///     get_credentials(user_handle)
   1169 /// }
   1170 /// /// Used to fetch the excluded `PublicKeyCredentialDescriptor`s associated with `user_handle` during
   1171 /// /// registration as well as the `AllowedCredentials` containing `AllowedCredential`s with no credential-specific
   1172 /// /// extensions which is used for non-discoverable requests.
   1173 /// # #[cfg(feature = "custom")]
   1174 /// fn get_credentials<const LEN: usize, T>(user_handle: &UserHandle<LEN>) -> T
   1175 /// where
   1176 ///     T: Credentials,
   1177 ///     PublicKeyCredentialDescriptor<Vec<u8>>: Into<T::Credential>,
   1178 /// {
   1179 ///     let iter = get_cred_parts(user_handle);
   1180 ///     let len = iter.size_hint().0;
   1181 ///     iter.fold(T::with_capacity(len), |mut creds, parts| {
   1182 ///         creds.push(
   1183 ///             PublicKeyCredentialDescriptor {
   1184 ///                 id: parts.0,
   1185 ///                 transports: parts.1,
   1186 ///             }
   1187 ///             .into(),
   1188 ///         );
   1189 ///         creds
   1190 ///     })
   1191 /// }
   1192 /// /// Fetches all `CredentialId`s and associated `AuthTransports` under `user_handle`
   1193 /// /// from the database.
   1194 /// # #[cfg(feature = "custom")]
   1195 /// fn get_cred_parts<const LEN: usize>(
   1196 ///     user_handle: &UserHandle<LEN>,
   1197 /// ) -> impl Iterator<Item = (CredentialId<Vec<u8>>, AuthTransports)> {
   1198 ///     // ⋮
   1199 /// #     [(
   1200 /// #         CredentialId::try_from(vec![0; 16]).unwrap(),
   1201 /// #         AuthTransports::NONE,
   1202 /// #     )]
   1203 /// #     .into_iter()
   1204 /// }
   1205 /// ```
   1206 pub trait Credentials: Sized {
   1207     /// The "credential"s that make up `Self`.
   1208     type Credential;
   1209     /// Returns `Self`.
   1210     #[inline]
   1211     #[must_use]
   1212     fn new() -> Self {
   1213         Self::with_capacity(0)
   1214     }
   1215     /// Returns `Self` with at least `capacity` allocated.
   1216     fn with_capacity(capacity: usize) -> Self;
   1217     /// Adds `cred` to `self`.
   1218     ///
   1219     /// Returns `true` iff `cred` was added.
   1220     fn push(&mut self, cred: Self::Credential) -> bool;
   1221     /// Returns the number of [`Self::Credential`]s in `Self`.
   1222     fn len(&self) -> usize;
   1223     /// Returns `true` iff [`Self::len`] is `0`.
   1224     #[inline]
   1225     fn is_empty(&self) -> bool {
   1226         self.len() == 0
   1227     }
   1228 }
   1229 impl<T> Credentials for Vec<T> {
   1230     type Credential = T;
   1231     #[inline]
   1232     fn with_capacity(capacity: usize) -> Self {
   1233         Self::with_capacity(capacity)
   1234     }
   1235     #[inline]
   1236     fn push(&mut self, cred: Self::Credential) -> bool {
   1237         self.push(cred);
   1238         true
   1239     }
   1240     #[inline]
   1241     fn len(&self) -> usize {
   1242         self.len()
   1243     }
   1244 }
   1245 /// Additional options that control how [`Ceremony::partial_validate`] works.
   1246 struct CeremonyOptions<'origins, 'top_origins, O, T> {
   1247     /// Origins to use for [origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin).
   1248     ///
   1249     /// When this is empty, the origin that will be used will be based on
   1250     /// the [`RpId`] passed to [`RegistrationServerState::verify`]. If [`RpId::Domain`], then the [`DomainOrigin`] returned from
   1251     /// passing [`AsciiDomain::as_ref`] to [`DomainOrigin::new`] will be used; otherwise the [`Url`] in
   1252     /// [`RpId::Url`] will be used.
   1253     allowed_origins: &'origins [O],
   1254     /// [Top-level origins](https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-top-level-origin)
   1255     /// to use for [origin validation](https://www.w3.org/TR/webauthn-3/#sctn-validating-origin).
   1256     ///
   1257     /// When this is `Some`, [`CollectedClientData::cross_origin`] is allowed to be `true`. When the contained
   1258     /// `slice` is empty, [`CollectedClientData::top_origin`] must be `None`. When this is `None`,
   1259     /// `CollectedClientData::cross_origin` must be `false` and `CollectedClientData::top_origin` must be `None`.
   1260     allowed_top_origins: Option<&'top_origins [T]>,
   1261     /// The required [`Backup`] state of the credential.
   1262     backup_requirement: BackupReq,
   1263     /// [`CollectedClientData::from_client_data_json_relaxed`] is used to extract [`CollectedClientData`] iff `true`.
   1264     #[cfg(feature = "serde_relaxed")]
   1265     client_data_json_relaxed: bool,
   1266 }
   1267 impl<'o, 't, O, T> From<&RegistrationVerificationOptions<'o, 't, O, T>>
   1268     for CeremonyOptions<'o, 't, O, T>
   1269 {
   1270     fn from(value: &RegistrationVerificationOptions<'o, 't, O, T>) -> Self {
   1271         Self {
   1272             allowed_origins: value.allowed_origins,
   1273             allowed_top_origins: value.allowed_top_origins,
   1274             backup_requirement: value.backup_requirement,
   1275             #[cfg(feature = "serde_relaxed")]
   1276             client_data_json_relaxed: value.client_data_json_relaxed,
   1277         }
   1278     }
   1279 }
   1280 /// Functionality common to both registration and authentication ceremonies.
   1281 ///
   1282 /// Designed to be implemented on the _request_ side.
   1283 trait Ceremony<const USER_LEN: usize, const DISCOVERABLE: bool> {
   1284     /// The type of response that is associated with the ceremony.
   1285     type R: Response;
   1286     /// Challenge.
   1287     fn rand_challenge(&self) -> SentChallenge;
   1288     /// `Instant` the ceremony was expires.
   1289     #[cfg(not(feature = "serializable_server_state"))]
   1290     fn expiry(&self) -> Instant;
   1291     /// `Instant` the ceremony was expires.
   1292     #[cfg(feature = "serializable_server_state")]
   1293     fn expiry(&self) -> SystemTime;
   1294     /// User verification requirement.
   1295     fn user_verification(&self) -> UserVerificationRequirement;
   1296     /// Performs validation of ceremony criteria common to both ceremony types.
   1297     #[expect(
   1298         clippy::type_complexity,
   1299         reason = "type aliases with bounds are even more problematic at least until lazy_type_alias is stable"
   1300     )]
   1301     #[expect(clippy::too_many_lines, reason = "102 lines is fine")]
   1302     fn partial_validate<'a, O: PartialEq<Origin<'a>>, T: PartialEq<Origin<'a>>>(
   1303         &self,
   1304         rp_id: &RpId,
   1305         resp: &'a Self::R,
   1306         key: <<Self::R as Response>::Auth as AuthResponse>::CredKey<'_>,
   1307         options: &CeremonyOptions<'_, '_, O, T>,
   1308     ) -> Result<
   1309         <<Self::R as Response>::Auth as AuthResponse>::Auth<'a>,
   1310         CeremonyErr<
   1311             <<<Self::R as Response>::Auth as AuthResponse>::Auth<'a> as AuthDataContainer<'a>>::Err,
   1312         >,
   1313     > {
   1314         // [Registration ceremony](https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential)
   1315         // is handled by:
   1316         //
   1317         // 1. Calling code.
   1318         // 2. Client code and the construction of `resp` (hopefully via [`Registration::deserialize`]).
   1319         // 3. Client code and the construction of `resp` (hopefully via [`AuthenticatorAttestation::deserialize`]).
   1320         // 4. Client code and the construction of `resp` (hopefully via [`ClientExtensionsOutputs::deserialize`]).
   1321         // 5. Below via [`CollectedClientData::from_client_data_json_relaxed`].
   1322         // 6. Below via [`CollectedClientData::from_client_data_json_relaxed`] or [`CollectedClientData::from_client_data_json_relaxed`].
   1323         // 7. Below via [`CollectedClientData::from_client_data_json_relaxed`] or [`CollectedClientData::from_client_data_json_relaxed`].
   1324         // 8. Below.
   1325         // 9. Below.
   1326         // 10. Below.
   1327         // 11. Below.
   1328         // 12. Below via [`AuthenticatorAttestation::new`].
   1329         // 13. Below via [`AttestationObject::parse_data`].
   1330         // 14. Below.
   1331         // 15. [`RegistrationServerState::verify`].
   1332         // 16. Below.
   1333         // 17. Below via [`AuthenticatorData::from_cbor`].
   1334         // 18. Below.
   1335         // 19. Below.
   1336         // 20. [`RegistrationServerState::verify`].
   1337         // 21. Below via [`AttestationObject::parse_data`].
   1338         // 22. Below via [`AttestationObject::parse_data`].
   1339         // 23. N/A since only none and self attestations are supported.
   1340         // 24. Always satisfied since only none and self attestations are supported (Item 3 is N/A).
   1341         // 25. Below via [`AttestedCredentialData::from_cbor`].
   1342         // 26. Calling code.
   1343         // 27. [`RegistrationServerState::verify`].
   1344         // 28. N/A since only none and self attestations are supported.
   1345         // 29. [`RegistrationServerState::verify`].
   1346         //
   1347         //
   1348         // [Authentication ceremony](https://www.w3.org/TR/webauthn-3/#sctn-verifying-assertion)
   1349         // is handled by:
   1350         //
   1351         // 1. Calling code.
   1352         // 2. Client code and the construction of `resp` (hopefully via [`Authentication::deserialize`]).
   1353         // 3. Client code and the construction of `resp` (hopefully via [`AuthenticatorAssertion::deserialize`]).
   1354         // 4. Client code and the construction of `resp` (hopefully via [`ClientExtensionsOutputs::deserialize`]).
   1355         // 5. [`AuthenticationServerState::verify`].
   1356         // 6. [`AuthenticationServerState::verify`].
   1357         // 7. Informative only in that it defines variables.
   1358         // 8. Below via [`CollectedClientData::from_client_data_json_relaxed`].
   1359         // 9. Below via [`CollectedClientData::from_client_data_json_relaxed`] or [`CollectedClientData::from_client_data_json_relaxed`].
   1360         // 10. Below via [`CollectedClientData::from_client_data_json_relaxed`] or [`CollectedClientData::from_client_data_json_relaxed`].
   1361         // 11. Below.
   1362         // 12. Below.
   1363         // 13. Below.
   1364         // 14. Below.
   1365         // 15. Below.
   1366         // 16. Below via [`AuthenticatorData::from_cbor`].
   1367         // 17. Below.
   1368         // 18. Below via [`AuthenticatorData::from_cbor`].
   1369         // 19. Below.
   1370         // 20. Below via [`AuthenticatorAssertion::new`].
   1371         // 21. Below.
   1372         // 22. [`AuthenticationServerState::verify`].
   1373         // 23. [`AuthenticationServerState::verify`].
   1374         // 24. [`AuthenticationServerState::verify`].
   1375         // 25. [`AuthenticationServerState::verify`].
   1376 
   1377         // Enforce timeout.
   1378         #[cfg(not(feature = "serializable_server_state"))]
   1379         let active = self.expiry() >= Instant::now();
   1380         #[cfg(feature = "serializable_server_state")]
   1381         let active = self.expiry() >= SystemTime::now();
   1382         if active {
   1383             #[cfg(feature = "serde_relaxed")]
   1384             let relaxed = options.client_data_json_relaxed;
   1385             #[cfg(not(feature = "serde_relaxed"))]
   1386             let relaxed = false;
   1387             resp.auth()
   1388                 // Steps 5–7, 12–13, 17, 21–22, and 25 of the registration ceremony.
   1389                 // Steps 8–10, 16, 18, and 20–21 of the authentication ceremony.
   1390                 .parse_data_and_verify_sig(key, relaxed)
   1391                 .map_err(CeremonyErr::AuthResp)
   1392                 .and_then(|(client_data_json, auth_response)| {
   1393                     if options.allowed_origins.is_empty() {
   1394                         if match *rp_id {
   1395                             RpId::Domain(ref dom) => {
   1396                                 // Steps 9 and 12 of the registration and authentication ceremonies
   1397                                 // respectively.
   1398                                 DomainOrigin::new(dom.as_ref()) == client_data_json.origin
   1399                             }
   1400                             // Steps 9 and 12 of the registration and authentication ceremonies
   1401                             // respectively.
   1402                             RpId::Url(ref url) => url == client_data_json.origin,
   1403                         } {
   1404                             Ok(())
   1405                         } else {
   1406                             Err(CeremonyErr::OriginMismatch)
   1407                         }
   1408                     } else {
   1409                         options
   1410                             .allowed_origins
   1411                             .iter()
   1412                             // Steps 9 and 12 of the registration and authentication ceremonies
   1413                             // respectively.
   1414                             .find(|o| **o == client_data_json.origin)
   1415                             .ok_or(CeremonyErr::OriginMismatch)
   1416                             .map(|_| ())
   1417                     }
   1418                     .and_then(|()| {
   1419                         // Steps 10–11 of the registration ceremony.
   1420                         // Steps 13–14 of the authentication ceremony.
   1421                         match options.allowed_top_origins {
   1422                             None => {
   1423                                 if client_data_json.cross_origin {
   1424                                     Err(CeremonyErr::CrossOrigin)
   1425                                 } else if client_data_json.top_origin.is_some() {
   1426                                     Err(CeremonyErr::TopOriginMismatch)
   1427                                 } else {
   1428                                     Ok(())
   1429                                 }
   1430                             }
   1431                             Some(top_origins) => client_data_json.top_origin.map_or(Ok(()), |t| {
   1432                                 top_origins
   1433                                     .iter()
   1434                                     .find(|top| **top == t)
   1435                                     .ok_or(CeremonyErr::TopOriginMismatch)
   1436                                     .map(|_| ())
   1437                             }),
   1438                         }
   1439                         .and_then(|()| {
   1440                             // Steps 8 and 11 of the registration and authentication ceremonies
   1441                             // respectively.
   1442                             if self.rand_challenge() == client_data_json.challenge {
   1443                                 let auth_data = auth_response.authenticator_data();
   1444                                 rp_id
   1445                                     // Steps 14 and 15 of the registration and authentication ceremonies
   1446                                     // respectively.
   1447                                     .validate_rp_id_hash(auth_data.rp_hash())
   1448                                     .and_then(|()| {
   1449                                         let flag = auth_data.flag();
   1450                                         // Steps 16 and 17 of the registration and authentication ceremonies
   1451                                         // respectively.
   1452                                         if flag.user_verified
   1453                                             || !matches!(
   1454                                                 self.user_verification(),
   1455                                                 UserVerificationRequirement::Required
   1456                                             )
   1457                                         {
   1458                                             // Steps 18–19 of the registration ceremony.
   1459                                             // Step 19 of the authentication ceremony.
   1460                                             match options.backup_requirement {
   1461                                                 BackupReq::None => Ok(()),
   1462                                                 BackupReq::NotEligible => {
   1463                                                     if matches!(flag.backup, Backup::NotEligible) {
   1464                                                         Ok(())
   1465                                                     } else {
   1466                                                         Err(CeremonyErr::BackupEligible)
   1467                                                     }
   1468                                                 }
   1469                                                 BackupReq::Eligible => {
   1470                                                     if matches!(flag.backup, Backup::NotEligible) {
   1471                                                         Err(CeremonyErr::BackupNotEligible)
   1472                                                     } else {
   1473                                                         Ok(())
   1474                                                     }
   1475                                                 }
   1476                                                 BackupReq::Exists => {
   1477                                                     if matches!(flag.backup, Backup::Exists) {
   1478                                                         Ok(())
   1479                                                     } else {
   1480                                                         Err(CeremonyErr::BackupDoesNotExist)
   1481                                                     }
   1482                                                 }
   1483                                             }
   1484                                         } else {
   1485                                             Err(CeremonyErr::UserNotVerified)
   1486                                         }
   1487                                     })
   1488                                     .map(|()| auth_response)
   1489                             } else {
   1490                                 Err(CeremonyErr::ChallengeMismatch)
   1491                             }
   1492                         })
   1493                     })
   1494                 })
   1495         } else {
   1496             Err(CeremonyErr::Timeout)
   1497         }
   1498     }
   1499 }
   1500 /// `300_000` milliseconds is equal to five minutes.
   1501 #[expect(
   1502     clippy::unwrap_used,
   1503     reason = "clearly correct, Option::unwrap is const, and better than unsafe"
   1504 )]
   1505 pub(super) const THREE_HUNDRED_THOUSAND: NonZeroU32 = NonZeroU32::new(300_000).unwrap();
   1506 
   1507 /// [`Hasher`] whose `write_*` methods simply store up to 64 bits of the passed argument _as is_ overwriting
   1508 /// any previous state.
   1509 ///
   1510 /// This is designed to only be used indirectly via a hash map whose keys are randomly generated on the server
   1511 /// based on at least 64 bits—the size of the integer returned from [`Self::finish`]—of entropy.
   1512 /// This makes this `Hasher` usable (and ideal) in only the most niche circumstances.
   1513 ///
   1514 /// [`RegistrationServerState`], [`DiscoverableAuthenticationServerState`], and
   1515 /// [`NonDiscoverableAuthenticationServerState`] implement [`Hash`] by simply writing the
   1516 /// contained [`Challenge`]; thus when they are stored in a hashed collection (e.g., [`FixedCapHashSet`]), one can
   1517 /// optimize without fear by using this `Hasher` since `Challenge`s are immutable and can only ever be created on
   1518 /// the server via [`Challenge::new`] (and equivalently [`Challenge::default`]). `RegistrationServerState`,
   1519 /// `DiscoverableAuthenticationServerState`, and `NonDiscoverableAuthenticationServerState` are also immutable and
   1520 /// only constructable via [`PublicKeyCredentialCreationOptions::start_ceremony`],
   1521 /// [`DiscoverableCredentialRequestOptions::start_ceremony`], and
   1522 /// [`NonDiscoverableCredentialRequestOptions::start_ceremony`] respectively. Since `Challenge` is already based on
   1523 /// a random `u128`, other `Hasher`s will be slower and likely produce lower-quality hashes (and never
   1524 /// higher quality).
   1525 #[cfg_attr(docsrs, doc(cfg(not(feature = "serializable_server_state"))))]
   1526 #[cfg(any(doc, not(feature = "serializable_server_state")))]
   1527 #[derive(Debug)]
   1528 pub struct IdentityHasher(u64);
   1529 // Note it is _not_ required for `write_*` methods to do the same thing as other `write_*` methods
   1530 // (e.g., `Self::write_u64` may not be the same thing as 8 calls to `Self::write_u8`).
   1531 #[cfg(any(doc, not(feature = "serializable_server_state")))]
   1532 impl Hasher for IdentityHasher {
   1533     /// Returns `0` if no `write_*` calls have been made; otherwise returns the result of the most recent
   1534     /// `write_*` call.
   1535     #[inline]
   1536     fn finish(&self) -> u64 {
   1537         self.0
   1538     }
   1539     /// Writes `i` to `self`.
   1540     #[inline]
   1541     fn write_u64(&mut self, i: u64) {
   1542         self.0 = i;
   1543     }
   1544     /// Sign-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`].
   1545     #[expect(
   1546         clippy::as_conversions,
   1547         clippy::cast_sign_loss,
   1548         reason = "we simply need to convert into a u64 in a deterministic way"
   1549     )]
   1550     #[inline]
   1551     fn write_i8(&mut self, i: i8) {
   1552         self.write_u64(i as u64);
   1553     }
   1554     /// Sign-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`].
   1555     #[expect(
   1556         clippy::as_conversions,
   1557         clippy::cast_sign_loss,
   1558         reason = "we simply need to convert into a u64 in a deterministic way"
   1559     )]
   1560     #[inline]
   1561     fn write_i16(&mut self, i: i16) {
   1562         self.write_u64(i as u64);
   1563     }
   1564     /// Sign-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`].
   1565     #[expect(
   1566         clippy::as_conversions,
   1567         clippy::cast_sign_loss,
   1568         reason = "we simply need to convert into a u64 in a deterministic way"
   1569     )]
   1570     #[inline]
   1571     fn write_i32(&mut self, i: i32) {
   1572         self.write_u64(i as u64);
   1573     }
   1574     /// Redirects to [`Self::write_u64`].
   1575     #[expect(
   1576         clippy::as_conversions,
   1577         clippy::cast_sign_loss,
   1578         reason = "we simply need to convert into a u64 in a deterministic way"
   1579     )]
   1580     #[inline]
   1581     fn write_i64(&mut self, i: i64) {
   1582         self.write_u64(i as u64);
   1583     }
   1584     /// Truncates `i` to a [`u64`] before redirecting to [`Self::write_u64`].
   1585     #[expect(
   1586         clippy::as_conversions,
   1587         clippy::cast_possible_truncation,
   1588         clippy::cast_sign_loss,
   1589         reason = "we simply need to convert into a u64 in a deterministic way"
   1590     )]
   1591     #[inline]
   1592     fn write_i128(&mut self, i: i128) {
   1593         self.write_u64(i as u64);
   1594     }
   1595     /// Zero-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`].
   1596     #[inline]
   1597     fn write_u8(&mut self, i: u8) {
   1598         self.write_u64(u64::from(i));
   1599     }
   1600     /// Zero-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`].
   1601     #[inline]
   1602     fn write_u16(&mut self, i: u16) {
   1603         self.write_u64(u64::from(i));
   1604     }
   1605     /// Zero-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`].
   1606     #[inline]
   1607     fn write_u32(&mut self, i: u32) {
   1608         self.write_u64(u64::from(i));
   1609     }
   1610     /// Truncates `i` to a [`u64`] before redirecting to [`Self::write_u64`].
   1611     #[expect(
   1612         clippy::as_conversions,
   1613         clippy::cast_possible_truncation,
   1614         reason = "we simply need to convert into a u64 in a deterministic way"
   1615     )]
   1616     #[inline]
   1617     fn write_u128(&mut self, i: u128) {
   1618         self.write_u64(i as u64);
   1619     }
   1620     /// This does nothing iff `bytes.len() < 8`; otherwise the first 8 bytes are converted
   1621     /// to a [`u64`] that is written via [`Self::write_u64`];
   1622     #[expect(clippy::host_endian_bytes, reason = "endianness does not matter")]
   1623     #[inline]
   1624     fn write(&mut self, bytes: &[u8]) {
   1625         if let Some(data) = bytes.get(..8) {
   1626             let mut val = [0; 8];
   1627             val.copy_from_slice(data);
   1628             self.write_u64(u64::from_ne_bytes(val));
   1629         }
   1630     }
   1631 }
   1632 /// [`BuildHasher`] of an [`IdentityHasher`].
   1633 ///
   1634 /// This MUST only be used with hash maps with keys that are randomly generated on the server based on at least 64
   1635 /// bits of entropy.
   1636 #[cfg_attr(docsrs, doc(cfg(not(feature = "serializable_server_state"))))]
   1637 #[cfg(any(doc, not(feature = "serializable_server_state")))]
   1638 #[derive(Clone, Copy, Debug)]
   1639 pub struct BuildIdentityHasher;
   1640 #[cfg_attr(docsrs, doc(cfg(not(feature = "serializable_server_state"))))]
   1641 #[cfg(not(feature = "serializable_server_state"))]
   1642 impl BuildHasher for BuildIdentityHasher {
   1643     type Hasher = IdentityHasher;
   1644     #[inline]
   1645     fn build_hasher(&self) -> Self::Hasher {
   1646         IdentityHasher(0)
   1647     }
   1648 }
   1649 /// "Ceremonies" stored on the server that expire after a certain duration.
   1650 ///
   1651 /// Types like [`RegistrationServerState`] and [`DiscoverableAuthenticationServerState`] are based on [`Challenge`]s
   1652 /// that expire after a certain duration.
   1653 pub trait TimedCeremony {
   1654     /// Returns the `Instant` the ceremony expires.
   1655     ///
   1656     /// Note when `serializable_server_state` is enabled, [`SystemTime`] is returned instead.
   1657     #[cfg_attr(docsrs, doc(cfg(not(feature = "serializable_server_state"))))]
   1658     #[cfg(any(doc, not(feature = "serializable_server_state")))]
   1659     fn expiration(&self) -> Instant;
   1660     /// Returns the `SystemTime` the ceremony expires.
   1661     #[cfg(all(not(doc), feature = "serializable_server_state"))]
   1662     fn expiration(&self) -> SystemTime;
   1663     /// Returns the `SentChallenge`.
   1664     fn sent_challenge(&self) -> SentChallenge;
   1665 }
   1666 /// Fixed-capacity hash set that only inserts items when there is available capacity.
   1667 ///
   1668 /// This should only be used if _both_ of the following conditions are met:
   1669 ///
   1670 /// * Application is already protected from memory exhaustion attacks (e.g., users must be connected
   1671 ///   via VPN).
   1672 /// * There are legitimate reasons for an in-memory collection to grow unbounded.
   1673 ///
   1674 /// The first point is necessary; otherwise an attacker could trivially slow down the application
   1675 /// by causing repeated inserts forcing repeated calls to functions like [`Self::retain`]. The second
   1676 /// point is necessary; otherwise any in-memory collection would suffice.
   1677 ///
   1678 /// When `T` is a [`TimedCeremony`], there are legitimate reasons why a ceremony will never finish (e.g.,
   1679 /// an outage could kill a user's connection after starting a ceremony). The longer the application
   1680 /// runs the more such instances occur to the point where the in-memory collection is full of expired
   1681 /// ceremonies. Since this should rarely occur and as long as [`Self::capacity`] is appropriate,
   1682 /// [`Self::insert`] should almost always succeed; however very rarely there will be a point when
   1683 /// one will have to [`Self::remove_expired_ceremonies`]. A vast majority of the time a user
   1684 /// will complete the ceremony which requires ownership of the `TimedCeremony` which in turn requires
   1685 /// [`Self::take`] which will add an available slot.
   1686 #[cfg_attr(docsrs, doc(cfg(not(feature = "serializable_server_state"))))]
   1687 #[cfg(any(doc, not(feature = "serializable_server_state")))]
   1688 #[derive(Debug)]
   1689 pub struct FixedCapHashSet<T, S = BuildIdentityHasher>(HashSet<T, S>);
   1690 #[cfg(any(doc, not(feature = "serializable_server_state")))]
   1691 impl<T> FixedCapHashSet<T, BuildIdentityHasher> {
   1692     /// Creates an empty `FixedCapHashSet` with at least the specified capacity.
   1693     ///
   1694     /// The hash set will be able to hold at least `capacity` elements without reallocating. This method is allowed
   1695     /// to allocate for more elements than `capacity`.
   1696     #[inline]
   1697     #[must_use]
   1698     pub fn new(capacity: usize) -> Self {
   1699         Self(HashSet::with_capacity_and_hasher(
   1700             capacity,
   1701             BuildIdentityHasher,
   1702         ))
   1703     }
   1704 }
   1705 #[cfg(any(doc, not(feature = "serializable_server_state")))]
   1706 impl<T, S> FixedCapHashSet<T, S> {
   1707     /// Creates an empty `FixedCapHashSet` with at least the specified capacity, using `hasher` to hash the keys.
   1708     ///
   1709     /// The hash set will be able to hold at least `capacity` elements without reallocating. This method is allowed
   1710     /// to allocate for more elements than `capacity`.
   1711     #[inline]
   1712     #[must_use]
   1713     pub fn new_with_hasher(capacity: usize, hasher: S) -> Self {
   1714         Self(HashSet::with_capacity_and_hasher(capacity, hasher))
   1715     }
   1716     /// Returns the immutable capacity.
   1717     ///
   1718     /// This number is a lower bound; the `FixedCapHashSet` might be able to hold more, but is guaranteed to be
   1719     /// able to hold at least this many.
   1720     #[inline]
   1721     #[must_use]
   1722     pub fn capacity(&self) -> usize {
   1723         self.0.capacity()
   1724     }
   1725     /// Clears the set, removing all values.
   1726     #[inline]
   1727     pub fn clear(&mut self) {
   1728         self.0.clear();
   1729     }
   1730     /// Returns the number of elements in the set.
   1731     #[inline]
   1732     #[must_use]
   1733     pub fn len(&self) -> usize {
   1734         self.0.len()
   1735     }
   1736     /// Returns `true` iff the set contains no elements.
   1737     #[inline]
   1738     #[must_use]
   1739     pub fn is_empty(&self) -> bool {
   1740         self.0.is_empty()
   1741     }
   1742     /// Retains only the elements specified by the predicate.
   1743     ///
   1744     /// In other words, remove all elements `e` for which `f(&e)` returns `false`. The elements are visited in
   1745     /// unsorted (and unspecified) order.
   1746     #[inline]
   1747     pub fn retain<F>(&mut self, f: F)
   1748     where
   1749         F: FnMut(&T) -> bool,
   1750     {
   1751         self.0.retain(f);
   1752     }
   1753 }
   1754 #[cfg(any(doc, not(feature = "serializable_server_state")))]
   1755 impl<T: TimedCeremony, S> FixedCapHashSet<T, S> {
   1756     /// Removes all expired ceremonies.
   1757     #[inline]
   1758     pub fn remove_expired_ceremonies(&mut self) {
   1759         // Even though it's more accurate to check the current `Instant` for each ceremony, we elect to capture
   1760         // the `Instant` we begin iteration for performance reasons. It's unlikely an appreciable amount of
   1761         // additional ceremonies would be removed.
   1762         let now = Instant::now();
   1763         self.retain(|v| v.expiration() >= now);
   1764     }
   1765 }
   1766 /// Result returned from [`FixedCapHashSet::insert`], [`FixedCapHashSet::insert_or_replace_expired`], and
   1767 /// [`FixedCapHashSet::insert_or_replace_all_expired`].
   1768 #[cfg_attr(docsrs, doc(cfg(not(feature = "serializable_server_state"))))]
   1769 #[cfg(any(doc, not(feature = "serializable_server_state")))]
   1770 #[derive(Clone, Copy, Debug)]
   1771 pub enum InsertResult {
   1772     /// Value was successfully inserted.
   1773     Success,
   1774     /// Value was not inserted since the capacity was full.
   1775     CapacityFull,
   1776     /// Value was not inserted since the value already existed.
   1777     ///
   1778     /// When the keys are based on [`Challenge`]s, this should almost never occur since `Challenge`s
   1779     /// are 16 bytes of random data.
   1780     Duplicate,
   1781 }
   1782 #[cfg(any(doc, not(feature = "serializable_server_state")))]
   1783 impl<T: Eq + Hash, S: BuildHasher> FixedCapHashSet<T, S> {
   1784     /// Returns `true` iff the set contains a value.
   1785     ///
   1786     /// The value may be any borrowed form of the set's value type, but `Hash` and `Eq` on the borrowed form _must_
   1787     /// match those for the value type.
   1788     #[inline]
   1789     #[must_use]
   1790     pub fn contains<Q>(&self, value: &Q) -> bool
   1791     where
   1792         T: Borrow<Q>,
   1793         Q: Eq + Hash + ?Sized,
   1794     {
   1795         self.0.contains(value)
   1796     }
   1797     /// Returns a reference to the value in the set, if any, that is equal to the given value.
   1798     ///
   1799     /// The value may be any borrowed form of the set's value type, but `Hash` and `Eq` on the borrowed form _must_
   1800     /// match those for the value type.
   1801     #[inline]
   1802     #[must_use]
   1803     pub fn get<Q>(&self, value: &Q) -> Option<&T>
   1804     where
   1805         T: Borrow<Q>,
   1806         Q: Eq + Hash + ?Sized,
   1807     {
   1808         self.0.get(value)
   1809     }
   1810     /// Removes a value from the set. Returns whether the value was present in the set.
   1811     ///
   1812     /// The value may be any borrowed form of the set's value type, but `Hash` and `Eq` on the borrowed form _must_
   1813     /// match those for the value type.
   1814     #[inline]
   1815     pub fn remove<Q>(&mut self, value: &Q) -> bool
   1816     where
   1817         T: Borrow<Q>,
   1818         Q: Eq + Hash + ?Sized,
   1819     {
   1820         self.0.remove(value)
   1821     }
   1822     /// Removes and returns the value in the set, if any, that is equal to the given one.
   1823     ///
   1824     /// The value may be any borrowed form of the set's value type, but `Hash` and `Eq` on the borrowed form _must_
   1825     /// match those for the value type.
   1826     #[inline]
   1827     pub fn take<Q>(&mut self, value: &Q) -> Option<T>
   1828     where
   1829         T: Borrow<Q>,
   1830         Q: Eq + Hash + ?Sized,
   1831     {
   1832         self.0.take(value)
   1833     }
   1834     /// Adds `value` to the set iff [`Self::capacity`] `>` [`Self::len`].
   1835     #[inline]
   1836     pub fn insert(&mut self, value: T) -> InsertResult {
   1837         if self.len() == self.capacity() {
   1838             InsertResult::CapacityFull
   1839         } else if self.0.insert(value) {
   1840             InsertResult::Success
   1841         } else {
   1842             InsertResult::Duplicate
   1843         }
   1844     }
   1845 }
   1846 #[cfg(any(doc, not(feature = "serializable_server_state")))]
   1847 impl<T: Borrow<SentChallenge> + Eq + Hash + TimedCeremony, S: BuildHasher> FixedCapHashSet<T, S> {
   1848     /// Adds a ceremony to the set.
   1849     ///
   1850     /// This will only insert `value` iff [`Self::capacity`] `>` [`Self::len`]. When [`Self::len`] `==`
   1851     /// [`Self::capacity`], this will iterate items in the set until an expired ceremony is encountered; at which
   1852     /// point, the expired ceremony will be removed before `value` is inserted. In the event no expired ceremonies
   1853     /// exist, [`InsertResult::CapacityFull`] will be returned.
   1854     ///
   1855     /// If one wants to avoid the potentially expensive operation of iterating the set for an expired ceremony,
   1856     /// call [`Self::insert`]. Alternatively one can call [`Self::insert_or_replace_all_expired`] to avoid
   1857     /// repeatedly iterating the hash set once its capacity is full.
   1858     #[inline]
   1859     pub fn insert_or_replace_expired(&mut self, value: T) -> InsertResult {
   1860         if self.len() == self.capacity() {
   1861             let now = Instant::now();
   1862             self.0
   1863                 .iter()
   1864                 .try_fold((), |(), v| {
   1865                     if v.expiration() < now {
   1866                         Err(v.sent_challenge())
   1867                     } else {
   1868                         Ok(())
   1869                     }
   1870                 })
   1871                 .map_or_else(
   1872                     |chall| {
   1873                         self.remove(&chall);
   1874                         if self.0.insert(value) {
   1875                             InsertResult::Success
   1876                         } else {
   1877                             InsertResult::Duplicate
   1878                         }
   1879                     },
   1880                     |()| InsertResult::CapacityFull,
   1881                 )
   1882         } else if self.0.insert(value) {
   1883             InsertResult::Success
   1884         } else {
   1885             InsertResult::Duplicate
   1886         }
   1887     }
   1888     /// Adds a ceremony to the set.
   1889     ///
   1890     /// This will only insert `value` iff [`Self::capacity`] `>` [`Self::len`]. When [`Self::len`] `==`
   1891     /// [`Self::capacity`], this will [`Self::remove_expired_ceremonies`] before `value` is inserted. In the event
   1892     /// no expired ceremonies exist, [`InsertResult::CapacityFull`] will be returned.
   1893     #[inline]
   1894     pub fn insert_or_replace_all_expired(&mut self, value: T) -> InsertResult {
   1895         if self.len() == self.capacity() {
   1896             self.remove_expired_ceremonies();
   1897         }
   1898         if self.len() == self.capacity() {
   1899             InsertResult::CapacityFull
   1900         } else if self.0.insert(value) {
   1901             InsertResult::Success
   1902         } else {
   1903             InsertResult::Duplicate
   1904         }
   1905     }
   1906 }
   1907 #[cfg(test)]
   1908 mod tests {
   1909     #[cfg(feature = "custom")]
   1910     use super::{
   1911         super::{
   1912             AggErr, AuthenticatedCredential,
   1913             response::{
   1914                 AuthTransports, AuthenticatorAttachment, Backup, CredentialId,
   1915                 auth::{
   1916                     DiscoverableAuthentication, DiscoverableAuthenticatorAssertion,
   1917                     NonDiscoverableAuthentication, NonDiscoverableAuthenticatorAssertion,
   1918                 },
   1919                 register::{
   1920                     AuthenticationExtensionsPrfOutputs, AuthenticatorAttestation,
   1921                     AuthenticatorExtensionOutputStaticState, ClientExtensionsOutputs,
   1922                     CompressedP256PubKey, CompressedP384PubKey, CompressedPubKey,
   1923                     CredentialProtectionPolicy, DynamicState, Ed25519PubKey, Registration,
   1924                     RsaPubKey, StaticState, UncompressedPubKey,
   1925                 },
   1926             },
   1927         },
   1928         AsciiDomain, Challenge, Credentials, ExtensionInfo, ExtensionReq,
   1929         PublicKeyCredentialDescriptor, RpId, UserVerificationRequirement,
   1930         auth::{
   1931             AllowedCredential, AllowedCredentials, AuthenticationVerificationOptions,
   1932             CredentialSpecificExtension, DiscoverableCredentialRequestOptions,
   1933             Extension as AuthExt, NonDiscoverableCredentialRequestOptions, PrfInputOwned,
   1934         },
   1935         register::{
   1936             CredProtect, Extension as RegExt, PublicKeyCredentialCreationOptions,
   1937             PublicKeyCredentialUserEntity, RegistrationVerificationOptions, UserHandle,
   1938         },
   1939     };
   1940     #[cfg(feature = "custom")]
   1941     use ed25519_dalek::{Signer, SigningKey};
   1942     #[cfg(feature = "custom")]
   1943     use p256::{
   1944         ecdsa::{DerSignature as P256DerSig, SigningKey as P256Key},
   1945         elliptic_curve::sec1::Tag,
   1946     };
   1947     #[cfg(feature = "custom")]
   1948     use p384::ecdsa::{DerSignature as P384DerSig, SigningKey as P384Key};
   1949     #[cfg(feature = "custom")]
   1950     use rsa::{
   1951         BigUint, RsaPrivateKey,
   1952         pkcs1v15::SigningKey as RsaKey,
   1953         sha2::{Digest, Sha256},
   1954         signature::{Keypair, SignatureEncoding},
   1955         traits::PublicKeyParts,
   1956     };
   1957     #[cfg(feature = "custom")]
   1958     const CBOR_UINT: u8 = 0b000_00000;
   1959     #[cfg(feature = "custom")]
   1960     const CBOR_NEG: u8 = 0b001_00000;
   1961     #[cfg(feature = "custom")]
   1962     const CBOR_BYTES: u8 = 0b010_00000;
   1963     #[cfg(feature = "custom")]
   1964     const CBOR_TEXT: u8 = 0b011_00000;
   1965     #[cfg(feature = "custom")]
   1966     const CBOR_MAP: u8 = 0b101_00000;
   1967     #[cfg(feature = "custom")]
   1968     const CBOR_SIMPLE: u8 = 0b111_00000;
   1969     #[cfg(feature = "custom")]
   1970     const CBOR_TRUE: u8 = CBOR_SIMPLE | 21;
   1971     #[test]
   1972     #[cfg(feature = "custom")]
   1973     fn ed25519_reg() -> Result<(), AggErr> {
   1974         let rp_id = RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?);
   1975         let id = UserHandle::from([0]);
   1976         let mut opts = PublicKeyCredentialCreationOptions::passkey(
   1977             &rp_id,
   1978             PublicKeyCredentialUserEntity {
   1979                 name: "foo".try_into()?,
   1980                 id: &id,
   1981                 display_name: None,
   1982             },
   1983             Vec::new(),
   1984         );
   1985         opts.challenge = Challenge(0);
   1986         opts.extensions = RegExt {
   1987             cred_props: None,
   1988             cred_protect: CredProtect::UserVerificationRequired(ExtensionInfo::RequireEnforceValue),
   1989             min_pin_length: Some((10, ExtensionInfo::RequireEnforceValue)),
   1990             prf: Some(ExtensionInfo::RequireEnforceValue),
   1991         };
   1992         let client_data_json = br#"{"type":"webauthn.create","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec();
   1993         // We over-allocate by 32 bytes. See [`AuthenticatorAttestation::new`] for more information.
   1994         let mut attestation_object = Vec::new();
   1995         attestation_object.extend_from_slice(
   1996             [
   1997                 CBOR_MAP | 3,
   1998                 CBOR_TEXT | 3,
   1999                 b'f',
   2000                 b'm',
   2001                 b't',
   2002                 CBOR_TEXT | 6,
   2003                 b'p',
   2004                 b'a',
   2005                 b'c',
   2006                 b'k',
   2007                 b'e',
   2008                 b'd',
   2009                 CBOR_TEXT | 7,
   2010                 b'a',
   2011                 b't',
   2012                 b't',
   2013                 b'S',
   2014                 b't',
   2015                 b'm',
   2016                 b't',
   2017                 CBOR_MAP | 2,
   2018                 CBOR_TEXT | 3,
   2019                 b'a',
   2020                 b'l',
   2021                 b'g',
   2022                 // COSE EdDSA.
   2023                 CBOR_NEG | 7,
   2024                 CBOR_TEXT | 3,
   2025                 b's',
   2026                 b'i',
   2027                 b'g',
   2028                 CBOR_BYTES | 24,
   2029                 64,
   2030                 0,
   2031                 0,
   2032                 0,
   2033                 0,
   2034                 0,
   2035                 0,
   2036                 0,
   2037                 0,
   2038                 0,
   2039                 0,
   2040                 0,
   2041                 0,
   2042                 0,
   2043                 0,
   2044                 0,
   2045                 0,
   2046                 0,
   2047                 0,
   2048                 0,
   2049                 0,
   2050                 0,
   2051                 0,
   2052                 0,
   2053                 0,
   2054                 0,
   2055                 0,
   2056                 0,
   2057                 0,
   2058                 0,
   2059                 0,
   2060                 0,
   2061                 0,
   2062                 0,
   2063                 0,
   2064                 0,
   2065                 0,
   2066                 0,
   2067                 0,
   2068                 0,
   2069                 0,
   2070                 0,
   2071                 0,
   2072                 0,
   2073                 0,
   2074                 0,
   2075                 0,
   2076                 0,
   2077                 0,
   2078                 0,
   2079                 0,
   2080                 0,
   2081                 0,
   2082                 0,
   2083                 0,
   2084                 0,
   2085                 0,
   2086                 0,
   2087                 0,
   2088                 0,
   2089                 0,
   2090                 0,
   2091                 0,
   2092                 0,
   2093                 0,
   2094                 CBOR_TEXT | 8,
   2095                 b'a',
   2096                 b'u',
   2097                 b't',
   2098                 b'h',
   2099                 b'D',
   2100                 b'a',
   2101                 b't',
   2102                 b'a',
   2103                 CBOR_BYTES | 24,
   2104                 // Length is 154.
   2105                 154,
   2106                 // RP ID HASH.
   2107                 // This will be overwritten later.
   2108                 0,
   2109                 0,
   2110                 0,
   2111                 0,
   2112                 0,
   2113                 0,
   2114                 0,
   2115                 0,
   2116                 0,
   2117                 0,
   2118                 0,
   2119                 0,
   2120                 0,
   2121                 0,
   2122                 0,
   2123                 0,
   2124                 0,
   2125                 0,
   2126                 0,
   2127                 0,
   2128                 0,
   2129                 0,
   2130                 0,
   2131                 0,
   2132                 0,
   2133                 0,
   2134                 0,
   2135                 0,
   2136                 0,
   2137                 0,
   2138                 0,
   2139                 0,
   2140                 // FLAGS.
   2141                 // UP, UV, AT, and ED (right-to-left).
   2142                 0b1100_0101,
   2143                 // COUNTER.
   2144                 // 0 as 32-bit big endian.
   2145                 0,
   2146                 0,
   2147                 0,
   2148                 0,
   2149                 // AAGUID.
   2150                 0,
   2151                 0,
   2152                 0,
   2153                 0,
   2154                 0,
   2155                 0,
   2156                 0,
   2157                 0,
   2158                 0,
   2159                 0,
   2160                 0,
   2161                 0,
   2162                 0,
   2163                 0,
   2164                 0,
   2165                 0,
   2166                 // L.
   2167                 // CREDENTIAL ID length is 16 as 16-bit big endian.
   2168                 0,
   2169                 16,
   2170                 // CREDENTIAL ID.
   2171                 0,
   2172                 0,
   2173                 0,
   2174                 0,
   2175                 0,
   2176                 0,
   2177                 0,
   2178                 0,
   2179                 0,
   2180                 0,
   2181                 0,
   2182                 0,
   2183                 0,
   2184                 0,
   2185                 0,
   2186                 0,
   2187                 CBOR_MAP | 4,
   2188                 // COSE kty.
   2189                 CBOR_UINT | 1,
   2190                 // COSE OKP.
   2191                 CBOR_UINT | 1,
   2192                 // COSE alg.
   2193                 CBOR_UINT | 3,
   2194                 // COSE EdDSA.
   2195                 CBOR_NEG | 7,
   2196                 // COSE OKP crv.
   2197                 CBOR_NEG,
   2198                 // COSE Ed25519.
   2199                 CBOR_UINT | 6,
   2200                 // COSE OKP x.
   2201                 CBOR_NEG | 1,
   2202                 CBOR_BYTES | 24,
   2203                 // Length is 32.
   2204                 32,
   2205                 // Compressed-y coordinate.
   2206                 // This will be overwritten later.
   2207                 0,
   2208                 0,
   2209                 0,
   2210                 0,
   2211                 0,
   2212                 0,
   2213                 0,
   2214                 0,
   2215                 0,
   2216                 0,
   2217                 0,
   2218                 0,
   2219                 0,
   2220                 0,
   2221                 0,
   2222                 0,
   2223                 0,
   2224                 0,
   2225                 0,
   2226                 0,
   2227                 0,
   2228                 0,
   2229                 0,
   2230                 0,
   2231                 0,
   2232                 0,
   2233                 0,
   2234                 0,
   2235                 0,
   2236                 0,
   2237                 0,
   2238                 0,
   2239                 CBOR_MAP | 3,
   2240                 CBOR_TEXT | 11,
   2241                 b'c',
   2242                 b'r',
   2243                 b'e',
   2244                 b'd',
   2245                 b'P',
   2246                 b'r',
   2247                 b'o',
   2248                 b't',
   2249                 b'e',
   2250                 b'c',
   2251                 b't',
   2252                 // userVerificationRequired.
   2253                 CBOR_UINT | 3,
   2254                 // CBOR text of length 11.
   2255                 CBOR_TEXT | 11,
   2256                 b'h',
   2257                 b'm',
   2258                 b'a',
   2259                 b'c',
   2260                 b'-',
   2261                 b's',
   2262                 b'e',
   2263                 b'c',
   2264                 b'r',
   2265                 b'e',
   2266                 b't',
   2267                 CBOR_TRUE,
   2268                 CBOR_TEXT | 12,
   2269                 b'm',
   2270                 b'i',
   2271                 b'n',
   2272                 b'P',
   2273                 b'i',
   2274                 b'n',
   2275                 b'L',
   2276                 b'e',
   2277                 b'n',
   2278                 b'g',
   2279                 b't',
   2280                 b'h',
   2281                 CBOR_UINT | 16,
   2282             ]
   2283             .as_slice(),
   2284         );
   2285         attestation_object
   2286             .extend_from_slice(Sha256::digest(client_data_json.as_slice()).as_slice());
   2287         let sig_key = SigningKey::from_bytes(&[0; 32]);
   2288         let ver_key = sig_key.verifying_key();
   2289         let pub_key = ver_key.as_bytes();
   2290         attestation_object[107..139]
   2291             .copy_from_slice(Sha256::digest(rp_id.as_ref().as_bytes()).as_slice());
   2292         attestation_object[188..220].copy_from_slice(pub_key);
   2293         let sig = sig_key.sign(&attestation_object[107..]);
   2294         attestation_object[32..96].copy_from_slice(sig.to_bytes().as_slice());
   2295         attestation_object.truncate(261);
   2296         assert!(matches!(opts.start_ceremony()?.0.verify(
   2297             &rp_id,
   2298             &Registration {
   2299                 response: AuthenticatorAttestation::new(
   2300                     client_data_json,
   2301                     attestation_object,
   2302                     AuthTransports::NONE,
   2303                 ),
   2304                 authenticator_attachment: AuthenticatorAttachment::None,
   2305                 client_extension_results: ClientExtensionsOutputs {
   2306                     cred_props: None,
   2307                     prf: Some(AuthenticationExtensionsPrfOutputs { enabled: true, }),
   2308                 },
   2309             },
   2310             &RegistrationVerificationOptions::<&str, &str>::default(),
   2311         )?.static_state.credential_public_key, UncompressedPubKey::Ed25519(k) if k.into_inner() == pub_key));
   2312         Ok(())
   2313     }
   2314     #[test]
   2315     #[cfg(feature = "custom")]
   2316     fn ed25519_auth() -> Result<(), AggErr> {
   2317         let rp_id = RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?);
   2318         let mut creds = AllowedCredentials::with_capacity(1);
   2319         creds.push(AllowedCredential {
   2320             credential: PublicKeyCredentialDescriptor {
   2321                 id: CredentialId::try_from(vec![0; 16])?,
   2322                 transports: AuthTransports::NONE,
   2323             },
   2324             extension: CredentialSpecificExtension {
   2325                 prf: Some(PrfInputOwned {
   2326                     first: Vec::new(),
   2327                     second: Some(Vec::new()),
   2328                     ext_info: ExtensionReq::Require,
   2329                 }),
   2330             },
   2331         });
   2332         let mut opts = NonDiscoverableCredentialRequestOptions::second_factor(&rp_id, creds)?;
   2333         opts.options().user_verification = UserVerificationRequirement::Required;
   2334         opts.options().challenge = Challenge(0);
   2335         opts.options().extensions = AuthExt { prf: None };
   2336         let client_data_json = br#"{"type":"webauthn.get","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec();
   2337         // We over-allocate by 32 bytes. See [`AuthenticatorAssertion::new`] for more information.
   2338         let mut authenticator_data = Vec::with_capacity(164);
   2339         authenticator_data.extend_from_slice(
   2340             [
   2341                 // rpIdHash.
   2342                 // This will be overwritten later.
   2343                 0,
   2344                 0,
   2345                 0,
   2346                 0,
   2347                 0,
   2348                 0,
   2349                 0,
   2350                 0,
   2351                 0,
   2352                 0,
   2353                 0,
   2354                 0,
   2355                 0,
   2356                 0,
   2357                 0,
   2358                 0,
   2359                 0,
   2360                 0,
   2361                 0,
   2362                 0,
   2363                 0,
   2364                 0,
   2365                 0,
   2366                 0,
   2367                 0,
   2368                 0,
   2369                 0,
   2370                 0,
   2371                 0,
   2372                 0,
   2373                 0,
   2374                 0,
   2375                 // flags.
   2376                 // UP, UV, and ED (right-to-left).
   2377                 0b1000_0101,
   2378                 // signCount.
   2379                 // 0 as 32-bit big endian.
   2380                 0,
   2381                 0,
   2382                 0,
   2383                 0,
   2384                 CBOR_MAP | 1,
   2385                 CBOR_TEXT | 11,
   2386                 b'h',
   2387                 b'm',
   2388                 b'a',
   2389                 b'c',
   2390                 b'-',
   2391                 b's',
   2392                 b'e',
   2393                 b'c',
   2394                 b'r',
   2395                 b'e',
   2396                 b't',
   2397                 CBOR_BYTES | 24,
   2398                 // Length is 80.
   2399                 80,
   2400                 // Two HMAC outputs concatenated and encrypted.
   2401                 0,
   2402                 0,
   2403                 0,
   2404                 0,
   2405                 0,
   2406                 0,
   2407                 0,
   2408                 0,
   2409                 0,
   2410                 0,
   2411                 0,
   2412                 0,
   2413                 0,
   2414                 0,
   2415                 0,
   2416                 0,
   2417                 0,
   2418                 0,
   2419                 0,
   2420                 0,
   2421                 0,
   2422                 0,
   2423                 0,
   2424                 0,
   2425                 0,
   2426                 0,
   2427                 0,
   2428                 0,
   2429                 0,
   2430                 0,
   2431                 0,
   2432                 0,
   2433                 0,
   2434                 0,
   2435                 0,
   2436                 0,
   2437                 0,
   2438                 0,
   2439                 0,
   2440                 0,
   2441                 0,
   2442                 0,
   2443                 0,
   2444                 0,
   2445                 0,
   2446                 0,
   2447                 0,
   2448                 0,
   2449                 0,
   2450                 0,
   2451                 0,
   2452                 0,
   2453                 0,
   2454                 0,
   2455                 0,
   2456                 0,
   2457                 0,
   2458                 0,
   2459                 0,
   2460                 0,
   2461                 0,
   2462                 0,
   2463                 0,
   2464                 0,
   2465                 0,
   2466                 0,
   2467                 0,
   2468                 0,
   2469                 0,
   2470                 0,
   2471                 0,
   2472                 0,
   2473                 0,
   2474                 0,
   2475                 0,
   2476                 0,
   2477                 0,
   2478                 0,
   2479                 0,
   2480                 0,
   2481             ]
   2482             .as_slice(),
   2483         );
   2484         authenticator_data[..32]
   2485             .copy_from_slice(Sha256::digest(rp_id.as_ref().as_bytes()).as_slice());
   2486         authenticator_data
   2487             .extend_from_slice(Sha256::digest(client_data_json.as_slice()).as_slice());
   2488         let ed_priv = SigningKey::from([0; 32]);
   2489         let sig = ed_priv.sign(authenticator_data.as_slice()).to_vec();
   2490         authenticator_data.truncate(132);
   2491         assert!(!opts.start_ceremony()?.0.verify(
   2492             &rp_id,
   2493             &NonDiscoverableAuthentication {
   2494                 raw_id: CredentialId::try_from(vec![0; 16])?,
   2495                 response: NonDiscoverableAuthenticatorAssertion::with_user(
   2496                     client_data_json,
   2497                     authenticator_data,
   2498                     sig,
   2499                     UserHandle::from([0]),
   2500                 ),
   2501                 authenticator_attachment: AuthenticatorAttachment::None,
   2502             },
   2503             &mut AuthenticatedCredential::new(
   2504                 CredentialId::try_from([0; 16].as_slice())?,
   2505                 &UserHandle::from([0]),
   2506                 StaticState {
   2507                     credential_public_key: CompressedPubKey::<_, &[u8], &[u8], &[u8]>::Ed25519(
   2508                         Ed25519PubKey::from(ed_priv.verifying_key().to_bytes()),
   2509                     ),
   2510                     extensions: AuthenticatorExtensionOutputStaticState {
   2511                         cred_protect: CredentialProtectionPolicy::None,
   2512                         hmac_secret: Some(true),
   2513                     },
   2514                 },
   2515                 DynamicState {
   2516                     user_verified: true,
   2517                     backup: Backup::NotEligible,
   2518                     sign_count: 0,
   2519                     authenticator_attachment: AuthenticatorAttachment::None,
   2520                 },
   2521             )?,
   2522             &AuthenticationVerificationOptions::<&str, &str>::default(),
   2523         )?);
   2524         Ok(())
   2525     }
   2526     #[test]
   2527     #[cfg(feature = "custom")]
   2528     fn p256_reg() -> Result<(), AggErr> {
   2529         let rp_id = RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?);
   2530         let id = UserHandle::from([0]);
   2531         let mut opts = PublicKeyCredentialCreationOptions::passkey(
   2532             &rp_id,
   2533             PublicKeyCredentialUserEntity {
   2534                 name: "foo".try_into()?,
   2535                 id: &id,
   2536                 display_name: None,
   2537             },
   2538             Vec::new(),
   2539         );
   2540         opts.challenge = Challenge(0);
   2541         let client_data_json = br#"{"type":"webauthn.create","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec();
   2542         // We over-allocate by 32 bytes. See [`AuthenticatorAttestation::new`] for more information.
   2543         let mut attestation_object = Vec::with_capacity(210);
   2544         attestation_object.extend_from_slice(
   2545             [
   2546                 CBOR_MAP | 3,
   2547                 CBOR_TEXT | 3,
   2548                 b'f',
   2549                 b'm',
   2550                 b't',
   2551                 CBOR_TEXT | 4,
   2552                 b'n',
   2553                 b'o',
   2554                 b'n',
   2555                 b'e',
   2556                 CBOR_TEXT | 7,
   2557                 b'a',
   2558                 b't',
   2559                 b't',
   2560                 b'S',
   2561                 b't',
   2562                 b'm',
   2563                 b't',
   2564                 CBOR_MAP,
   2565                 CBOR_TEXT | 8,
   2566                 b'a',
   2567                 b'u',
   2568                 b't',
   2569                 b'h',
   2570                 b'D',
   2571                 b'a',
   2572                 b't',
   2573                 b'a',
   2574                 CBOR_BYTES | 24,
   2575                 // Length is 148.
   2576                 148,
   2577                 // RP ID HASH.
   2578                 // This will be overwritten later.
   2579                 0,
   2580                 0,
   2581                 0,
   2582                 0,
   2583                 0,
   2584                 0,
   2585                 0,
   2586                 0,
   2587                 0,
   2588                 0,
   2589                 0,
   2590                 0,
   2591                 0,
   2592                 0,
   2593                 0,
   2594                 0,
   2595                 0,
   2596                 0,
   2597                 0,
   2598                 0,
   2599                 0,
   2600                 0,
   2601                 0,
   2602                 0,
   2603                 0,
   2604                 0,
   2605                 0,
   2606                 0,
   2607                 0,
   2608                 0,
   2609                 0,
   2610                 0,
   2611                 // FLAGS.
   2612                 // UP, UV, and AT (right-to-left).
   2613                 0b0100_0101,
   2614                 // COUNTER.
   2615                 // 0 as 32-bit big endian.
   2616                 0,
   2617                 0,
   2618                 0,
   2619                 0,
   2620                 // AAGUID.
   2621                 0,
   2622                 0,
   2623                 0,
   2624                 0,
   2625                 0,
   2626                 0,
   2627                 0,
   2628                 0,
   2629                 0,
   2630                 0,
   2631                 0,
   2632                 0,
   2633                 0,
   2634                 0,
   2635                 0,
   2636                 0,
   2637                 // L.
   2638                 // CREDENTIAL ID length is 16 as 16-bit big endian.
   2639                 0,
   2640                 16,
   2641                 // CREDENTIAL ID.
   2642                 0,
   2643                 0,
   2644                 0,
   2645                 0,
   2646                 0,
   2647                 0,
   2648                 0,
   2649                 0,
   2650                 0,
   2651                 0,
   2652                 0,
   2653                 0,
   2654                 0,
   2655                 0,
   2656                 0,
   2657                 0,
   2658                 CBOR_MAP | 5,
   2659                 // COSE kty.
   2660                 CBOR_UINT | 1,
   2661                 // COSE EC2.
   2662                 CBOR_UINT | 2,
   2663                 // COSE alg.
   2664                 CBOR_UINT | 3,
   2665                 // COSE ES256.
   2666                 CBOR_NEG | 6,
   2667                 // COSE EC2 crv.
   2668                 CBOR_NEG,
   2669                 // COSE P-256.
   2670                 CBOR_UINT | 1,
   2671                 // COSE EC2 x.
   2672                 CBOR_NEG | 1,
   2673                 CBOR_BYTES | 24,
   2674                 // Length is 32.
   2675                 32,
   2676                 // X-coordinate. This will be overwritten later.
   2677                 0,
   2678                 0,
   2679                 0,
   2680                 0,
   2681                 0,
   2682                 0,
   2683                 0,
   2684                 0,
   2685                 0,
   2686                 0,
   2687                 0,
   2688                 0,
   2689                 0,
   2690                 0,
   2691                 0,
   2692                 0,
   2693                 0,
   2694                 0,
   2695                 0,
   2696                 0,
   2697                 0,
   2698                 0,
   2699                 0,
   2700                 0,
   2701                 0,
   2702                 0,
   2703                 0,
   2704                 0,
   2705                 0,
   2706                 0,
   2707                 0,
   2708                 0,
   2709                 // COSE EC2 y.
   2710                 CBOR_NEG | 2,
   2711                 CBOR_BYTES | 24,
   2712                 // Length is 32.
   2713                 32,
   2714                 // Y-coordinate. This will be overwritten later.
   2715                 0,
   2716                 0,
   2717                 0,
   2718                 0,
   2719                 0,
   2720                 0,
   2721                 0,
   2722                 0,
   2723                 0,
   2724                 0,
   2725                 0,
   2726                 0,
   2727                 0,
   2728                 0,
   2729                 0,
   2730                 0,
   2731                 0,
   2732                 0,
   2733                 0,
   2734                 0,
   2735                 0,
   2736                 0,
   2737                 0,
   2738                 0,
   2739                 0,
   2740                 0,
   2741                 0,
   2742                 0,
   2743                 0,
   2744                 0,
   2745                 0,
   2746                 0,
   2747             ]
   2748             .as_slice(),
   2749         );
   2750         attestation_object[30..62]
   2751             .copy_from_slice(Sha256::digest(rp_id.as_ref().as_bytes()).as_slice());
   2752         let p256_key = P256Key::from_bytes(
   2753             &[
   2754                 137, 133, 36, 206, 163, 47, 255, 5, 76, 144, 163, 141, 40, 109, 108, 240, 246, 115,
   2755                 178, 237, 169, 68, 6, 129, 92, 21, 238, 127, 55, 158, 207, 95,
   2756             ]
   2757             .into(),
   2758         )
   2759         .unwrap()
   2760         .verifying_key()
   2761         .to_encoded_point(false);
   2762         let x = p256_key.x().unwrap();
   2763         let y = p256_key.y().unwrap();
   2764         attestation_object[111..143].copy_from_slice(x);
   2765         attestation_object[146..].copy_from_slice(y);
   2766         assert!(matches!(opts.start_ceremony()?.0.verify(
   2767             &rp_id,
   2768             &Registration {
   2769                 response: AuthenticatorAttestation::new(
   2770                     client_data_json,
   2771                     attestation_object,
   2772                     AuthTransports::NONE,
   2773                 ),
   2774                 authenticator_attachment: AuthenticatorAttachment::None,
   2775                 client_extension_results: ClientExtensionsOutputs {
   2776                     cred_props: None,
   2777                     prf: None,
   2778                 },
   2779             },
   2780             &RegistrationVerificationOptions::<&str, &str>::default(),
   2781         )?.static_state.credential_public_key, UncompressedPubKey::P256(k) if k.x() == x.as_slice() && k.y() == y.as_slice()));
   2782         Ok(())
   2783     }
   2784     #[test]
   2785     #[cfg(feature = "custom")]
   2786     fn p256_auth() -> Result<(), AggErr> {
   2787         let rp_id = RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?);
   2788         let mut opts = DiscoverableCredentialRequestOptions::passkey(&rp_id);
   2789         opts.0.challenge = Challenge(0);
   2790         let client_data_json = br#"{"type":"webauthn.get","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec();
   2791         // We over-allocate by 32 bytes. See [`AuthenticatorAssertion::new`] for more information.
   2792         let mut authenticator_data = Vec::with_capacity(69);
   2793         authenticator_data.extend_from_slice(
   2794             [
   2795                 // rpIdHash.
   2796                 // This will be overwritten later.
   2797                 0,
   2798                 0,
   2799                 0,
   2800                 0,
   2801                 0,
   2802                 0,
   2803                 0,
   2804                 0,
   2805                 0,
   2806                 0,
   2807                 0,
   2808                 0,
   2809                 0,
   2810                 0,
   2811                 0,
   2812                 0,
   2813                 0,
   2814                 0,
   2815                 0,
   2816                 0,
   2817                 0,
   2818                 0,
   2819                 0,
   2820                 0,
   2821                 0,
   2822                 0,
   2823                 0,
   2824                 0,
   2825                 0,
   2826                 0,
   2827                 0,
   2828                 0,
   2829                 // flags.
   2830                 // UP and UV (right-to-left).
   2831                 0b0000_0101,
   2832                 // signCount.
   2833                 // 0 as 32-bit big endian.
   2834                 0,
   2835                 0,
   2836                 0,
   2837                 0,
   2838             ]
   2839             .as_slice(),
   2840         );
   2841         authenticator_data[..32]
   2842             .copy_from_slice(Sha256::digest(rp_id.as_ref().as_bytes()).as_slice());
   2843         authenticator_data
   2844             .extend_from_slice(Sha256::digest(client_data_json.as_slice()).as_slice());
   2845         let p256_key = P256Key::from_bytes(
   2846             &[
   2847                 137, 133, 36, 206, 163, 47, 255, 5, 76, 144, 163, 141, 40, 109, 108, 240, 246, 115,
   2848                 178, 237, 169, 68, 6, 129, 92, 21, 238, 127, 55, 158, 207, 95,
   2849             ]
   2850             .into(),
   2851         )
   2852         .unwrap();
   2853         let der_sig: P256DerSig = p256_key.sign(authenticator_data.as_slice());
   2854         let pub_key = p256_key.verifying_key().to_encoded_point(true);
   2855         authenticator_data.truncate(37);
   2856         assert!(!opts.start_ceremony()?.0.verify(
   2857             &rp_id,
   2858             &DiscoverableAuthentication {
   2859                 raw_id: CredentialId::try_from(vec![0; 16])?,
   2860                 response: DiscoverableAuthenticatorAssertion::new(
   2861                     client_data_json,
   2862                     authenticator_data,
   2863                     der_sig.as_bytes().into(),
   2864                     UserHandle::from([0]),
   2865                 ),
   2866                 authenticator_attachment: AuthenticatorAttachment::None,
   2867             },
   2868             &mut AuthenticatedCredential::new(
   2869                 CredentialId::try_from([0; 16].as_slice())?,
   2870                 &UserHandle::from([0]),
   2871                 StaticState {
   2872                     credential_public_key: CompressedPubKey::<&[u8], _, &[u8], &[u8]>::P256(
   2873                         CompressedP256PubKey::from((
   2874                             (*pub_key.x().unwrap()).into(),
   2875                             pub_key.tag() == Tag::CompressedOddY
   2876                         )),
   2877                     ),
   2878                     extensions: AuthenticatorExtensionOutputStaticState {
   2879                         cred_protect: CredentialProtectionPolicy::None,
   2880                         hmac_secret: None,
   2881                     },
   2882                 },
   2883                 DynamicState {
   2884                     user_verified: true,
   2885                     backup: Backup::NotEligible,
   2886                     sign_count: 0,
   2887                     authenticator_attachment: AuthenticatorAttachment::None,
   2888                 },
   2889             )?,
   2890             &AuthenticationVerificationOptions::<&str, &str>::default(),
   2891         )?);
   2892         Ok(())
   2893     }
   2894     #[test]
   2895     #[cfg(feature = "custom")]
   2896     fn p384_reg() -> Result<(), AggErr> {
   2897         let rp_id = RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?);
   2898         let id = UserHandle::from([0]);
   2899         let mut opts = PublicKeyCredentialCreationOptions::passkey(
   2900             &rp_id,
   2901             PublicKeyCredentialUserEntity {
   2902                 name: "foo".try_into()?,
   2903                 id: &id,
   2904                 display_name: None,
   2905             },
   2906             Vec::new(),
   2907         );
   2908         opts.challenge = Challenge(0);
   2909         let client_data_json = br#"{"type":"webauthn.create","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec();
   2910         // We over-allocate by 32 bytes. See [`AuthenticatorAttestation::new`] for more information.
   2911         let mut attestation_object = Vec::with_capacity(243);
   2912         attestation_object.extend_from_slice(
   2913             [
   2914                 CBOR_MAP | 3,
   2915                 CBOR_TEXT | 3,
   2916                 b'f',
   2917                 b'm',
   2918                 b't',
   2919                 CBOR_TEXT | 4,
   2920                 b'n',
   2921                 b'o',
   2922                 b'n',
   2923                 b'e',
   2924                 // CBOR text of length 7.
   2925                 CBOR_TEXT | 7,
   2926                 b'a',
   2927                 b't',
   2928                 b't',
   2929                 b'S',
   2930                 b't',
   2931                 b'm',
   2932                 b't',
   2933                 CBOR_MAP,
   2934                 CBOR_TEXT | 8,
   2935                 b'a',
   2936                 b'u',
   2937                 b't',
   2938                 b'h',
   2939                 b'D',
   2940                 b'a',
   2941                 b't',
   2942                 b'a',
   2943                 CBOR_BYTES | 24,
   2944                 // Length is 181.
   2945                 181,
   2946                 // RP ID HASH.
   2947                 // This will be overwritten later.
   2948                 0,
   2949                 0,
   2950                 0,
   2951                 0,
   2952                 0,
   2953                 0,
   2954                 0,
   2955                 0,
   2956                 0,
   2957                 0,
   2958                 0,
   2959                 0,
   2960                 0,
   2961                 0,
   2962                 0,
   2963                 0,
   2964                 0,
   2965                 0,
   2966                 0,
   2967                 0,
   2968                 0,
   2969                 0,
   2970                 0,
   2971                 0,
   2972                 0,
   2973                 0,
   2974                 0,
   2975                 0,
   2976                 0,
   2977                 0,
   2978                 0,
   2979                 0,
   2980                 // FLAGS.
   2981                 // UP, UV, and AT (right-to-left).
   2982                 0b0100_0101,
   2983                 // COUNTER.
   2984                 // 0 as 32-bit big-endian.
   2985                 0,
   2986                 0,
   2987                 0,
   2988                 0,
   2989                 // AAGUID.
   2990                 0,
   2991                 0,
   2992                 0,
   2993                 0,
   2994                 0,
   2995                 0,
   2996                 0,
   2997                 0,
   2998                 0,
   2999                 0,
   3000                 0,
   3001                 0,
   3002                 0,
   3003                 0,
   3004                 0,
   3005                 0,
   3006                 // L.
   3007                 // CREDENTIAL ID length is 16 as 16-bit big endian.
   3008                 0,
   3009                 16,
   3010                 // CREDENTIAL ID.
   3011                 0,
   3012                 0,
   3013                 0,
   3014                 0,
   3015                 0,
   3016                 0,
   3017                 0,
   3018                 0,
   3019                 0,
   3020                 0,
   3021                 0,
   3022                 0,
   3023                 0,
   3024                 0,
   3025                 0,
   3026                 0,
   3027                 CBOR_MAP | 5,
   3028                 // COSE kty.
   3029                 CBOR_UINT | 1,
   3030                 // COSE EC2.
   3031                 CBOR_UINT | 2,
   3032                 // COSE alg.
   3033                 CBOR_UINT | 3,
   3034                 CBOR_NEG | 24,
   3035                 // COSE ES384.
   3036                 34,
   3037                 // COSE EC2 crv.
   3038                 CBOR_NEG,
   3039                 // COSE P-384.
   3040                 CBOR_UINT | 2,
   3041                 // COSE EC2 x.
   3042                 CBOR_NEG | 1,
   3043                 CBOR_BYTES | 24,
   3044                 // Length is 48.
   3045                 48,
   3046                 // X-coordinate. This will be overwritten later.
   3047                 0,
   3048                 0,
   3049                 0,
   3050                 0,
   3051                 0,
   3052                 0,
   3053                 0,
   3054                 0,
   3055                 0,
   3056                 0,
   3057                 0,
   3058                 0,
   3059                 0,
   3060                 0,
   3061                 0,
   3062                 0,
   3063                 0,
   3064                 0,
   3065                 0,
   3066                 0,
   3067                 0,
   3068                 0,
   3069                 0,
   3070                 0,
   3071                 0,
   3072                 0,
   3073                 0,
   3074                 0,
   3075                 0,
   3076                 0,
   3077                 0,
   3078                 0,
   3079                 0,
   3080                 0,
   3081                 0,
   3082                 0,
   3083                 0,
   3084                 0,
   3085                 0,
   3086                 0,
   3087                 0,
   3088                 0,
   3089                 0,
   3090                 0,
   3091                 0,
   3092                 0,
   3093                 0,
   3094                 0,
   3095                 // COSE EC2 y.
   3096                 CBOR_NEG | 2,
   3097                 CBOR_BYTES | 24,
   3098                 // Length is 48.
   3099                 48,
   3100                 // Y-coordinate. This will be overwritten later.
   3101                 0,
   3102                 0,
   3103                 0,
   3104                 0,
   3105                 0,
   3106                 0,
   3107                 0,
   3108                 0,
   3109                 0,
   3110                 0,
   3111                 0,
   3112                 0,
   3113                 0,
   3114                 0,
   3115                 0,
   3116                 0,
   3117                 0,
   3118                 0,
   3119                 0,
   3120                 0,
   3121                 0,
   3122                 0,
   3123                 0,
   3124                 0,
   3125                 0,
   3126                 0,
   3127                 0,
   3128                 0,
   3129                 0,
   3130                 0,
   3131                 0,
   3132                 0,
   3133                 0,
   3134                 0,
   3135                 0,
   3136                 0,
   3137                 0,
   3138                 0,
   3139                 0,
   3140                 0,
   3141                 0,
   3142                 0,
   3143                 0,
   3144                 0,
   3145                 0,
   3146                 0,
   3147                 0,
   3148                 0,
   3149             ]
   3150             .as_slice(),
   3151         );
   3152         attestation_object[30..62]
   3153             .copy_from_slice(Sha256::digest(rp_id.as_ref().as_bytes()).as_slice());
   3154         let p384_key = P384Key::from_bytes(
   3155             &[
   3156                 158, 99, 156, 49, 190, 211, 85, 167, 28, 2, 80, 57, 31, 22, 17, 38, 85, 78, 232,
   3157                 42, 45, 199, 154, 243, 136, 251, 84, 34, 5, 120, 208, 91, 61, 248, 64, 144, 87, 1,
   3158                 32, 86, 220, 68, 182, 11, 105, 223, 75, 70,
   3159             ]
   3160             .into(),
   3161         )
   3162         .unwrap()
   3163         .verifying_key()
   3164         .to_encoded_point(false);
   3165         let x = p384_key.x().unwrap();
   3166         let y = p384_key.y().unwrap();
   3167         attestation_object[112..160].copy_from_slice(x);
   3168         attestation_object[163..].copy_from_slice(y);
   3169         assert!(matches!(opts.start_ceremony()?.0.verify(
   3170             &rp_id,
   3171             &Registration {
   3172                 response: AuthenticatorAttestation::new(
   3173                     client_data_json,
   3174                     attestation_object,
   3175                     AuthTransports::NONE,
   3176                 ),
   3177                 authenticator_attachment: AuthenticatorAttachment::None,
   3178                 client_extension_results: ClientExtensionsOutputs {
   3179                     cred_props: None,
   3180                     prf: None,
   3181                 },
   3182             },
   3183             &RegistrationVerificationOptions::<&str, &str>::default(),
   3184         )?.static_state.credential_public_key, UncompressedPubKey::P384(k) if k.x() == x.as_slice() && k.y() == y.as_slice()));
   3185         Ok(())
   3186     }
   3187     #[test]
   3188     #[cfg(feature = "custom")]
   3189     fn p384_auth() -> Result<(), AggErr> {
   3190         let rp_id = RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?);
   3191         let mut opts = DiscoverableCredentialRequestOptions::passkey(&rp_id);
   3192         opts.0.challenge = Challenge(0);
   3193         let client_data_json = br#"{"type":"webauthn.get","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec();
   3194         // We over-allocate by 32 bytes. See [`AuthenticatorAssertion::new`] for more information.
   3195         let mut authenticator_data = Vec::with_capacity(69);
   3196         authenticator_data.extend_from_slice(
   3197             [
   3198                 // rpIdHash.
   3199                 // This will be overwritten later.
   3200                 0,
   3201                 0,
   3202                 0,
   3203                 0,
   3204                 0,
   3205                 0,
   3206                 0,
   3207                 0,
   3208                 0,
   3209                 0,
   3210                 0,
   3211                 0,
   3212                 0,
   3213                 0,
   3214                 0,
   3215                 0,
   3216                 0,
   3217                 0,
   3218                 0,
   3219                 0,
   3220                 0,
   3221                 0,
   3222                 0,
   3223                 0,
   3224                 0,
   3225                 0,
   3226                 0,
   3227                 0,
   3228                 0,
   3229                 0,
   3230                 0,
   3231                 0,
   3232                 // flags.
   3233                 // UP and UV (right-to-left).
   3234                 0b0000_0101,
   3235                 // signCount.
   3236                 // 0 as 32-bit big-endian.
   3237                 0,
   3238                 0,
   3239                 0,
   3240                 0,
   3241             ]
   3242             .as_slice(),
   3243         );
   3244         authenticator_data[..32]
   3245             .copy_from_slice(Sha256::digest(rp_id.as_ref().as_bytes()).as_slice());
   3246         authenticator_data
   3247             .extend_from_slice(Sha256::digest(client_data_json.as_slice()).as_slice());
   3248         let p384_key = P384Key::from_bytes(
   3249             &[
   3250                 158, 99, 156, 49, 190, 211, 85, 167, 28, 2, 80, 57, 31, 22, 17, 38, 85, 78, 232,
   3251                 42, 45, 199, 154, 243, 136, 251, 84, 34, 5, 120, 208, 91, 61, 248, 64, 144, 87, 1,
   3252                 32, 86, 220, 68, 182, 11, 105, 223, 75, 70,
   3253             ]
   3254             .into(),
   3255         )
   3256         .unwrap();
   3257         let der_sig: P384DerSig = p384_key.sign(authenticator_data.as_slice());
   3258         let pub_key = p384_key.verifying_key().to_encoded_point(true);
   3259         authenticator_data.truncate(37);
   3260         assert!(!opts.start_ceremony()?.0.verify(
   3261             &rp_id,
   3262             &DiscoverableAuthentication {
   3263                 raw_id: CredentialId::try_from(vec![0; 16])?,
   3264                 response: DiscoverableAuthenticatorAssertion::new(
   3265                     client_data_json,
   3266                     authenticator_data,
   3267                     der_sig.as_bytes().into(),
   3268                     UserHandle::from([0]),
   3269                 ),
   3270                 authenticator_attachment: AuthenticatorAttachment::None,
   3271             },
   3272             &mut AuthenticatedCredential::new(
   3273                 CredentialId::try_from([0; 16].as_slice())?,
   3274                 &UserHandle::from([0]),
   3275                 StaticState {
   3276                     credential_public_key: CompressedPubKey::<&[u8], &[u8], _, &[u8]>::P384(
   3277                         CompressedP384PubKey::from((
   3278                             (*pub_key.x().unwrap()).into(),
   3279                             pub_key.tag() == Tag::CompressedOddY
   3280                         )),
   3281                     ),
   3282                     extensions: AuthenticatorExtensionOutputStaticState {
   3283                         cred_protect: CredentialProtectionPolicy::None,
   3284                         hmac_secret: None,
   3285                     },
   3286                 },
   3287                 DynamicState {
   3288                     user_verified: true,
   3289                     backup: Backup::NotEligible,
   3290                     sign_count: 0,
   3291                     authenticator_attachment: AuthenticatorAttachment::None,
   3292                 },
   3293             )?,
   3294             &AuthenticationVerificationOptions::<&str, &str>::default(),
   3295         )?);
   3296         Ok(())
   3297     }
   3298     #[test]
   3299     #[cfg(feature = "custom")]
   3300     fn rsa_reg() -> Result<(), AggErr> {
   3301         let rp_id = RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?);
   3302         let id = UserHandle::from([0]);
   3303         let mut opts = PublicKeyCredentialCreationOptions::passkey(
   3304             &rp_id,
   3305             PublicKeyCredentialUserEntity {
   3306                 name: "foo".try_into()?,
   3307                 id: &id,
   3308                 display_name: None,
   3309             },
   3310             Vec::new(),
   3311         );
   3312         opts.challenge = Challenge(0);
   3313         let client_data_json = br#"{"type":"webauthn.create","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec();
   3314         // We over-allocate by 32 bytes. See [`AuthenticatorAttestation::new`] for more information.
   3315         let mut attestation_object = Vec::with_capacity(406);
   3316         attestation_object.extend_from_slice(
   3317             [
   3318                 CBOR_MAP | 3,
   3319                 CBOR_TEXT | 3,
   3320                 b'f',
   3321                 b'm',
   3322                 b't',
   3323                 CBOR_TEXT | 4,
   3324                 b'n',
   3325                 b'o',
   3326                 b'n',
   3327                 b'e',
   3328                 CBOR_TEXT | 7,
   3329                 b'a',
   3330                 b't',
   3331                 b't',
   3332                 b'S',
   3333                 b't',
   3334                 b'm',
   3335                 b't',
   3336                 CBOR_MAP,
   3337                 CBOR_TEXT | 8,
   3338                 b'a',
   3339                 b'u',
   3340                 b't',
   3341                 b'h',
   3342                 b'D',
   3343                 b'a',
   3344                 b't',
   3345                 b'a',
   3346                 CBOR_BYTES | 25,
   3347                 // Length is 343 as 16-bit big-endian.
   3348                 1,
   3349                 87,
   3350                 // RP ID HASH.
   3351                 // This will be overwritten later.
   3352                 0,
   3353                 0,
   3354                 0,
   3355                 0,
   3356                 0,
   3357                 0,
   3358                 0,
   3359                 0,
   3360                 0,
   3361                 0,
   3362                 0,
   3363                 0,
   3364                 0,
   3365                 0,
   3366                 0,
   3367                 0,
   3368                 0,
   3369                 0,
   3370                 0,
   3371                 0,
   3372                 0,
   3373                 0,
   3374                 0,
   3375                 0,
   3376                 0,
   3377                 0,
   3378                 0,
   3379                 0,
   3380                 0,
   3381                 0,
   3382                 0,
   3383                 0,
   3384                 // FLAGS.
   3385                 // UP, UV, and AT (right-to-left).
   3386                 0b0100_0101,
   3387                 // COUNTER.
   3388                 // 0 as 32-bit big-endian.
   3389                 0,
   3390                 0,
   3391                 0,
   3392                 0,
   3393                 // AAGUID.
   3394                 0,
   3395                 0,
   3396                 0,
   3397                 0,
   3398                 0,
   3399                 0,
   3400                 0,
   3401                 0,
   3402                 0,
   3403                 0,
   3404                 0,
   3405                 0,
   3406                 0,
   3407                 0,
   3408                 0,
   3409                 0,
   3410                 // L.
   3411                 // CREDENTIAL ID length is 16 as 16-bit big endian.
   3412                 0,
   3413                 16,
   3414                 // CREDENTIAL ID.
   3415                 0,
   3416                 0,
   3417                 0,
   3418                 0,
   3419                 0,
   3420                 0,
   3421                 0,
   3422                 0,
   3423                 0,
   3424                 0,
   3425                 0,
   3426                 0,
   3427                 0,
   3428                 0,
   3429                 0,
   3430                 0,
   3431                 CBOR_MAP | 4,
   3432                 // COSE kty.
   3433                 CBOR_UINT | 1,
   3434                 // COSE RSA.
   3435                 CBOR_UINT | 3,
   3436                 // COSE alg.
   3437                 CBOR_UINT | 3,
   3438                 CBOR_NEG | 25,
   3439                 // COSE RS256.
   3440                 1,
   3441                 0,
   3442                 // COSE n.
   3443                 CBOR_NEG,
   3444                 CBOR_BYTES | 25,
   3445                 // Length is 256 as 16-bit big-endian.
   3446                 1,
   3447                 0,
   3448                 // N. This will be overwritten later.
   3449                 0,
   3450                 0,
   3451                 0,
   3452                 0,
   3453                 0,
   3454                 0,
   3455                 0,
   3456                 0,
   3457                 0,
   3458                 0,
   3459                 0,
   3460                 0,
   3461                 0,
   3462                 0,
   3463                 0,
   3464                 0,
   3465                 0,
   3466                 0,
   3467                 0,
   3468                 0,
   3469                 0,
   3470                 0,
   3471                 0,
   3472                 0,
   3473                 0,
   3474                 0,
   3475                 0,
   3476                 0,
   3477                 0,
   3478                 0,
   3479                 0,
   3480                 0,
   3481                 0,
   3482                 0,
   3483                 0,
   3484                 0,
   3485                 0,
   3486                 0,
   3487                 0,
   3488                 0,
   3489                 0,
   3490                 0,
   3491                 0,
   3492                 0,
   3493                 0,
   3494                 0,
   3495                 0,
   3496                 0,
   3497                 0,
   3498                 0,
   3499                 0,
   3500                 0,
   3501                 0,
   3502                 0,
   3503                 0,
   3504                 0,
   3505                 0,
   3506                 0,
   3507                 0,
   3508                 0,
   3509                 0,
   3510                 0,
   3511                 0,
   3512                 0,
   3513                 0,
   3514                 0,
   3515                 0,
   3516                 0,
   3517                 0,
   3518                 0,
   3519                 0,
   3520                 0,
   3521                 0,
   3522                 0,
   3523                 0,
   3524                 0,
   3525                 0,
   3526                 0,
   3527                 0,
   3528                 0,
   3529                 0,
   3530                 0,
   3531                 0,
   3532                 0,
   3533                 0,
   3534                 0,
   3535                 0,
   3536                 0,
   3537                 0,
   3538                 0,
   3539                 0,
   3540                 0,
   3541                 0,
   3542                 0,
   3543                 0,
   3544                 0,
   3545                 0,
   3546                 0,
   3547                 0,
   3548                 0,
   3549                 0,
   3550                 0,
   3551                 0,
   3552                 0,
   3553                 0,
   3554                 0,
   3555                 0,
   3556                 0,
   3557                 0,
   3558                 0,
   3559                 0,
   3560                 0,
   3561                 0,
   3562                 0,
   3563                 0,
   3564                 0,
   3565                 0,
   3566                 0,
   3567                 0,
   3568                 0,
   3569                 0,
   3570                 0,
   3571                 0,
   3572                 0,
   3573                 0,
   3574                 0,
   3575                 0,
   3576                 0,
   3577                 0,
   3578                 0,
   3579                 0,
   3580                 0,
   3581                 0,
   3582                 0,
   3583                 0,
   3584                 0,
   3585                 0,
   3586                 0,
   3587                 0,
   3588                 0,
   3589                 0,
   3590                 0,
   3591                 0,
   3592                 0,
   3593                 0,
   3594                 0,
   3595                 0,
   3596                 0,
   3597                 0,
   3598                 0,
   3599                 0,
   3600                 0,
   3601                 0,
   3602                 0,
   3603                 0,
   3604                 0,
   3605                 0,
   3606                 0,
   3607                 0,
   3608                 0,
   3609                 0,
   3610                 0,
   3611                 0,
   3612                 0,
   3613                 0,
   3614                 0,
   3615                 0,
   3616                 0,
   3617                 0,
   3618                 0,
   3619                 0,
   3620                 0,
   3621                 0,
   3622                 0,
   3623                 0,
   3624                 0,
   3625                 0,
   3626                 0,
   3627                 0,
   3628                 0,
   3629                 0,
   3630                 0,
   3631                 0,
   3632                 0,
   3633                 0,
   3634                 0,
   3635                 0,
   3636                 0,
   3637                 0,
   3638                 0,
   3639                 0,
   3640                 0,
   3641                 0,
   3642                 0,
   3643                 0,
   3644                 0,
   3645                 0,
   3646                 0,
   3647                 0,
   3648                 0,
   3649                 0,
   3650                 0,
   3651                 0,
   3652                 0,
   3653                 0,
   3654                 0,
   3655                 0,
   3656                 0,
   3657                 0,
   3658                 0,
   3659                 0,
   3660                 0,
   3661                 0,
   3662                 0,
   3663                 0,
   3664                 0,
   3665                 0,
   3666                 0,
   3667                 0,
   3668                 0,
   3669                 0,
   3670                 0,
   3671                 0,
   3672                 0,
   3673                 0,
   3674                 0,
   3675                 0,
   3676                 0,
   3677                 0,
   3678                 0,
   3679                 0,
   3680                 0,
   3681                 0,
   3682                 0,
   3683                 0,
   3684                 0,
   3685                 0,
   3686                 0,
   3687                 0,
   3688                 0,
   3689                 0,
   3690                 0,
   3691                 0,
   3692                 0,
   3693                 0,
   3694                 0,
   3695                 0,
   3696                 0,
   3697                 0,
   3698                 0,
   3699                 0,
   3700                 0,
   3701                 0,
   3702                 0,
   3703                 0,
   3704                 0,
   3705                 // COSE e.
   3706                 CBOR_NEG | 1,
   3707                 CBOR_BYTES | 3,
   3708                 // 65537 as 24-bit big-endian.
   3709                 1,
   3710                 0,
   3711                 1,
   3712             ]
   3713             .as_slice(),
   3714         );
   3715         attestation_object[31..63]
   3716             .copy_from_slice(Sha256::digest(rp_id.as_ref().as_bytes()).as_slice());
   3717         let n = [
   3718             111, 183, 124, 133, 38, 167, 70, 148, 44, 50, 30, 60, 121, 14, 38, 37, 96, 114, 107,
   3719             195, 248, 64, 79, 36, 237, 140, 43, 27, 94, 74, 102, 152, 135, 102, 184, 150, 186, 206,
   3720             185, 19, 165, 209, 48, 98, 98, 9, 3, 205, 208, 82, 250, 105, 132, 201, 73, 62, 60, 165,
   3721             100, 128, 153, 9, 41, 118, 66, 95, 236, 214, 73, 135, 197, 68, 184, 10, 27, 116, 204,
   3722             145, 50, 174, 58, 42, 183, 181, 119, 232, 126, 252, 217, 96, 162, 190, 103, 122, 64,
   3723             87, 145, 45, 32, 207, 17, 239, 223, 3, 35, 14, 112, 119, 124, 141, 123, 208, 239, 105,
   3724             81, 217, 151, 162, 190, 17, 88, 182, 176, 158, 81, 200, 42, 166, 133, 48, 23, 236, 55,
   3725             117, 248, 233, 151, 203, 122, 155, 231, 46, 177, 20, 20, 151, 64, 222, 239, 226, 7, 21,
   3726             254, 81, 202, 64, 232, 161, 235, 22, 51, 246, 207, 213, 0, 229, 138, 46, 222, 205, 157,
   3727             108, 139, 253, 230, 80, 50, 2, 122, 212, 163, 100, 180, 114, 12, 113, 52, 56, 99, 188,
   3728             42, 198, 212, 23, 182, 222, 56, 221, 200, 79, 96, 239, 221, 135, 10, 17, 106, 183, 56,
   3729             104, 68, 94, 198, 196, 35, 200, 83, 204, 26, 185, 204, 212, 31, 183, 19, 111, 233, 13,
   3730             72, 93, 53, 65, 111, 59, 242, 122, 160, 244, 162, 126, 38, 235, 156, 47, 88, 39, 132,
   3731             153, 79, 0, 133, 78, 7, 218, 165, 241,
   3732         ];
   3733         let e = 65537;
   3734         let d = [
   3735             145, 79, 21, 97, 233, 3, 192, 194, 177, 68, 181, 80, 120, 197, 23, 44, 185, 74, 144, 0,
   3736             132, 149, 139, 11, 16, 224, 4, 112, 236, 94, 238, 97, 121, 124, 213, 145, 24, 253, 168,
   3737             35, 190, 205, 132, 115, 33, 201, 38, 253, 246, 180, 66, 155, 165, 46, 3, 254, 68, 108,
   3738             154, 247, 246, 45, 187, 0, 204, 96, 185, 157, 249, 174, 158, 38, 62, 244, 183, 76, 102,
   3739             6, 219, 92, 212, 138, 59, 147, 163, 219, 111, 39, 105, 21, 236, 196, 38, 255, 114, 247,
   3740             82, 104, 113, 204, 29, 152, 209, 219, 48, 239, 74, 129, 19, 247, 33, 239, 119, 166,
   3741             216, 152, 94, 138, 238, 164, 242, 129, 50, 150, 57, 20, 53, 224, 56, 241, 138, 97, 111,
   3742             215, 107, 212, 195, 146, 108, 143, 0, 229, 181, 171, 73, 152, 105, 146, 25, 243, 242,
   3743             140, 252, 248, 162, 247, 63, 168, 180, 20, 153, 120, 10, 248, 211, 1, 71, 127, 212,
   3744             249, 237, 203, 202, 48, 26, 216, 226, 228, 186, 13, 204, 70, 255, 240, 89, 255, 59, 83,
   3745             31, 253, 55, 43, 158, 90, 248, 83, 32, 159, 105, 57, 134, 34, 96, 18, 255, 245, 153,
   3746             162, 60, 91, 99, 220, 51, 44, 85, 114, 67, 125, 202, 65, 217, 245, 40, 8, 81, 165, 142,
   3747             24, 245, 127, 122, 247, 152, 212, 75, 45, 59, 90, 184, 234, 31, 147, 36, 8, 212, 45,
   3748             50, 23, 3, 25, 253, 87, 227, 79, 119, 161,
   3749         ];
   3750         let p = BigUint::from_slice(
   3751             [
   3752                 352691927, 1294578443, 816143558, 690659917, 1161596366, 1544791087, 3999549486,
   3753                 3319149924, 2349250979, 1304689381, 3959753736, 3377900978, 866506027, 1671521644,
   3754                 3926847564, 898221388, 3448219846, 494454484, 3915534864, 2869735916, 2456511629,
   3755                 3397234721, 3012775852, 3472309790, 1923617705, 2993441050, 3210302569, 3605331368,
   3756                 3352563766, 688081007, 4104512503, 4145593376,
   3757             ]
   3758             .as_slice(),
   3759         );
   3760         let p_2 = BigUint::from_slice(
   3761             [
   3762                 4039514409, 964284038, 3230008587, 3320139220, 3562360334, 3165876926, 212773653,
   3763                 2752465512, 2973674888, 1717425549, 2084262803, 3585031058, 4162394935, 1428626842,
   3764                 1015474994, 3283774155, 2840050110, 190639246, 147241978, 2994256073, 4081014755,
   3765                 3102401369, 3547397148, 1545029057, 895305733, 2689179461, 1593439337, 3960057302,
   3766                 193068804, 2835123424, 4054880057, 4200258364,
   3767             ]
   3768             .as_slice(),
   3769         );
   3770         let rsa_key = RsaKey::<Sha256>::new(
   3771             RsaPrivateKey::from_components(
   3772                 BigUint::from_bytes_le(n.as_slice()),
   3773                 e.into(),
   3774                 BigUint::from_bytes_le(d.as_slice()),
   3775                 vec![p, p_2],
   3776             )
   3777             .unwrap(),
   3778         )
   3779         .verifying_key();
   3780         let n = rsa_key.as_ref().n().to_bytes_be();
   3781         attestation_object[113..369].copy_from_slice(n.as_slice());
   3782         assert!(matches!(opts.start_ceremony()?.0.verify(
   3783             &rp_id,
   3784             &Registration {
   3785                 response: AuthenticatorAttestation::new(
   3786                     client_data_json,
   3787                     attestation_object,
   3788                     AuthTransports::NONE,
   3789                 ),
   3790                 authenticator_attachment: AuthenticatorAttachment::None,
   3791                 client_extension_results: ClientExtensionsOutputs {
   3792                     cred_props: None,
   3793                     prf: None,
   3794                 },
   3795             },
   3796             &RegistrationVerificationOptions::<&str, &str>::default(),
   3797         )?.static_state.credential_public_key, UncompressedPubKey::Rsa(k) if *k.n() == n.as_slice() && k.e() == e));
   3798         Ok(())
   3799     }
   3800     #[test]
   3801     #[cfg(feature = "custom")]
   3802     fn rsa_auth() -> Result<(), AggErr> {
   3803         let rp_id = RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?);
   3804         let mut opts = DiscoverableCredentialRequestOptions::passkey(&rp_id);
   3805         opts.0.challenge = Challenge(0);
   3806         let client_data_json = br#"{"type":"webauthn.get","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec();
   3807         // We over-allocate by 32 bytes. See [`AuthenticatorAssertion::new`] for more information.
   3808         let mut authenticator_data = Vec::with_capacity(69);
   3809         authenticator_data.extend_from_slice(
   3810             [
   3811                 // rpIdHash.
   3812                 // This will be overwritten later.
   3813                 0,
   3814                 0,
   3815                 0,
   3816                 0,
   3817                 0,
   3818                 0,
   3819                 0,
   3820                 0,
   3821                 0,
   3822                 0,
   3823                 0,
   3824                 0,
   3825                 0,
   3826                 0,
   3827                 0,
   3828                 0,
   3829                 0,
   3830                 0,
   3831                 0,
   3832                 0,
   3833                 0,
   3834                 0,
   3835                 0,
   3836                 0,
   3837                 0,
   3838                 0,
   3839                 0,
   3840                 0,
   3841                 0,
   3842                 0,
   3843                 0,
   3844                 0,
   3845                 // flags.
   3846                 // UP and UV (right-to-left).
   3847                 0b0000_0101,
   3848                 // signCount.
   3849                 // 0 as 32-bit big-endian.
   3850                 0,
   3851                 0,
   3852                 0,
   3853                 0,
   3854             ]
   3855             .as_slice(),
   3856         );
   3857         authenticator_data[..32]
   3858             .copy_from_slice(Sha256::digest(rp_id.as_ref().as_bytes()).as_slice());
   3859         authenticator_data
   3860             .extend_from_slice(Sha256::digest(client_data_json.as_slice()).as_slice());
   3861         let n = [
   3862             111, 183, 124, 133, 38, 167, 70, 148, 44, 50, 30, 60, 121, 14, 38, 37, 96, 114, 107,
   3863             195, 248, 64, 79, 36, 237, 140, 43, 27, 94, 74, 102, 152, 135, 102, 184, 150, 186, 206,
   3864             185, 19, 165, 209, 48, 98, 98, 9, 3, 205, 208, 82, 250, 105, 132, 201, 73, 62, 60, 165,
   3865             100, 128, 153, 9, 41, 118, 66, 95, 236, 214, 73, 135, 197, 68, 184, 10, 27, 116, 204,
   3866             145, 50, 174, 58, 42, 183, 181, 119, 232, 126, 252, 217, 96, 162, 190, 103, 122, 64,
   3867             87, 145, 45, 32, 207, 17, 239, 223, 3, 35, 14, 112, 119, 124, 141, 123, 208, 239, 105,
   3868             81, 217, 151, 162, 190, 17, 88, 182, 176, 158, 81, 200, 42, 166, 133, 48, 23, 236, 55,
   3869             117, 248, 233, 151, 203, 122, 155, 231, 46, 177, 20, 20, 151, 64, 222, 239, 226, 7, 21,
   3870             254, 81, 202, 64, 232, 161, 235, 22, 51, 246, 207, 213, 0, 229, 138, 46, 222, 205, 157,
   3871             108, 139, 253, 230, 80, 50, 2, 122, 212, 163, 100, 180, 114, 12, 113, 52, 56, 99, 188,
   3872             42, 198, 212, 23, 182, 222, 56, 221, 200, 79, 96, 239, 221, 135, 10, 17, 106, 183, 56,
   3873             104, 68, 94, 198, 196, 35, 200, 83, 204, 26, 185, 204, 212, 31, 183, 19, 111, 233, 13,
   3874             72, 93, 53, 65, 111, 59, 242, 122, 160, 244, 162, 126, 38, 235, 156, 47, 88, 39, 132,
   3875             153, 79, 0, 133, 78, 7, 218, 165, 241,
   3876         ];
   3877         let e = 65537;
   3878         let d = [
   3879             145, 79, 21, 97, 233, 3, 192, 194, 177, 68, 181, 80, 120, 197, 23, 44, 185, 74, 144, 0,
   3880             132, 149, 139, 11, 16, 224, 4, 112, 236, 94, 238, 97, 121, 124, 213, 145, 24, 253, 168,
   3881             35, 190, 205, 132, 115, 33, 201, 38, 253, 246, 180, 66, 155, 165, 46, 3, 254, 68, 108,
   3882             154, 247, 246, 45, 187, 0, 204, 96, 185, 157, 249, 174, 158, 38, 62, 244, 183, 76, 102,
   3883             6, 219, 92, 212, 138, 59, 147, 163, 219, 111, 39, 105, 21, 236, 196, 38, 255, 114, 247,
   3884             82, 104, 113, 204, 29, 152, 209, 219, 48, 239, 74, 129, 19, 247, 33, 239, 119, 166,
   3885             216, 152, 94, 138, 238, 164, 242, 129, 50, 150, 57, 20, 53, 224, 56, 241, 138, 97, 111,
   3886             215, 107, 212, 195, 146, 108, 143, 0, 229, 181, 171, 73, 152, 105, 146, 25, 243, 242,
   3887             140, 252, 248, 162, 247, 63, 168, 180, 20, 153, 120, 10, 248, 211, 1, 71, 127, 212,
   3888             249, 237, 203, 202, 48, 26, 216, 226, 228, 186, 13, 204, 70, 255, 240, 89, 255, 59, 83,
   3889             31, 253, 55, 43, 158, 90, 248, 83, 32, 159, 105, 57, 134, 34, 96, 18, 255, 245, 153,
   3890             162, 60, 91, 99, 220, 51, 44, 85, 114, 67, 125, 202, 65, 217, 245, 40, 8, 81, 165, 142,
   3891             24, 245, 127, 122, 247, 152, 212, 75, 45, 59, 90, 184, 234, 31, 147, 36, 8, 212, 45,
   3892             50, 23, 3, 25, 253, 87, 227, 79, 119, 161,
   3893         ];
   3894         let p = BigUint::from_slice(
   3895             [
   3896                 352691927, 1294578443, 816143558, 690659917, 1161596366, 1544791087, 3999549486,
   3897                 3319149924, 2349250979, 1304689381, 3959753736, 3377900978, 866506027, 1671521644,
   3898                 3926847564, 898221388, 3448219846, 494454484, 3915534864, 2869735916, 2456511629,
   3899                 3397234721, 3012775852, 3472309790, 1923617705, 2993441050, 3210302569, 3605331368,
   3900                 3352563766, 688081007, 4104512503, 4145593376,
   3901             ]
   3902             .as_slice(),
   3903         );
   3904         let p_2 = BigUint::from_slice(
   3905             [
   3906                 4039514409, 964284038, 3230008587, 3320139220, 3562360334, 3165876926, 212773653,
   3907                 2752465512, 2973674888, 1717425549, 2084262803, 3585031058, 4162394935, 1428626842,
   3908                 1015474994, 3283774155, 2840050110, 190639246, 147241978, 2994256073, 4081014755,
   3909                 3102401369, 3547397148, 1545029057, 895305733, 2689179461, 1593439337, 3960057302,
   3910                 193068804, 2835123424, 4054880057, 4200258364,
   3911             ]
   3912             .as_slice(),
   3913         );
   3914         let rsa_key = RsaKey::<Sha256>::new(
   3915             RsaPrivateKey::from_components(
   3916                 BigUint::from_bytes_le(n.as_slice()),
   3917                 e.into(),
   3918                 BigUint::from_bytes_le(d.as_slice()),
   3919                 vec![p, p_2],
   3920             )
   3921             .unwrap(),
   3922         );
   3923         let rsa_pub = rsa_key.verifying_key();
   3924         let sig = rsa_key.sign(authenticator_data.as_slice()).to_vec();
   3925         authenticator_data.truncate(37);
   3926         assert!(!opts.start_ceremony()?.0.verify(
   3927             &rp_id,
   3928             &DiscoverableAuthentication {
   3929                 raw_id: CredentialId::try_from(vec![0; 16])?,
   3930                 response: DiscoverableAuthenticatorAssertion::new(
   3931                     client_data_json,
   3932                     authenticator_data,
   3933                     sig,
   3934                     UserHandle::from([0]),
   3935                 ),
   3936                 authenticator_attachment: AuthenticatorAttachment::None,
   3937             },
   3938             &mut AuthenticatedCredential::new(
   3939                 CredentialId::try_from([0; 16].as_slice())?,
   3940                 &UserHandle::from([0]),
   3941                 StaticState {
   3942                     credential_public_key: CompressedPubKey::<&[u8], &[u8], &[u8], _>::Rsa(
   3943                         RsaPubKey::try_from((rsa_pub.as_ref().n().to_bytes_be(), e)).unwrap(),
   3944                     ),
   3945                     extensions: AuthenticatorExtensionOutputStaticState {
   3946                         cred_protect: CredentialProtectionPolicy::None,
   3947                         hmac_secret: None,
   3948                     },
   3949                 },
   3950                 DynamicState {
   3951                     user_verified: true,
   3952                     backup: Backup::NotEligible,
   3953                     sign_count: 0,
   3954                     authenticator_attachment: AuthenticatorAttachment::None,
   3955                 },
   3956             )?,
   3957             &AuthenticationVerificationOptions::<&str, &str>::default(),
   3958         )?);
   3959         Ok(())
   3960     }
   3961 }