webauthn_rp

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

request.rs (141871B)


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