webauthn_rp

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

request.rs (138390B)


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