webauthn_rp

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

ser.rs (130827B)


      1 #[cfg(test)]
      2 mod tests;
      3 use super::{
      4     super::{
      5         super::response::ser::{Null, Type},
      6         auth::PrfInputOwned,
      7         ser::PrfHelper,
      8     },
      9     AuthenticatorAttachment, AuthenticatorSelectionCriteria, Challenge, CoseAlgorithmIdentifier,
     10     CoseAlgorithmIdentifiers, CredProtect, CredentialCreationOptions,
     11     CredentialMediationRequirement, Extension, ExtensionInfo, ExtensionReq, FIVE_MINUTES,
     12     FourToSixtyThree, Hints, PrfInput, PublicKeyCredentialCreationOptions,
     13     PublicKeyCredentialDescriptor, PublicKeyCredentialUserEntity, RegistrationClientState,
     14     ResidentKeyRequirement, RpId, UserHandle, UserVerificationRequirement,
     15 };
     16 #[cfg(doc)]
     17 use core::str::FromStr;
     18 use core::{
     19     convert,
     20     error::Error as E,
     21     fmt::{self, Display, Formatter},
     22     num::NonZeroU32,
     23     str,
     24 };
     25 use serde::{
     26     de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Unexpected, Visitor},
     27     ser::{Serialize, SerializeSeq as _, SerializeStruct as _, Serializer},
     28 };
     29 /// `"type"`
     30 const TYPE: &str = "type";
     31 /// `"public-key"`
     32 const PUBLIC_KEY: &str = "public-key";
     33 /// `"alg"`
     34 const ALG: &str = "alg";
     35 /// [EdDSA](https://www.iana.org/assignments/cose/cose.xhtml#algorithms)
     36 const EDDSA: i16 = -8i16;
     37 /// [ES256](https://www.iana.org/assignments/cose/cose.xhtml#algorithms)
     38 const ES256: i16 = -7i16;
     39 /// [ES384](https://www.iana.org/assignments/cose/cose.xhtml#algorithms)
     40 const ES384: i16 = -35i16;
     41 /// [ML-DSA-44](https://www.iana.org/assignments/cose/cose.xhtml#algorithms)
     42 const MLDSA44: i16 = -48i16;
     43 /// [ML-DSA-65](https://www.iana.org/assignments/cose/cose.xhtml#algorithms)
     44 const MLDSA65: i16 = -49i16;
     45 /// [ML-DSA-87](https://www.iana.org/assignments/cose/cose.xhtml#algorithms)
     46 const MLDSA87: i16 = -50i16;
     47 /// [RS256](https://www.iana.org/assignments/cose/cose.xhtml#algorithms)
     48 const RS256: i16 = -257i16;
     49 impl Serialize for CoseAlgorithmIdentifier {
     50     /// Serializes `self` into a `struct` based on
     51     /// [`PublicKeyCredentialParameters`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialparameters).
     52     #[inline]
     53     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     54     where
     55         S: Serializer,
     56     {
     57         serializer
     58             .serialize_struct("PublicKeyCredentialParameters", 2)
     59             .and_then(|mut ser| {
     60                 ser.serialize_field(TYPE, PUBLIC_KEY).and_then(|()| {
     61                     ser.serialize_field(
     62                         ALG,
     63                         &match *self {
     64                             Self::Mldsa87 => MLDSA87,
     65                             Self::Mldsa65 => MLDSA65,
     66                             Self::Mldsa44 => MLDSA44,
     67                             Self::Eddsa => EDDSA,
     68                             Self::Es256 => ES256,
     69                             Self::Es384 => ES384,
     70                             Self::Rs256 => RS256,
     71                         },
     72                     )
     73                     .and_then(|()| ser.end())
     74                 })
     75             })
     76     }
     77 }
     78 impl Serialize for CoseAlgorithmIdentifiers {
     79     /// Serializes `self` to conform with
     80     /// [`pubKeyCredParams`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptionsjson-pubkeycredparams).
     81     ///
     82     /// # Examples
     83     ///
     84     /// ```
     85     /// # use webauthn_rp::request::register::{CoseAlgorithmIdentifier,CoseAlgorithmIdentifiers};
     86     /// assert_eq!(
     87     ///     serde_json::to_string(&CoseAlgorithmIdentifiers::ALL)?,
     88     ///     r#"[{"type":"public-key","alg":-50},{"type":"public-key","alg":-49},{"type":"public-key","alg":-48},{"type":"public-key","alg":-8},{"type":"public-key","alg":-7},{"type":"public-key","alg":-35},{"type":"public-key","alg":-257}]"#
     89     /// );
     90     /// assert_eq!(
     91     ///     serde_json::to_string(&CoseAlgorithmIdentifiers::default().remove(CoseAlgorithmIdentifier::Es384))?,
     92     ///     r#"[{"type":"public-key","alg":-50},{"type":"public-key","alg":-49},{"type":"public-key","alg":-48},{"type":"public-key","alg":-8},{"type":"public-key","alg":-7},{"type":"public-key","alg":-257}]"#
     93     /// );
     94     /// # Ok::<_, serde_json::Error>(())
     95     /// ```
     96     #[expect(
     97         clippy::arithmetic_side_effects,
     98         reason = "comment justifies correctness"
     99     )]
    100     #[inline]
    101     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    102     where
    103         S: Serializer,
    104     {
    105         // At most we add `1` seven times which clearly cannot overflow or `usize`.
    106         serializer
    107             .serialize_seq(Some(
    108                 usize::from(self.contains(CoseAlgorithmIdentifier::Mldsa87))
    109                     + usize::from(self.contains(CoseAlgorithmIdentifier::Mldsa65))
    110                     + usize::from(self.contains(CoseAlgorithmIdentifier::Mldsa44))
    111                     + usize::from(self.contains(CoseAlgorithmIdentifier::Eddsa))
    112                     + usize::from(self.contains(CoseAlgorithmIdentifier::Es256))
    113                     + usize::from(self.contains(CoseAlgorithmIdentifier::Es384))
    114                     + usize::from(self.contains(CoseAlgorithmIdentifier::Es384)),
    115             ))
    116             .and_then(|mut ser| {
    117                 if self.contains(CoseAlgorithmIdentifier::Mldsa87) {
    118                     ser.serialize_element(&CoseAlgorithmIdentifier::Mldsa87)
    119                 } else {
    120                     Ok(())
    121                 }
    122                 .and_then(|()| {
    123                     if self.contains(CoseAlgorithmIdentifier::Mldsa65) {
    124                         ser.serialize_element(&CoseAlgorithmIdentifier::Mldsa65)
    125                     } else {
    126                         Ok(())
    127                     }
    128                     .and_then(|()| {
    129                         if self.contains(CoseAlgorithmIdentifier::Mldsa44) {
    130                             ser.serialize_element(&CoseAlgorithmIdentifier::Mldsa44)
    131                         } else {
    132                             Ok(())
    133                         }
    134                         .and_then(|()| {
    135                             if self.contains(CoseAlgorithmIdentifier::Eddsa) {
    136                                 ser.serialize_element(&CoseAlgorithmIdentifier::Eddsa)
    137                             } else {
    138                                 Ok(())
    139                             }
    140                             .and_then(|()| {
    141                                 if self.contains(CoseAlgorithmIdentifier::Es256) {
    142                                     ser.serialize_element(&CoseAlgorithmIdentifier::Es256)
    143                                 } else {
    144                                     Ok(())
    145                                 }
    146                                 .and_then(|()| {
    147                                     if self.contains(CoseAlgorithmIdentifier::Es384) {
    148                                         ser.serialize_element(&CoseAlgorithmIdentifier::Es384)
    149                                     } else {
    150                                         Ok(())
    151                                     }
    152                                     .and_then(|()| {
    153                                         if self.contains(CoseAlgorithmIdentifier::Rs256) {
    154                                             ser.serialize_element(&CoseAlgorithmIdentifier::Rs256)
    155                                         } else {
    156                                             Ok(())
    157                                         }
    158                                         .and_then(|()| ser.end())
    159                                     })
    160                                 })
    161                             })
    162                         })
    163                     })
    164                 })
    165             })
    166     }
    167 }
    168 /// `"name"`.
    169 const NAME: &str = "name";
    170 /// `"id"`.
    171 const ID: &str = "id";
    172 /// `newtype` around `RpId` to be used to serialize `PublicKeyCredentialRpEntity`.
    173 struct PublicKeyCredentialRpEntity<'a>(&'a RpId);
    174 impl Serialize for PublicKeyCredentialRpEntity<'_> {
    175     /// Serializes `self` to conform with
    176     /// [`PublicKeyCredentialRpEntity`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrpentity).
    177     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    178     where
    179         S: Serializer,
    180     {
    181         serializer
    182             .serialize_struct("PublicKeyCredentialRpEntity", 2)
    183             .and_then(|mut ser| {
    184                 ser.serialize_field(NAME, self.0)
    185                     .and_then(|()| ser.serialize_field(ID, self.0).and_then(|()| ser.end()))
    186             })
    187     }
    188 }
    189 // We implement this separately from `user_serialize` for proper documentation and example purposes.
    190 impl Serialize for UserHandle<1> {
    191     /// Serializes `self` to conform with
    192     /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentityjson-id).
    193     ///
    194     /// # Examples
    195     ///
    196     /// ```
    197     /// # use webauthn_rp::request::register::UserHandle;
    198     /// # #[cfg(feature = "custom")]
    199     /// // We create this manually purely for example. One should almost always
    200     /// // randomly generate this (e.g., `UserHandle::new`).
    201     /// let id = UserHandle::from([0]);
    202     /// # #[cfg(feature = "custom")]
    203     /// assert_eq!(serde_json::to_string(&id)?, r#""AA""#);
    204     /// # Ok::<_, serde_json::Error>(())
    205     /// ```
    206     #[inline]
    207     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    208     where
    209         S: Serializer,
    210     {
    211         serializer.serialize_str(base64url_nopad::encode_buffer(
    212             self.0.as_slice(),
    213             [0; base64url_nopad::encode_len(1)].as_mut_slice(),
    214         ))
    215     }
    216 }
    217 /// Implements [`Serialize`] for [`UserHandle`] of array of length of the passed `usize` literal.
    218 ///
    219 /// Only [`USER_HANDLE_MIN_LEN`]–[`USER_HANDLE_MAX_LEN`] inclusively are allowed to be passed.
    220 macro_rules! user_serialize {
    221     ( $( $x:literal),* ) => {
    222         $(
    223 impl Serialize for UserHandle<$x> {
    224     /// See [`UserHandle::serialize`].
    225     #[inline]
    226     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    227     where
    228         S: Serializer,
    229     {
    230 
    231         serializer.serialize_str(base64url_nopad::encode_buffer(self.0.as_slice(), [0; base64url_nopad::encode_len($x)].as_mut_slice()))
    232     }
    233 }
    234         )*
    235     };
    236 }
    237 // MUST only pass `2`–[`USER_HANDLE_MAX_LEN`] inclusively.
    238 user_serialize!(
    239     2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
    240     28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
    241     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
    242 );
    243 /// `"displayName"`.
    244 const DISPLAY_NAME: &str = "displayName";
    245 impl<const LEN: usize> Serialize for PublicKeyCredentialUserEntity<'_, '_, '_, LEN>
    246 where
    247     UserHandle<LEN>: Serialize,
    248 {
    249     /// Serializes `self` to conform with
    250     /// [`PublicKeyCredentialUserEntityJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialuserentityjson).
    251     ///
    252     /// # Examples
    253     ///
    254     /// ```
    255     /// # use webauthn_rp::request::register::{PublicKeyCredentialUserEntity, UserHandle};
    256     /// # #[cfg(feature = "custom")]
    257     /// // We create this manually purely for example. One should almost always
    258     /// // randomly generate this (e.g., `UserHandle::new`).
    259     /// let id = UserHandle::from([0]);
    260     /// # #[cfg(feature = "custom")]
    261     /// assert_eq!(
    262     ///     serde_json::to_string(&PublicKeyCredentialUserEntity {
    263     ///         name: "georg.cantor",
    264     ///         id: &id,
    265     ///         display_name: "Гео́рг Ка́нтор",
    266     ///     }).unwrap(),
    267     ///     r#"{"name":"georg.cantor","id":"AA","displayName":"Гео́рг Ка́нтор"}"#
    268     /// );
    269     /// // The display name gets serialized as an empty string
    270     /// // iff `Self::display_name` is `None`.
    271     /// # #[cfg(feature = "custom")]
    272     /// assert_eq!(
    273     ///     serde_json::to_string(&PublicKeyCredentialUserEntity {
    274     ///         name: "georg.cantor",
    275     ///         id: &id,
    276     ///         display_name: "",
    277     ///     }).unwrap(),
    278     ///     r#"{"name":"georg.cantor","id":"AA","displayName":""}"#
    279     /// );
    280     /// # Ok::<_, webauthn_rp::AggErr>(())
    281     /// ```
    282     #[inline]
    283     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    284     where
    285         S: Serializer,
    286     {
    287         serializer
    288             .serialize_struct("PublicKeyCredentialUserEntity", 3)
    289             .and_then(|mut ser| {
    290                 ser.serialize_field(NAME, &self.name).and_then(|()| {
    291                     ser.serialize_field(ID, &self.id).and_then(|()| {
    292                         ser.serialize_field(DISPLAY_NAME, &self.display_name)
    293                             .and_then(|()| ser.end())
    294                     })
    295                 })
    296             })
    297     }
    298 }
    299 /// `"required"`
    300 const REQUIRED: &str = "required";
    301 /// `"discouraged"`
    302 const DISCOURAGED: &str = "discouraged";
    303 /// `"preferred"`
    304 const PREFERRED: &str = "preferred";
    305 impl Serialize for ResidentKeyRequirement {
    306     /// Serializes `self` to conform with
    307     /// [`ResidentKeyRequirement`](https://www.w3.org/TR/webauthn-3/#enumdef-residentkeyrequirement).
    308     ///
    309     /// # Examples
    310     ///
    311     /// ```
    312     /// # use webauthn_rp::request::register::ResidentKeyRequirement;
    313     /// assert_eq!(
    314     ///     serde_json::to_string(&ResidentKeyRequirement::Required)?,
    315     ///     r#""required""#
    316     /// );
    317     /// assert_eq!(
    318     ///     serde_json::to_string(&ResidentKeyRequirement::Discouraged)?,
    319     ///     r#""discouraged""#
    320     /// );
    321     /// assert_eq!(
    322     ///     serde_json::to_string(&ResidentKeyRequirement::Preferred)?,
    323     ///     r#""preferred""#
    324     /// );
    325     /// # Ok::<_, serde_json::Error>(())
    326     /// ```
    327     #[inline]
    328     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    329     where
    330         S: Serializer,
    331     {
    332         serializer.serialize_str(match *self {
    333             Self::Required => REQUIRED,
    334             Self::Discouraged => DISCOURAGED,
    335             Self::Preferred => PREFERRED,
    336         })
    337     }
    338 }
    339 /// `"platform"`.
    340 const PLATFORM: &str = "platform";
    341 /// `"cross-platform"`.
    342 const CROSS_PLATFORM: &str = "cross-platform";
    343 /// `"authenticatorAttachment"`.
    344 const AUTHENTICATOR_ATTACHMENT: &str = "authenticatorAttachment";
    345 /// `"residentKey"`.
    346 const RESIDENT_KEY: &str = "residentKey";
    347 /// `"requireResidentKey"`.
    348 const REQUIRE_RESIDENT_KEY: &str = "requireResidentKey";
    349 /// `"userVerification"`.
    350 const USER_VERIFICATION: &str = "userVerification";
    351 impl Serialize for AuthenticatorSelectionCriteria {
    352     /// Serializes `self` to conform with
    353     /// [`AuthenticatorSelectionCriteria`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticatorselectioncriteria).
    354     ///
    355     /// # Examples
    356     ///
    357     /// ```
    358     /// # use webauthn_rp::{request::register::AuthenticatorSelectionCriteria, response::AuthenticatorAttachment};
    359     /// assert_eq!(
    360     ///     serde_json::to_string(&AuthenticatorSelectionCriteria::passkey())?,
    361     ///     r#"{"residentKey":"required","requireResidentKey":true,"userVerification":"required"}"#
    362     /// );
    363     /// assert_eq!(
    364     ///     serde_json::to_string(&AuthenticatorSelectionCriteria::second_factor())?,
    365     ///     r#"{"residentKey":"discouraged","requireResidentKey":false,"userVerification":"discouraged"}"#
    366     /// );
    367     /// let mut crit = AuthenticatorSelectionCriteria::passkey();
    368     /// crit.authenticator_attachment = AuthenticatorAttachment::CrossPlatform;
    369     /// assert_eq!(
    370     ///     serde_json::to_string(&crit)?,
    371     ///     r#"{"authenticatorAttachment":"cross-platform","residentKey":"required","requireResidentKey":true,"userVerification":"required"}"#
    372     /// );
    373     /// # Ok::<_, serde_json::Error>(())
    374     /// ```
    375     #[inline]
    376     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    377     where
    378         S: Serializer,
    379     {
    380         let count = if matches!(self.authenticator_attachment, AuthenticatorAttachment::None) {
    381             3
    382         } else {
    383             4
    384         };
    385         serializer
    386             .serialize_struct("AuthenticatorSelectionCriteria", count)
    387             .and_then(|mut ser| {
    388                 if count == 3 {
    389                     Ok(())
    390                 } else {
    391                     ser.serialize_field(
    392                         AUTHENTICATOR_ATTACHMENT,
    393                         if matches!(
    394                             self.authenticator_attachment,
    395                             AuthenticatorAttachment::Platform
    396                         ) {
    397                             PLATFORM
    398                         } else {
    399                             CROSS_PLATFORM
    400                         },
    401                     )
    402                 }
    403                 .and_then(|()| {
    404                     ser.serialize_field(RESIDENT_KEY, &self.resident_key)
    405                         .and_then(|()| {
    406                             ser.serialize_field(
    407                                 REQUIRE_RESIDENT_KEY,
    408                                 &matches!(self.resident_key, ResidentKeyRequirement::Required),
    409                             )
    410                             .and_then(|()| {
    411                                 ser.serialize_field(USER_VERIFICATION, &self.user_verification)
    412                                     .and_then(|()| ser.end())
    413                             })
    414                         })
    415                 })
    416             })
    417     }
    418 }
    419 /// Helper that serializes prf registration information to conform with
    420 /// [`AuthenticationExtensionsPRFInputs`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfinputs).
    421 struct Prf<'a, 'b>(PrfInput<'a, 'b>);
    422 impl Serialize for Prf<'_, '_> {
    423     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    424     where
    425         S: Serializer,
    426     {
    427         serializer.serialize_struct("Prf", 1).and_then(|mut ser| {
    428             ser.serialize_field("eval", &self.0)
    429                 .and_then(|()| ser.end())
    430         })
    431     }
    432 }
    433 /// `credProps` key name.
    434 const CRED_PROPS: &str = "credProps";
    435 /// `minPinLength` key name.
    436 const MIN_PIN_LENGTH: &str = "minPinLength";
    437 /// `prf` key name.
    438 const PRF: &str = "prf";
    439 /// `credentialProtectionPolicy` key name.
    440 const CREDENTIAL_PROTECTION_POLICY: &str = "credentialProtectionPolicy";
    441 /// `enforceCredentialProtectionPolicy` key name.
    442 const ENFORCE_CREDENTIAL_PROTECTION_POLICY: &str = "enforceCredentialProtectionPolicy";
    443 /// `"userVerificationOptional"`.
    444 const USER_VERIFICATION_OPTIONAL: &str = "userVerificationOptional";
    445 /// `"userVerificationOptionalWithCredentialIDList"`.
    446 const USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST: &str =
    447     "userVerificationOptionalWithCredentialIDList";
    448 /// `"userVerificationRequired"`.
    449 const USER_VERIFICATION_REQUIRED: &str = "userVerificationRequired";
    450 impl Serialize for Extension<'_, '_> {
    451     /// Serializes `self` to conform with
    452     /// [`AuthenticationExtensionsClientInputsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsclientinputsjson).
    453     ///
    454     /// # Examples
    455     ///
    456     /// ```
    457     /// # use webauthn_rp::request::{
    458     /// #     register::{CredProtect, Extension, FourToSixtyThree},
    459     /// #     PrfInput, ExtensionInfo, ExtensionReq,
    460     /// # };
    461     /// assert_eq!(serde_json::to_string(&Extension::default())?, r#"{}"#);
    462     /// assert_eq!(
    463     ///     serde_json::to_string(&Extension {
    464     ///         cred_props: Some(ExtensionReq::Allow),
    465     ///         cred_protect: CredProtect::UserVerificationRequired(false, ExtensionInfo::RequireEnforceValue),
    466     ///         min_pin_length: Some((FourToSixtyThree::Sixteen, ExtensionInfo::AllowDontEnforceValue)),
    467     ///         prf: Some((PrfInput { first: [0].as_slice(), second: None, }, ExtensionInfo::AllowEnforceValue))
    468     ///     })?,
    469     ///     r#"{"credProps":true,"credentialProtectionPolicy":"userVerificationRequired","enforceCredentialProtectionPolicy":false,"minPinLength":true,"prf":{"eval":{"first":"AA"}}}"#
    470     /// );
    471     /// # Ok::<_, serde_json::Error>(())
    472     /// ```
    473     #[expect(clippy::unreachable, reason = "we want to crash when there is a bug")]
    474     #[expect(
    475         clippy::arithmetic_side_effects,
    476         reason = "comment explains how overflow is not possible"
    477     )]
    478     #[inline]
    479     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    480     where
    481         S: Serializer,
    482     {
    483         // The max is 1 + 2 + 1 + 1 = 5, so overflow is no concern.
    484         let count = usize::from(self.cred_props.is_some())
    485             + if matches!(self.cred_protect, CredProtect::None) {
    486                 0
    487             } else {
    488                 2
    489             }
    490             + usize::from(self.min_pin_length.is_some())
    491             + usize::from(self.prf.is_some());
    492         serializer
    493             .serialize_struct("Extension", count)
    494             .and_then(|mut ser| {
    495                 self.cred_props
    496                     .map_or(Ok(()), |_| ser.serialize_field(CRED_PROPS, &true))
    497                     .and_then(|()| {
    498                         if matches!(self.cred_protect, CredProtect::None) {
    499                             Ok(())
    500                         } else {
    501                             let enforce_policy;
    502                             // [`credProtect`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-credProtect-extension)
    503                             // is serialized by serializing its fields directly and not as a map of fields.
    504                             ser.serialize_field(
    505                                 CREDENTIAL_PROTECTION_POLICY,
    506                                 match self.cred_protect {
    507                                     CredProtect::None => unreachable!(
    508                                         "Extensions is incorrectly serializing credProtect"
    509                                     ),
    510                                     CredProtect::UserVerificationOptional(enforce, _) => {
    511                                         enforce_policy = enforce;
    512                                         USER_VERIFICATION_OPTIONAL
    513                                     }
    514                                     CredProtect::UserVerificationOptionalWithCredentialIdList(
    515                                         enforce,
    516                                         _,
    517                                     ) => {
    518                                         enforce_policy = enforce;
    519                                         USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST
    520                                     }
    521                                     CredProtect::UserVerificationRequired(enforce, _) => {
    522                                         enforce_policy = enforce;
    523                                         USER_VERIFICATION_REQUIRED
    524                                     }
    525                                 },
    526                             )
    527                             .and_then(|()| {
    528                                 ser.serialize_field(
    529                                     ENFORCE_CREDENTIAL_PROTECTION_POLICY,
    530                                     &enforce_policy,
    531                                 )
    532                             })
    533                         }
    534                         .and_then(|()| {
    535                             self.min_pin_length
    536                                 .map_or(Ok(()), |_| ser.serialize_field(MIN_PIN_LENGTH, &true))
    537                                 .and_then(|()| {
    538                                     self.prf
    539                                         .map_or(Ok(()), |(prf, _)| {
    540                                             ser.serialize_field(PRF, &Prf(prf))
    541                                         })
    542                                         .and_then(|()| ser.end())
    543                                 })
    544                         })
    545                     })
    546             })
    547     }
    548 }
    549 /// `"rp"`
    550 const RP: &str = "rp";
    551 /// `"user"`
    552 const USER: &str = "user";
    553 /// `"challenge"`
    554 const CHALLENGE: &str = "challenge";
    555 /// `"pubKeyCredParams"`
    556 const PUB_KEY_CRED_PARAMS: &str = "pubKeyCredParams";
    557 /// `"timeout"`
    558 const TIMEOUT: &str = "timeout";
    559 /// `"excludeCredentials"`
    560 const EXCLUDE_CREDENTIALS: &str = "excludeCredentials";
    561 /// `"authenticatorSelection"`
    562 const AUTHENTICATOR_SELECTION: &str = "authenticatorSelection";
    563 /// `"hints"`
    564 const HINTS: &str = "hints";
    565 /// `"attestation"`
    566 const ATTESTATION: &str = "attestation";
    567 /// `"attestationFormats"`
    568 const ATTESTATION_FORMATS: &str = "attestationFormats";
    569 /// `"extensions"`
    570 const EXTENSIONS: &str = "extensions";
    571 /// "none".
    572 const NONE: &str = "none";
    573 impl<'user_name, 'user_display_name, 'user_id, const USER_LEN: usize> Serialize
    574     for PublicKeyCredentialCreationOptions<
    575         '_,
    576         'user_name,
    577         'user_display_name,
    578         'user_id,
    579         '_,
    580         '_,
    581         USER_LEN,
    582     >
    583 where
    584     PublicKeyCredentialUserEntity<'user_name, 'user_display_name, 'user_id, USER_LEN>: Serialize,
    585 {
    586     /// Serializes `self` to conform with
    587     /// [`PublicKeyCredentialCreationOptionsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialcreationoptionsjson).
    588     #[inline]
    589     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    590     where
    591         S: Serializer,
    592     {
    593         serializer
    594             .serialize_struct("PublicKeyCredentialCreationOptions", 11)
    595             .and_then(|mut ser| {
    596                 ser.serialize_field(RP, &PublicKeyCredentialRpEntity(self.rp_id))
    597                     .and_then(|()| {
    598                         ser.serialize_field(USER, &self.user).and_then(|()| {
    599                             ser.serialize_field(CHALLENGE, &self.challenge)
    600                                 .and_then(|()| {
    601                                     ser.serialize_field(
    602                                         PUB_KEY_CRED_PARAMS,
    603                                         &self.pub_key_cred_params,
    604                                     )
    605                                     .and_then(|()| {
    606                                         ser.serialize_field(TIMEOUT, &self.timeout).and_then(|()| {
    607                                             ser.serialize_field(
    608                                                 EXCLUDE_CREDENTIALS,
    609                                                 self.exclude_credentials.as_slice(),
    610                                             )
    611                                             .and_then(
    612                                                 |()| {
    613                                                     ser.serialize_field(
    614                                                         AUTHENTICATOR_SELECTION,
    615                                                         &self.authenticator_selection,
    616                                                     )
    617                                                     .and_then(|()| {
    618                                                         ser.serialize_field(HINTS, &self.hints)
    619                                                             .and_then(|()| {
    620                                                                 ser.serialize_field(
    621                                                                     ATTESTATION,
    622                                                                     NONE,
    623                                                                 )
    624                                                                 .and_then(|()| {
    625                                                                     ser.serialize_field(
    626                                                                         ATTESTATION_FORMATS,
    627                                                                         [NONE].as_slice(),
    628                                                                     )
    629                                                                     .and_then(|()| {
    630                                                                         ser.serialize_field(
    631                                                                             EXTENSIONS,
    632                                                                             &self.extensions,
    633                                                                         )
    634                                                                         .and_then(|()| ser.end())
    635                                                                     })
    636                                                                 })
    637                                                             })
    638                                                     })
    639                                                 },
    640                                             )
    641                                         })
    642                                     })
    643                                 })
    644                         })
    645                     })
    646             })
    647     }
    648 }
    649 /// `"mediation"`.
    650 const MEDIATION: &str = "mediation";
    651 /// `"publicKey"`.
    652 const PUBLIC_KEY_NO_HYPEN: &str = "publicKey";
    653 impl<
    654     'rp_id,
    655     'user_name,
    656     'user_display_name,
    657     'user_id,
    658     'prf_first,
    659     'prf_second,
    660     const USER_LEN: usize,
    661 > Serialize
    662     for CredentialCreationOptions<
    663         'rp_id,
    664         'user_name,
    665         'user_display_name,
    666         'user_id,
    667         'prf_first,
    668         'prf_second,
    669         USER_LEN,
    670     >
    671 where
    672     PublicKeyCredentialCreationOptions<
    673         'rp_id,
    674         'user_name,
    675         'user_display_name,
    676         'user_id,
    677         'prf_first,
    678         'prf_second,
    679         USER_LEN,
    680     >: Serialize,
    681 {
    682     /// Serializes `self` to conform with
    683     /// [`CredentialCreationOptions`](https://www.w3.org/TR/credential-management-1/#dictdef-credentialcreationoptions).
    684     ///
    685     /// Note [`signal`](https://www.w3.org/TR/credential-management-1/#dom-credentialcreationoptions-signal)
    686     /// is not present, and [`publicKey`](https://www.w3.org/TR/credential-management-1/#sctn-cred-type-registry)
    687     /// is serialized according to [`PublicKeyCredentialCreationOptions::serialize`].
    688     #[inline]
    689     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    690     where
    691         S: Serializer,
    692     {
    693         serializer
    694             .serialize_struct("CredentialCreationOptions", 2)
    695             .and_then(|mut ser| {
    696                 ser.serialize_field(MEDIATION, &self.mediation)
    697                     .and_then(|()| {
    698                         ser.serialize_field(PUBLIC_KEY_NO_HYPEN, &self.public_key)
    699                             .and_then(|()| ser.end())
    700                     })
    701             })
    702     }
    703 }
    704 impl<
    705     'rp_id,
    706     'user_name,
    707     'user_display_name,
    708     'user_id,
    709     'prf_first,
    710     'prf_second,
    711     const USER_LEN: usize,
    712 > Serialize
    713     for RegistrationClientState<
    714         'rp_id,
    715         'user_name,
    716         'user_display_name,
    717         'user_id,
    718         'prf_first,
    719         'prf_second,
    720         USER_LEN,
    721     >
    722 where
    723     CredentialCreationOptions<
    724         'rp_id,
    725         'user_name,
    726         'user_display_name,
    727         'user_id,
    728         'prf_first,
    729         'prf_second,
    730         USER_LEN,
    731     >: Serialize,
    732 {
    733     /// Serializes `self` according to [`CredentialCreationOptions::serialize`].
    734     ///
    735     /// # Examples
    736     ///
    737     /// ```
    738     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    739     /// # use webauthn_rp::{bin::Decode, response::bin::DecodeAuthTransportsErr};
    740     /// # use webauthn_rp::{
    741     /// #     request::{
    742     /// #         register::{
    743     /// #             FourToSixtyThree, UserHandle64, CredentialCreationOptions, PublicKeyCredentialUserEntity, UserHandle
    744     /// #         },
    745     /// #         AsciiDomain, ExtensionInfo, Hints, PublicKeyCredentialHint, RpId, PublicKeyCredentialDescriptor, Credentials, UserVerificationRequirement,
    746     /// #     },
    747     /// #     response::{AuthTransports, AuthenticatorAttachment, CredentialId},
    748     /// # };
    749     /// /// Retrieves the `AuthTransports` associated with the unique `cred_id`
    750     /// /// from the database.
    751     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    752     /// fn get_transports(cred_id: CredentialId<&[u8]>) -> Result<AuthTransports, DecodeAuthTransportsErr> {
    753     ///     // ⋮
    754     /// #     AuthTransports::decode(32)
    755     /// }
    756     /// let mut creds = Vec::with_capacity(1);
    757     /// // `CredentialId::try_from` only exists when `custom` is enabled; and even then, it is
    758     /// // likely never needed since the `CredentialId` was originally sent from the client and is likely
    759     /// // stored in a database which would be fetched by `UserHandle` or `Authentication::raw_id`.
    760     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    761     /// let id = CredentialId::try_from(vec![0; 16].into_boxed_slice())?;
    762     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    763     /// let transports = get_transports((&id).into())?;
    764     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    765     /// creds.push(PublicKeyCredentialDescriptor { id, transports });
    766     /// let rp_id = RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?);
    767     /// let user_handle = UserHandle64::new();
    768     /// let mut options = CredentialCreationOptions::passkey(&rp_id, PublicKeyCredentialUserEntity { name: "pierre.de.fermat", id: &user_handle, display_name: "Pierre de Fermat", }, creds);
    769     /// options.public_key.authenticator_selection.authenticator_attachment = AuthenticatorAttachment::None;
    770     /// options.public_key.hints = Hints::EMPTY.add(PublicKeyCredentialHint::SecurityKey);
    771     /// options.public_key.extensions.min_pin_length = Some((FourToSixtyThree::Sixteen, ExtensionInfo::RequireEnforceValue));
    772     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    773     /// let client_state = serde_json::to_string(&options.start_ceremony()?.1).unwrap_or_else(|_e| unreachable!("bug in RegistrationClientState::serialize"));
    774     /// let json = serde_json::json!({
    775     ///     "mediation":"required",
    776     ///     "publicKey":{
    777     ///         "rp":{
    778     ///             "name":"example.com",
    779     ///             "id":"example.com"
    780     ///         },
    781     ///         "user":{
    782     ///             "name":"pierre.de.fermat",
    783     ///             "id":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
    784     ///             "displayName":"Pierre de Fermat"
    785     ///         },
    786     ///         "challenge":"AAAAAAAAAAAAAAAAAAAAAA",
    787     ///         "pubKeyCredParams":[
    788     ///             {
    789     ///                 "type":"public-key",
    790     ///                 "alg":-50
    791     ///             },
    792     ///             {
    793     ///                 "type":"public-key",
    794     ///                 "alg":-49
    795     ///             },
    796     ///             {
    797     ///                 "type":"public-key",
    798     ///                 "alg":-48
    799     ///             },
    800     ///             {
    801     ///                 "type":"public-key",
    802     ///                 "alg":-8
    803     ///             },
    804     ///             {
    805     ///                 "type":"public-key",
    806     ///                 "alg":-7
    807     ///             },
    808     ///             {
    809     ///                 "type":"public-key",
    810     ///                 "alg":-35
    811     ///             },
    812     ///             {
    813     ///                 "type":"public-key",
    814     ///                 "alg":-257
    815     ///             },
    816     ///         ],
    817     ///         "timeout":300000,
    818     ///         "excludeCredentials":[
    819     ///             {
    820     ///                 "type":"public-key",
    821     ///                 "id":"AAAAAAAAAAAAAAAAAAAAAA",
    822     ///                 "transports":["usb"]
    823     ///             }
    824     ///         ],
    825     ///         "authenticatorSelection":{
    826     ///             "residentKey":"required",
    827     ///             "requireResidentKey":true,
    828     ///             "userVerification":"required"
    829     ///         },
    830     ///         "hints":[
    831     ///             "security-key"
    832     ///         ],
    833     ///         "attestation":"none",
    834     ///         "attestationFormats":[
    835     ///             "none"
    836     ///         ],
    837     ///         "extensions":{
    838     ///             "credentialProtectionPolicy":"userVerificationRequired",
    839     ///             "enforceCredentialProtectionPolicy":false,
    840     ///             "minPinLength":true
    841     ///         }
    842     ///     }
    843     /// }).to_string();
    844     /// // Since `Challenge`s are randomly generated, we don't know what it will be.
    845     /// // Similarly since we randomly generated a 64-byte `UserHandle`, we don't know what
    846     /// // it will be; thus we test the JSON string for everything except those two.
    847     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    848     /// assert_eq!(client_state.get(..124), json.get(..124));
    849     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    850     /// assert_eq!(client_state.get(210..259), json.get(210..259));
    851     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    852     /// assert_eq!(client_state.get(281..), json.get(281..));
    853     /// # Ok::<_, webauthn_rp::AggErr>(())
    854     /// ```
    855     #[inline]
    856     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    857     where
    858         S: Serializer,
    859     {
    860         self.0.serialize(serializer)
    861     }
    862 }
    863 impl<'de, const LEN: usize> Deserialize<'de> for UserHandle<LEN>
    864 where
    865     Self: Default,
    866 {
    867     /// Deserializes [`prim@str`] based on
    868     /// [`userHandle`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorassertionresponsejson-userhandle).
    869     ///
    870     /// # Examples
    871     ///
    872     /// ```
    873     /// # use webauthn_rp::request::register::{UserHandle, USER_HANDLE_MIN_LEN};
    874     /// # #[cfg(feature = "custom")]
    875     /// assert_eq!(
    876     ///     serde_json::from_str::<UserHandle<USER_HANDLE_MIN_LEN>>(r#""AA""#)?,
    877     ///     UserHandle::from([0])
    878     /// );
    879     /// # Ok::<_, serde_json::Error>(())
    880     ///```
    881     #[inline]
    882     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    883     where
    884         D: Deserializer<'de>,
    885     {
    886         /// `Visitor` for `UserHandle`.
    887         struct UserHandleVisitor<const L: usize>;
    888         impl<const L: usize> Visitor<'_> for UserHandleVisitor<L>
    889         where
    890             UserHandle<L>: Default,
    891         {
    892             type Value = UserHandle<L>;
    893             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    894                 formatter.write_str("UserHandle")
    895             }
    896             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    897             where
    898                 E: Error,
    899             {
    900                 if base64url_nopad::encode_len(L) == v.len() {
    901                     let mut data = [0; L];
    902                     base64url_nopad::decode_buffer_exact(v.as_bytes(), data.as_mut_slice())
    903                         .map_err(E::custom)
    904                         .map(|()| UserHandle(data))
    905                 } else {
    906                     Err(E::invalid_value(
    907                         Unexpected::Str(v),
    908                         &format!("{L} bytes encoded in base64url without padding").as_str(),
    909                     ))
    910                 }
    911             }
    912         }
    913         deserializer.deserialize_str(UserHandleVisitor)
    914     }
    915 }
    916 impl<'de> Deserialize<'de> for CoseAlgorithmIdentifier {
    917     /// Deserializes [`i16`] based on
    918     /// [COSE Algorithms](https://www.iana.org/assignments/cose/cose.xhtml#algorithms).
    919     #[inline]
    920     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    921     where
    922         D: Deserializer<'de>,
    923     {
    924         /// `Visitor` for `CoseAlgorithmIdentifier`.
    925         ///
    926         /// We visit all signed integral types sans `i8` just in case a `Deserializer` only implements one of them.
    927         struct CoseAlgorithmIdentifierVisitor;
    928         impl Visitor<'_> for CoseAlgorithmIdentifierVisitor {
    929             type Value = CoseAlgorithmIdentifier;
    930             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    931                 formatter.write_str("CoseAlgorithmIdentifier")
    932             }
    933             fn visit_i16<E>(self, v: i16) -> Result<Self::Value, E>
    934             where
    935                 E: Error,
    936             {
    937                 match v {
    938                     RS256 => Ok(CoseAlgorithmIdentifier::Rs256),
    939                     MLDSA87 => Ok(CoseAlgorithmIdentifier::Mldsa87),
    940                     MLDSA65 => Ok(CoseAlgorithmIdentifier::Mldsa65),
    941                     MLDSA44 => Ok(CoseAlgorithmIdentifier::Mldsa44),
    942                     ES384 => Ok(CoseAlgorithmIdentifier::Es384),
    943                     EDDSA => Ok(CoseAlgorithmIdentifier::Eddsa),
    944                     ES256 => Ok(CoseAlgorithmIdentifier::Es256),
    945                     _ => Err(E::invalid_value(
    946                         Unexpected::Signed(i64::from(v)),
    947                         &format!(
    948                             "{MLDSA87}, {MLDSA65}, {MLDSA44}, {EDDSA}, {ES256}, {ES384}, or {RS256}"
    949                         )
    950                         .as_str(),
    951                     )),
    952                 }
    953             }
    954             fn visit_i32<E>(self, v: i32) -> Result<Self::Value, E>
    955             where
    956                 E: Error,
    957             {
    958                 i16::try_from(v)
    959                     .map_err(E::custom)
    960                     .and_then(|val| self.visit_i16(val))
    961             }
    962             fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
    963             where
    964                 E: Error,
    965             {
    966                 i16::try_from(v)
    967                     .map_err(E::custom)
    968                     .and_then(|val| self.visit_i16(val))
    969             }
    970         }
    971         deserializer.deserialize_i16(CoseAlgorithmIdentifierVisitor)
    972     }
    973 }
    974 /// Helper to deserialize `PublicKeyCredentialRpEntity` with an optional `RpId`.
    975 ///
    976 /// Used in [`ClientCredentialCreationOptions::deserialize`].
    977 struct PublicKeyCredentialRpEntityHelper(Option<RpId>);
    978 impl<'de> Deserialize<'de> for PublicKeyCredentialRpEntityHelper {
    979     /// Conforms to the following schema:
    980     ///
    981     /// ```json
    982     /// {
    983     ///   "id": null | <RpId>,
    984     ///   "name": null | <RpId> | <Nickname>
    985     /// }
    986     /// ```
    987     ///
    988     /// None of the fields are required, and missing fields are interpreted the same as fields
    989     /// with `null` values.
    990     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    991     where
    992         D: Deserializer<'de>,
    993     {
    994         /// `Visitor` for `PublicKeyCredentialRpEntityHelper`.
    995         struct PublicKeyCredentialRpEntityHelperVisitor;
    996         impl<'d> Visitor<'d> for PublicKeyCredentialRpEntityHelperVisitor {
    997             type Value = PublicKeyCredentialRpEntityHelper;
    998             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    999                 formatter.write_str("PublicKeyCredentialRpEntityHelper")
   1000             }
   1001             fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
   1002             where
   1003                 A: MapAccess<'d>,
   1004             {
   1005                 /// Field for `PublicKeyCredentialRpEntityHelper`.
   1006                 enum Field {
   1007                     /// `id`.
   1008                     Id,
   1009                     /// `name`.
   1010                     Name,
   1011                 }
   1012                 impl<'e> Deserialize<'e> for Field {
   1013                     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1014                     where
   1015                         D: Deserializer<'e>,
   1016                     {
   1017                         /// `Visitor` for `Field`.
   1018                         struct FieldVisitor;
   1019                         impl Visitor<'_> for FieldVisitor {
   1020                             type Value = Field;
   1021                             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1022                                 write!(formatter, "'{ID}' or '{NAME}'")
   1023                             }
   1024                             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
   1025                             where
   1026                                 E: Error,
   1027                             {
   1028                                 match v {
   1029                                     ID => Ok(Field::Id),
   1030                                     NAME => Ok(Field::Name),
   1031                                     _ => Err(E::unknown_field(v, FIELDS)),
   1032                                 }
   1033                             }
   1034                         }
   1035                         deserializer.deserialize_identifier(FieldVisitor)
   1036                     }
   1037                 }
   1038                 /// Helper to deserialize `name`.
   1039                 struct Name;
   1040                 impl<'e> Deserialize<'e> for Name {
   1041                     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1042                     where
   1043                         D: Deserializer<'e>,
   1044                     {
   1045                         /// `Visitor` for `Name`.
   1046                         struct NameVisitor;
   1047                         impl Visitor<'_> for NameVisitor {
   1048                             type Value = Name;
   1049                             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1050                                 formatter.write_str("RpId name")
   1051                             }
   1052                             fn visit_str<E>(self, _: &str) -> Result<Self::Value, E>
   1053                             where
   1054                                 E: Error,
   1055                             {
   1056                                 Ok(Name)
   1057                             }
   1058                         }
   1059                         deserializer.deserialize_str(NameVisitor)
   1060                     }
   1061                 }
   1062                 let mut id = None;
   1063                 let mut name = false;
   1064                 while let Some(key) = map.next_key()? {
   1065                     match key {
   1066                         Field::Id => {
   1067                             if id.is_some() {
   1068                                 return Err(Error::duplicate_field(ID));
   1069                             }
   1070                             id = map.next_value::<Option<_>>().map(Some)?;
   1071                         }
   1072                         Field::Name => {
   1073                             if name {
   1074                                 return Err(Error::duplicate_field(NAME));
   1075                             }
   1076                             name = map.next_value::<Option<Name>>().map(|_n| true)?;
   1077                         }
   1078                     }
   1079                 }
   1080                 Ok(PublicKeyCredentialRpEntityHelper(id.flatten()))
   1081             }
   1082         }
   1083         /// Fields for `PublicKeyCredentialRpEntityHelper`.
   1084         const FIELDS: &[&str; 2] = &[ID, NAME];
   1085         deserializer.deserialize_struct(
   1086             "PublicKeyCredentialRpEntityHelper",
   1087             FIELDS,
   1088             PublicKeyCredentialRpEntityHelperVisitor,
   1089         )
   1090     }
   1091 }
   1092 /// Similar to [`PublicKeyCredentialUserEntity`] except the [`UserHandle`] is owned, and all fields are
   1093 /// optional.
   1094 ///
   1095 /// This is primarily useful to assist [`ClientCredentialCreationOptions::deserialize`].
   1096 #[derive(Debug, Default)]
   1097 pub struct PublicKeyCredentialUserEntityOwned<const LEN: usize> {
   1098     /// See [`PublicKeyCredentialUserEntity::name`].
   1099     pub name: Option<String>,
   1100     /// See [`PublicKeyCredentialUserEntity::id`].
   1101     pub id: Option<UserHandle<LEN>>,
   1102     /// See [`PublicKeyCredentialUserEntity::display_name`].
   1103     pub display_name: Option<String>,
   1104 }
   1105 /// Error returned when converting a [`PublicKeyCredentialUserEntityOwned`] into a
   1106 /// [`PublicKeyCredentialUserEntity`] (e.g., via [`PublicKeyCredentialUserEntityOwned::with_id`]).
   1107 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
   1108 pub enum PublicKeyCredentialUserEntityOwnedErr {
   1109     /// Variant returned when [`PublicKeyCredentialUserEntityOwned::name`] is `None`.
   1110     MissingName,
   1111     /// Variant returned when [`PublicKeyCredentialUserEntityOwned::id`] is `None`.
   1112     MissingId,
   1113     /// Variant returned when [`PublicKeyCredentialUserEntityOwned::display_name`] is `None`.
   1114     MissingDisplayName,
   1115 }
   1116 impl Display for PublicKeyCredentialUserEntityOwnedErr {
   1117     #[inline]
   1118     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
   1119         f.write_str(match *self {
   1120             Self::MissingName => "user entity info did not have a username",
   1121             Self::MissingId => "user entity info did not have a user handle",
   1122             Self::MissingDisplayName => "user entity info did not have a user display name",
   1123         })
   1124     }
   1125 }
   1126 impl E for PublicKeyCredentialUserEntityOwnedErr {}
   1127 impl<const LEN: usize> PublicKeyCredentialUserEntityOwned<LEN> {
   1128     /// Returns a `PublicKeyCredentialUserEntity` based on `self`.
   1129     ///
   1130     /// # Errors
   1131     ///
   1132     /// Errors iff any of the fields in `self` are `None`.
   1133     #[inline]
   1134     pub fn as_entity(
   1135         &self,
   1136     ) -> Result<PublicKeyCredentialUserEntity<'_, '_, '_, LEN>, PublicKeyCredentialUserEntityOwnedErr>
   1137     {
   1138         self.name
   1139             .as_ref()
   1140             .ok_or(PublicKeyCredentialUserEntityOwnedErr::MissingName)
   1141             .and_then(|username| {
   1142                 self.id
   1143                     .as_ref()
   1144                     .ok_or(PublicKeyCredentialUserEntityOwnedErr::MissingId)
   1145                     .and_then(|id| {
   1146                         self.display_name
   1147                             .as_ref()
   1148                             .ok_or(PublicKeyCredentialUserEntityOwnedErr::MissingDisplayName)
   1149                             .map(|display| PublicKeyCredentialUserEntity {
   1150                                 name: username,
   1151                                 id,
   1152                                 display_name: display,
   1153                             })
   1154                     })
   1155             })
   1156     }
   1157     /// Returns a `PublicKeyCredentialUserEntity` based on `self` and `id`.
   1158     ///
   1159     /// Note `id` is used _unconditionally_ regardless if [`Self::id`] is `Some`.
   1160     ///
   1161     /// # Errors
   1162     ///
   1163     /// Errors iff [`Self::name`] or [`Self::display_name`] are `None`.
   1164     #[inline]
   1165     pub fn with_id<'id>(
   1166         &self,
   1167         id: &'id UserHandle<LEN>,
   1168     ) -> Result<
   1169         PublicKeyCredentialUserEntity<'_, '_, 'id, LEN>,
   1170         PublicKeyCredentialUserEntityOwnedErr,
   1171     > {
   1172         self.name
   1173             .as_ref()
   1174             .ok_or(PublicKeyCredentialUserEntityOwnedErr::MissingName)
   1175             .and_then(|username| {
   1176                 self.display_name
   1177                     .as_ref()
   1178                     .ok_or(PublicKeyCredentialUserEntityOwnedErr::MissingDisplayName)
   1179                     .map(|display| PublicKeyCredentialUserEntity {
   1180                         name: username,
   1181                         id,
   1182                         display_name: display,
   1183                     })
   1184             })
   1185     }
   1186     /// Returns a `PublicKeyCredentialUserEntity` based on `self`, `name`, and `display_name`.
   1187     ///
   1188     /// Note `name` and `display_name` are used _unconditionally_ regardless if [`Self::name`] or
   1189     /// [`Self::display_name`] are `Some`.
   1190     ///
   1191     /// # Errors
   1192     ///
   1193     /// Errors iff [`Self::id`] is `None`.
   1194     #[inline]
   1195     pub fn with_name_and_display_name<'name, 'display_name>(
   1196         &self,
   1197         name: &'name str,
   1198         display_name: &'display_name str,
   1199     ) -> Result<
   1200         PublicKeyCredentialUserEntity<'name, 'display_name, '_, LEN>,
   1201         PublicKeyCredentialUserEntityOwnedErr,
   1202     > {
   1203         self.id
   1204             .as_ref()
   1205             .ok_or(PublicKeyCredentialUserEntityOwnedErr::MissingId)
   1206             .map(|id| PublicKeyCredentialUserEntity {
   1207                 name,
   1208                 id,
   1209                 display_name,
   1210             })
   1211     }
   1212 }
   1213 impl<'de, const LEN: usize> Deserialize<'de> for PublicKeyCredentialUserEntityOwned<LEN>
   1214 where
   1215     UserHandle<LEN>: Deserialize<'de>,
   1216 {
   1217     /// Deserializes a `struct` according to
   1218     /// [`PublicKeyCredentialUserEntityJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialuserentityjson).
   1219     ///
   1220     /// Note none of the fields are required and all of them are allowed to be `null`.
   1221     /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentityjson-id) is deserialized
   1222     /// according to [`UserHandle::deserialize`],
   1223     /// [`name`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentityjson-name) deserializes
   1224     /// [`prim@str`].
   1225     /// [`displayName`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentityjson-displayname)
   1226     /// deserializes [`prim@str`].
   1227     ///
   1228     /// Unknown or duplicate fields lead to an error. Missing fields are interpreted the same as if the field
   1229     /// were assigned `null`.
   1230     ///
   1231     /// # Examples
   1232     ///
   1233     /// ```
   1234     /// # use webauthn_rp::request::register::ser::PublicKeyCredentialUserEntityOwned;
   1235     /// let val = serde_json::from_str::<PublicKeyCredentialUserEntityOwned<16>>(r#"{"name":"paul.erdos","displayName":"Erdős Pál"}"#)?;
   1236     /// assert!(val.name.is_some_and(|name| name == "paul.erdos"));
   1237     /// assert!(val.display_name.is_some_and(|display| display == "Erdős Pál"));
   1238     /// assert!(val.id.is_none());
   1239     /// # Ok::<_, serde_json::Error>(())
   1240     /// ```
   1241     #[inline]
   1242     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1243     where
   1244         D: Deserializer<'de>,
   1245     {
   1246         /// `Visitor` for `PublicKeyCredentialUserEntityOwned`.
   1247         struct PublicKeyCredentialUserEntityOwnedVisitor<const L: usize>;
   1248         impl<'d, const L: usize> Visitor<'d> for PublicKeyCredentialUserEntityOwnedVisitor<L>
   1249         where
   1250             UserHandle<L>: Deserialize<'d>,
   1251         {
   1252             type Value = PublicKeyCredentialUserEntityOwned<L>;
   1253             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1254                 formatter.write_str("PublicKeyCredentialUserEntityOwned")
   1255             }
   1256             fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
   1257             where
   1258                 A: MapAccess<'d>,
   1259             {
   1260                 /// Field for `PublicKeyCredentialUserEntityOwned`.
   1261                 enum Field {
   1262                     /// `id`.
   1263                     Id,
   1264                     /// `name`.
   1265                     Name,
   1266                     /// `displayName`
   1267                     DisplayName,
   1268                 }
   1269                 impl<'e> Deserialize<'e> for Field {
   1270                     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1271                     where
   1272                         D: Deserializer<'e>,
   1273                     {
   1274                         /// `Visitor` for `Field`.
   1275                         struct FieldVisitor;
   1276                         impl Visitor<'_> for FieldVisitor {
   1277                             type Value = Field;
   1278                             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1279                                 write!(formatter, "'{ID}', '{NAME}', or '{DISPLAY_NAME}'")
   1280                             }
   1281                             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
   1282                             where
   1283                                 E: Error,
   1284                             {
   1285                                 match v {
   1286                                     ID => Ok(Field::Id),
   1287                                     NAME => Ok(Field::Name),
   1288                                     DISPLAY_NAME => Ok(Field::DisplayName),
   1289                                     _ => Err(E::unknown_field(v, FIELDS)),
   1290                                 }
   1291                             }
   1292                         }
   1293                         deserializer.deserialize_identifier(FieldVisitor)
   1294                     }
   1295                 }
   1296                 let mut user_handle = None;
   1297                 let mut username = None;
   1298                 let mut display = None;
   1299                 while let Some(key) = map.next_key()? {
   1300                     match key {
   1301                         Field::Id => {
   1302                             if user_handle.is_some() {
   1303                                 return Err(Error::duplicate_field(ID));
   1304                             }
   1305                             user_handle = map.next_value::<Option<_>>().map(Some)?;
   1306                         }
   1307                         Field::Name => {
   1308                             if username.is_some() {
   1309                                 return Err(Error::duplicate_field(NAME));
   1310                             }
   1311                             username = map.next_value::<Option<_>>().map(Some)?;
   1312                         }
   1313                         Field::DisplayName => {
   1314                             if display.is_some() {
   1315                                 return Err(Error::duplicate_field(DISPLAY_NAME));
   1316                             }
   1317                             display = map.next_value::<Option<_>>().map(Some)?;
   1318                         }
   1319                     }
   1320                 }
   1321                 Ok(PublicKeyCredentialUserEntityOwned {
   1322                     id: user_handle.flatten(),
   1323                     name: username.flatten(),
   1324                     display_name: display.flatten(),
   1325                 })
   1326             }
   1327         }
   1328         /// Fields for `PublicKeyCredentialUserEntityOwned`.
   1329         const FIELDS: &[&str; 3] = &[ID, NAME, DISPLAY_NAME];
   1330         deserializer.deserialize_struct(
   1331             "PublicKeyCredentialUserEntityOwned",
   1332             FIELDS,
   1333             PublicKeyCredentialUserEntityOwnedVisitor,
   1334         )
   1335     }
   1336 }
   1337 /// `newtype` around `CoseAlgorithmIdentifier`.
   1338 struct PubParam(CoseAlgorithmIdentifier);
   1339 impl<'de> Deserialize<'de> for PubParam {
   1340     /// Conforms to the following schema:
   1341     ///
   1342     /// ```json
   1343     /// {
   1344     ///   "alg": <CoseAlgorithmIdentifier>,
   1345     ///   "type": "public-key",
   1346     /// }
   1347     /// ```
   1348     ///
   1349     /// `"alg"` is required.
   1350     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1351     where
   1352         D: Deserializer<'de>,
   1353     {
   1354         /// `Visitor` for `PubParam`.
   1355         struct PubParamVisitor;
   1356         impl<'d> Visitor<'d> for PubParamVisitor {
   1357             type Value = PubParam;
   1358             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1359                 formatter.write_str("PubParam")
   1360             }
   1361             fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
   1362             where
   1363                 A: MapAccess<'d>,
   1364             {
   1365                 /// Field for `PubParam`.
   1366                 enum Field {
   1367                     /// `"type"`.
   1368                     Type,
   1369                     /// `"alg"`.
   1370                     Alg,
   1371                 }
   1372                 impl<'e> Deserialize<'e> for Field {
   1373                     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1374                     where
   1375                         D: Deserializer<'e>,
   1376                     {
   1377                         /// `Visitor` for `Field`.
   1378                         struct FieldVisitor;
   1379                         impl Visitor<'_> for FieldVisitor {
   1380                             type Value = Field;
   1381                             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1382                                 write!(formatter, "'{TYPE}' or '{ALG}'")
   1383                             }
   1384                             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
   1385                             where
   1386                                 E: Error,
   1387                             {
   1388                                 match v {
   1389                                     TYPE => Ok(Field::Type),
   1390                                     ALG => Ok(Field::Alg),
   1391                                     _ => Err(E::unknown_field(v, FIELDS)),
   1392                                 }
   1393                             }
   1394                         }
   1395                         deserializer.deserialize_identifier(FieldVisitor)
   1396                     }
   1397                 }
   1398                 let mut typ = false;
   1399                 let mut alg = None;
   1400                 while let Some(key) = map.next_key()? {
   1401                     match key {
   1402                         Field::Type => {
   1403                             if typ {
   1404                                 return Err(Error::duplicate_field(TYPE));
   1405                             }
   1406                             typ = map.next_value::<Type>().map(|_t| true)?;
   1407                         }
   1408                         Field::Alg => {
   1409                             if alg.is_some() {
   1410                                 return Err(Error::duplicate_field(ALG));
   1411                             }
   1412                             alg = map.next_value().map(Some)?;
   1413                         }
   1414                     }
   1415                 }
   1416                 alg.ok_or_else(|| Error::missing_field(ALG)).map(PubParam)
   1417             }
   1418         }
   1419         /// Fields for `PubParam`.
   1420         const FIELDS: &[&str; 2] = &[TYPE, ALG];
   1421         deserializer.deserialize_struct("PubParam", FIELDS, PubParamVisitor)
   1422     }
   1423 }
   1424 impl<'de> Deserialize<'de> for CoseAlgorithmIdentifiers {
   1425     /// Deserializes a sequence based on
   1426     /// [`pubKeyCredParams`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptionsjson-pubkeycredparams)
   1427     /// except [`type`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialparameters-type) is not required.
   1428     ///
   1429     /// Note the sequence of [`CoseAlgorithmIdentifier`]s MUST match [`CoseAlgorithmIdentifier::cmp`] or an
   1430     /// error will occur (e.g., if [`CoseAlgorithmIdentifier::Mldsa87`] exists, then it must appear first).
   1431     ///
   1432     /// An empty sequence will be treated as [`Self::ALL`].
   1433     ///
   1434     /// Unknown or duplicate fields lead to an error.
   1435     ///
   1436     /// # Examples
   1437     ///
   1438     /// ```
   1439     /// # use webauthn_rp::request::register::CoseAlgorithmIdentifiers;
   1440     /// assert!(serde_json::from_str::<CoseAlgorithmIdentifiers>(r#"[{"type":"public-key","alg":-50},{"type":"public-key","alg":-49},{"type":"public-key","alg":-48},{"type":"public-key","alg":-8},{"type":"public-key","alg":-7},{"type":"public-key","alg":-35},{"type":"public-key","alg":-257}]"#).is_ok());
   1441     /// ```
   1442     #[expect(clippy::too_many_lines, reason = "132 is fine")]
   1443     #[inline]
   1444     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1445     where
   1446         D: Deserializer<'de>,
   1447     {
   1448         /// `Visitor` for `CoseAlgorithmIdentifiers`.
   1449         struct CoseAlgorithmIdentifiersVisitor;
   1450         impl<'d> Visitor<'d> for CoseAlgorithmIdentifiersVisitor {
   1451             type Value = CoseAlgorithmIdentifiers;
   1452             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1453                 formatter.write_str("CoseAlgorithmIdentifiers")
   1454             }
   1455             #[expect(clippy::too_many_lines, reason = "118 is fine")]
   1456             #[expect(clippy::else_if_without_else, reason = "prefer it this way")]
   1457             fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
   1458             where
   1459                 A: SeqAccess<'d>,
   1460             {
   1461                 let mut mldsa87 = false;
   1462                 let mut mldsa65 = false;
   1463                 let mut mldsa44 = false;
   1464                 let mut eddsa = false;
   1465                 let mut es256 = false;
   1466                 let mut es384 = false;
   1467                 let mut rs256 = false;
   1468                 while let Some(elem) = seq.next_element::<PubParam>()? {
   1469                     match elem.0 {
   1470                         CoseAlgorithmIdentifier::Mldsa87 => {
   1471                             if mldsa87 {
   1472                                 return Err(Error::custom(
   1473                                     "pubKeyCredParams contained duplicate ML-DSA-87 values",
   1474                                 ));
   1475                             } else if mldsa65 || mldsa44 || eddsa || es256 || es384 || rs256 {
   1476                                 return Err(Error::custom(
   1477                                     "pubKeyCredParams contained ML-DSA-87, but it wasn't the first value",
   1478                                 ));
   1479                             }
   1480                             mldsa87 = true;
   1481                         }
   1482                         CoseAlgorithmIdentifier::Mldsa65 => {
   1483                             if mldsa65 {
   1484                                 return Err(Error::custom(
   1485                                     "pubKeyCredParams contained duplicate ML-DSA-65 values",
   1486                                 ));
   1487                             } else if mldsa44 || eddsa || es256 || es384 || rs256 {
   1488                                 return Err(Error::custom(
   1489                                     "pubKeyCredParams contained ML-DSA-65, but it was preceded by Mldsa44, Eddsa, Es256, Es384, or Rs256",
   1490                                 ));
   1491                             }
   1492                             mldsa65 = true;
   1493                         }
   1494                         CoseAlgorithmIdentifier::Mldsa44 => {
   1495                             if mldsa44 {
   1496                                 return Err(Error::custom(
   1497                                     "pubKeyCredParams contained duplicate ML-DSA-44 values",
   1498                                 ));
   1499                             } else if eddsa || es256 || es384 || rs256 {
   1500                                 return Err(Error::custom(
   1501                                     "pubKeyCredParams contained ML-DSA-44, but it was preceded by Eddsa, Es256, Es384, or Rs256",
   1502                                 ));
   1503                             }
   1504                             mldsa44 = true;
   1505                         }
   1506                         CoseAlgorithmIdentifier::Eddsa => {
   1507                             if eddsa {
   1508                                 return Err(Error::custom(
   1509                                     "pubKeyCredParams contained duplicate EdDSA values",
   1510                                 ));
   1511                             } else if es256 || es384 || rs256 {
   1512                                 return Err(Error::custom(
   1513                                     "pubKeyCredParams contained Eddsa, but it was preceded by Es256, Es384, or Rs256",
   1514                                 ));
   1515                             }
   1516                             eddsa = true;
   1517                         }
   1518                         CoseAlgorithmIdentifier::Es256 => {
   1519                             if es256 {
   1520                                 return Err(Error::custom(
   1521                                     "pubKeyCredParams contained duplicate Es256 values",
   1522                                 ));
   1523                             } else if es384 || rs256 {
   1524                                 return Err(Error::custom(
   1525                                     "pubKeyCredParams contained Es256, but it was preceded by Es384 or Rs256",
   1526                                 ));
   1527                             }
   1528                             es256 = true;
   1529                         }
   1530                         CoseAlgorithmIdentifier::Es384 => {
   1531                             if es384 {
   1532                                 return Err(Error::custom(
   1533                                     "pubKeyCredParams contained duplicate Es384 values",
   1534                                 ));
   1535                             } else if rs256 {
   1536                                 return Err(Error::custom(
   1537                                     "pubKeyCredParams contained Es384, but it was preceded by Rs256",
   1538                                 ));
   1539                             }
   1540                             es384 = true;
   1541                         }
   1542                         CoseAlgorithmIdentifier::Rs256 => {
   1543                             if rs256 {
   1544                                 return Err(Error::custom(
   1545                                     "pubKeyCredParams contained duplicate Rs256 values",
   1546                                 ));
   1547                             }
   1548                             rs256 = true;
   1549                         }
   1550                     }
   1551                 }
   1552                 let mut algs = CoseAlgorithmIdentifiers(0);
   1553                 if mldsa87 {
   1554                     algs = algs.add(CoseAlgorithmIdentifier::Mldsa87);
   1555                 }
   1556                 if mldsa65 {
   1557                     algs = algs.add(CoseAlgorithmIdentifier::Mldsa65);
   1558                 }
   1559                 if mldsa44 {
   1560                     algs = algs.add(CoseAlgorithmIdentifier::Mldsa44);
   1561                 }
   1562                 if eddsa {
   1563                     algs = algs.add(CoseAlgorithmIdentifier::Eddsa);
   1564                 }
   1565                 if es256 {
   1566                     algs = algs.add(CoseAlgorithmIdentifier::Es256);
   1567                 }
   1568                 if es384 {
   1569                     algs = algs.add(CoseAlgorithmIdentifier::Es384);
   1570                 }
   1571                 if rs256 {
   1572                     algs = algs.add(CoseAlgorithmIdentifier::Rs256);
   1573                 }
   1574                 Ok(if algs.0 == 0 {
   1575                     CoseAlgorithmIdentifiers::ALL
   1576                 } else {
   1577                     algs
   1578                 })
   1579             }
   1580         }
   1581         deserializer.deserialize_seq(CoseAlgorithmIdentifiersVisitor)
   1582     }
   1583 }
   1584 /// Helper for `UserVerificatonRequirement::deserialize` and [`ResidentKeyRequirement::deserialize`].
   1585 enum Requirement {
   1586     /// Required.
   1587     Required,
   1588     /// Discouraged.
   1589     Discouraged,
   1590     /// Preferred.
   1591     Preferred,
   1592 }
   1593 impl<'de> Deserialize<'de> for Requirement {
   1594     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1595     where
   1596         D: Deserializer<'de>,
   1597     {
   1598         /// `Visitor` for `Requirement`.
   1599         struct RequirementVisitor;
   1600         impl Visitor<'_> for RequirementVisitor {
   1601             type Value = Requirement;
   1602             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1603                 write!(formatter, "'{REQUIRED}', '{DISCOURAGED}', or '{PREFERRED}'")
   1604             }
   1605             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
   1606             where
   1607                 E: Error,
   1608             {
   1609                 match v {
   1610                     REQUIRED => Ok(Requirement::Required),
   1611                     DISCOURAGED => Ok(Requirement::Discouraged),
   1612                     PREFERRED => Ok(Requirement::Preferred),
   1613                     _ => Err(E::invalid_value(
   1614                         Unexpected::Str(v),
   1615                         &format!("'{REQUIRED}', '{DISCOURAGED}', or '{PREFERRED}'").as_str(),
   1616                     )),
   1617                 }
   1618             }
   1619         }
   1620         deserializer.deserialize_str(RequirementVisitor)
   1621     }
   1622 }
   1623 impl From<Requirement> for ResidentKeyRequirement {
   1624     #[inline]
   1625     fn from(value: Requirement) -> Self {
   1626         match value {
   1627             Requirement::Required => Self::Required,
   1628             Requirement::Discouraged => Self::Discouraged,
   1629             Requirement::Preferred => Self::Preferred,
   1630         }
   1631     }
   1632 }
   1633 impl From<Requirement> for UserVerificationRequirement {
   1634     #[inline]
   1635     fn from(value: Requirement) -> Self {
   1636         match value {
   1637             Requirement::Required => Self::Required,
   1638             Requirement::Discouraged => Self::Discouraged,
   1639             Requirement::Preferred => Self::Preferred,
   1640         }
   1641     }
   1642 }
   1643 impl<'de> Deserialize<'de> for ResidentKeyRequirement {
   1644     /// Deserializes [`prim@str`] based on
   1645     /// [`ResidentKeyRequirement`](https://www.w3.org/TR/webauthn-3/#enumdef-residentkeyrequirement).
   1646     ///
   1647     /// # Examples
   1648     ///
   1649     /// ```
   1650     /// # use webauthn_rp::request::register::ResidentKeyRequirement;
   1651     /// assert!(
   1652     ///     matches!(
   1653     ///         serde_json::from_str(r#""required""#)?,
   1654     ///         ResidentKeyRequirement::Required
   1655     ///     )
   1656     /// );
   1657     /// # Ok::<_, serde_json::Error>(())
   1658     /// ```
   1659     #[inline]
   1660     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1661     where
   1662         D: Deserializer<'de>,
   1663     {
   1664         Requirement::deserialize(deserializer).map(Self::from)
   1665     }
   1666 }
   1667 impl<'de> Deserialize<'de> for UserVerificationRequirement {
   1668     /// Deserializes [`prim@str`] based on
   1669     /// [`UserVerificationRequirement`](https://www.w3.org/TR/webauthn-3/#enumdef-userverificationrequirement).
   1670     ///
   1671     /// # Examples
   1672     ///
   1673     /// ```
   1674     /// # use webauthn_rp::request::UserVerificationRequirement;
   1675     /// assert!(
   1676     ///     matches!(
   1677     ///         serde_json::from_str(r#""required""#)?,
   1678     ///         UserVerificationRequirement::Required
   1679     ///     )
   1680     /// );
   1681     /// # Ok::<_, serde_json::Error>(())
   1682     /// ```
   1683     #[inline]
   1684     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1685     where
   1686         D: Deserializer<'de>,
   1687     {
   1688         Requirement::deserialize(deserializer).map(Self::from)
   1689     }
   1690 }
   1691 impl<'de> Deserialize<'de> for AuthenticatorSelectionCriteria {
   1692     /// Deserializes a `struct` based on
   1693     /// [`AuthenticatorSelectionCriteria`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticatorselectioncriteria).
   1694     ///
   1695     /// Note that none of the fields are required, and all are allowed to be `null`. Additionally
   1696     /// [`residentKey`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-residentkey) and
   1697     /// [`requireResidentKey`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-requireresidentkey)
   1698     /// must be consistent (i.e., `requireResidentKey` iff `residentKey` is [`ResidentKeyRequirement::Required`]).
   1699     ///
   1700     /// Missing and `null` fields default to the corresponding [`Default`] value. Unknown and duplicate fields
   1701     /// lead to an error.
   1702     ///
   1703     /// # Examples
   1704     ///
   1705     /// ```
   1706     /// # use webauthn_rp::{request::register::AuthenticatorSelectionCriteria, response::AuthenticatorAttachment};
   1707     /// assert_eq!(
   1708     ///     serde_json::from_str::<AuthenticatorSelectionCriteria>(r#"{"authenticatorAttachment":null,"residentKey":"required","requireResidentKey":true,"userVerification":"required"}"#)?.authenticator_attachment,
   1709     ///     AuthenticatorAttachment::None,
   1710     /// );
   1711     /// # Ok::<_, serde_json::Error>(())
   1712     /// ```
   1713     #[expect(clippy::too_many_lines, reason = "144 isn't too bad")]
   1714     #[inline]
   1715     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1716     where
   1717         D: Deserializer<'de>,
   1718     {
   1719         /// `Visitor` for `AuthenticatorSelectionCriteria`.
   1720         struct AuthenticatorSelectionCriteriaVisitor;
   1721         impl<'de> Visitor<'de> for AuthenticatorSelectionCriteriaVisitor {
   1722             type Value = AuthenticatorSelectionCriteria;
   1723             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1724                 formatter.write_str("AuthenticatorSelectionCriteria")
   1725             }
   1726             #[expect(clippy::too_many_lines, reason = "121 isn't too bad")]
   1727             fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
   1728             where
   1729                 A: MapAccess<'de>,
   1730             {
   1731                 /// Field for `AuthenticatorSelectionCriteria`.
   1732                 enum Field {
   1733                     /// `"authenticatorAttachment"`.
   1734                     AuthenticatorAttachment,
   1735                     /// `"residentKey"`.
   1736                     ResidentKey,
   1737                     /// `"requireResidentKey"`.
   1738                     RequireResidentKey,
   1739                     /// `"userVerification"`.
   1740                     UserVerification,
   1741                 }
   1742                 impl<'e> Deserialize<'e> for Field {
   1743                     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1744                     where
   1745                         D: Deserializer<'e>,
   1746                     {
   1747                         /// `Visitor` for `Field`.
   1748                         struct FieldVisitor;
   1749                         impl Visitor<'_> for FieldVisitor {
   1750                             type Value = Field;
   1751                             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1752                                 write!(
   1753                                     formatter,
   1754                                     "'{AUTHENTICATOR_ATTACHMENT}', '{RESIDENT_KEY}', '{REQUIRE_RESIDENT_KEY}', or '{USER_VERIFICATION}'"
   1755                                 )
   1756                             }
   1757                             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
   1758                             where
   1759                                 E: Error,
   1760                             {
   1761                                 match v {
   1762                                     AUTHENTICATOR_ATTACHMENT => Ok(Field::AuthenticatorAttachment),
   1763                                     RESIDENT_KEY => Ok(Field::ResidentKey),
   1764                                     REQUIRE_RESIDENT_KEY => Ok(Field::RequireResidentKey),
   1765                                     USER_VERIFICATION => Ok(Field::UserVerification),
   1766                                     _ => Err(Error::unknown_field(v, FIELDS)),
   1767                                 }
   1768                             }
   1769                         }
   1770                         deserializer.deserialize_identifier(FieldVisitor)
   1771                     }
   1772                 }
   1773                 let mut attach = None;
   1774                 let mut res_key = None;
   1775                 let mut res_req: Option<Option<bool>> = None;
   1776                 let mut uv = None;
   1777                 while let Some(key) = map.next_key()? {
   1778                     match key {
   1779                         Field::AuthenticatorAttachment => {
   1780                             if attach.is_some() {
   1781                                 return Err(Error::duplicate_field(AUTHENTICATOR_ATTACHMENT));
   1782                             }
   1783                             attach = map.next_value::<Option<_>>().map(Some)?;
   1784                         }
   1785                         Field::ResidentKey => {
   1786                             if res_key.is_some() {
   1787                                 return Err(Error::duplicate_field(RESIDENT_KEY));
   1788                             }
   1789                             res_key = map.next_value::<Option<_>>().and_then(|opt| {
   1790                                 opt.map_or(Ok(Some(None)), |res| res_req.map_or(Ok(Some(opt)), |req_opt| req_opt.map_or(Ok(Some(opt)), |req| {
   1791                                     match res {
   1792                                         ResidentKeyRequirement::Required => {
   1793                                             if req {
   1794                                                 Ok(Some(opt))
   1795                                             } else {
   1796                                                 Err(Error::custom(format!("'{RESIDENT_KEY}' is '{REQUIRED}', but '{REQUIRE_RESIDENT_KEY}' is false")))
   1797                                             }
   1798                                         }
   1799                                         ResidentKeyRequirement::Discouraged | ResidentKeyRequirement::Preferred => {
   1800                                             if req {
   1801                                                 Err(Error::custom(format!("'{RESIDENT_KEY}' is not '{REQUIRED}', but '{REQUIRE_RESIDENT_KEY}' is true")))
   1802                                             } else {
   1803                                                 Ok(Some(opt))
   1804                                             }
   1805                                         }
   1806                                     }
   1807                                 })))
   1808                             })?;
   1809                         }
   1810                         Field::RequireResidentKey => {
   1811                             if res_req.is_some() {
   1812                                 return Err(Error::duplicate_field(REQUIRE_RESIDENT_KEY));
   1813                             }
   1814                             res_req = map.next_value::<Option<_>>().and_then(|opt| {
   1815                                 opt.map_or(Ok(Some(None)), |req| res_key.map_or(Ok(Some(opt)), |req_opt| req_opt.map_or(Ok(Some(opt)), |res| {
   1816                                     match res {
   1817                                         ResidentKeyRequirement::Required => {
   1818                                             if req {
   1819                                                 Ok(Some(opt))
   1820                                             } else {
   1821                                                 Err(Error::custom(format!("'{RESIDENT_KEY}' is '{REQUIRED}', but '{REQUIRE_RESIDENT_KEY}' is false")))
   1822                                             }
   1823                                         }
   1824                                         ResidentKeyRequirement::Discouraged | ResidentKeyRequirement::Preferred => {
   1825                                             if req {
   1826                                                 Err(Error::custom(format!("'{RESIDENT_KEY}' is not '{REQUIRED}', but '{REQUIRE_RESIDENT_KEY}' is true")))
   1827                                             } else {
   1828                                                 Ok(Some(opt))
   1829                                             }
   1830                                         }
   1831                                     }
   1832                                 })))
   1833                             })?;
   1834                         }
   1835                         Field::UserVerification => {
   1836                             if uv.is_some() {
   1837                                 return Err(Error::duplicate_field(USER_VERIFICATION));
   1838                             }
   1839                             uv = map.next_value::<Option<_>>().map(Some)?;
   1840                         }
   1841                     }
   1842                 }
   1843                 Ok(AuthenticatorSelectionCriteria {
   1844                     authenticator_attachment: attach.flatten().unwrap_or_default(),
   1845                     resident_key: res_key.flatten().unwrap_or_else(|| {
   1846                         if res_req.flatten().is_some_and(convert::identity) {
   1847                             ResidentKeyRequirement::Required
   1848                         } else {
   1849                             ResidentKeyRequirement::Discouraged
   1850                         }
   1851                     }),
   1852                     user_verification: uv
   1853                         .flatten()
   1854                         .unwrap_or(UserVerificationRequirement::Preferred),
   1855                 })
   1856             }
   1857         }
   1858         /// Fields for `AuthenticatorSelectionCriteria`.
   1859         const FIELDS: &[&str; 4] = &[
   1860             AUTHENTICATOR_ATTACHMENT,
   1861             RESIDENT_KEY,
   1862             REQUIRE_RESIDENT_KEY,
   1863             USER_VERIFICATION,
   1864         ];
   1865         deserializer.deserialize_struct(
   1866             "AuthenticatorSelectionCriteria",
   1867             FIELDS,
   1868             AuthenticatorSelectionCriteriaVisitor,
   1869         )
   1870     }
   1871 }
   1872 /// Helper for [`ClientCredentialCreationOptions::deserialize`].
   1873 struct Attestation;
   1874 impl<'de> Deserialize<'de> for Attestation {
   1875     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1876     where
   1877         D: Deserializer<'de>,
   1878     {
   1879         /// `Visitor` for `Attestation`.
   1880         struct AttestationVisitor;
   1881         impl Visitor<'_> for AttestationVisitor {
   1882             type Value = Attestation;
   1883             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1884                 formatter.write_str(NONE)
   1885             }
   1886             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
   1887             where
   1888                 E: Error,
   1889             {
   1890                 if v == NONE {
   1891                     Ok(Attestation)
   1892                 } else {
   1893                     Err(E::invalid_value(Unexpected::Str(v), &NONE))
   1894                 }
   1895             }
   1896         }
   1897         deserializer.deserialize_str(AttestationVisitor)
   1898     }
   1899 }
   1900 /// Helper for [`ClientCredentialCreationOptions::deserialize`].
   1901 struct AttestationFormats;
   1902 impl<'de> Deserialize<'de> for AttestationFormats {
   1903     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1904     where
   1905         D: Deserializer<'de>,
   1906     {
   1907         /// `Visitor` for `AttestationFormats`.
   1908         struct AttestationFormatsVisitor;
   1909         impl<'d> Visitor<'d> for AttestationFormatsVisitor {
   1910             type Value = AttestationFormats;
   1911             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1912                 formatter.write_str("AttestationFormats")
   1913             }
   1914             fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
   1915             where
   1916                 A: SeqAccess<'d>,
   1917             {
   1918                 seq.next_element::<Attestation>().and_then(|opt| {
   1919                     opt.map_or(Ok(AttestationFormats), |_f| {
   1920                         seq.next_element::<Attestation>().and_then(|opt2| {
   1921                             opt2.map_or(Ok(AttestationFormats), |_val| Err(Error::custom("attestationFormats must be an empty sequence or contain exactly one string whose value is 'none'")))
   1922                         })
   1923                     })
   1924                 })
   1925             }
   1926         }
   1927         deserializer.deserialize_seq(AttestationFormatsVisitor)
   1928     }
   1929 }
   1930 impl<'de> Deserialize<'de> for FourToSixtyThree {
   1931     /// Deserializes a `u8` based on [`Self::from_u8`].
   1932     ///
   1933     /// # Examples
   1934     ///
   1935     /// ```
   1936     /// # use webauthn_rp::request::register::FourToSixtyThree;
   1937     /// # use serde_json::Error;
   1938     /// assert_eq!(serde_json::from_str::<FourToSixtyThree>("4")?, FourToSixtyThree::Four);
   1939     /// assert_eq!(serde_json::from_str::<FourToSixtyThree>("63")?, FourToSixtyThree::SixtyThree);
   1940     /// assert!(serde_json::from_str::<FourToSixtyThree>("0").is_err());
   1941     /// assert!(serde_json::from_str::<FourToSixtyThree>("3").is_err());
   1942     /// assert!(serde_json::from_str::<FourToSixtyThree>("64").is_err());
   1943     /// # Ok::<_, Error>(())
   1944     /// ```
   1945     #[inline]
   1946     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1947     where
   1948         D: Deserializer<'de>,
   1949     {
   1950         u8::deserialize(deserializer).and_then(|val| {
   1951             Self::from_u8(val).ok_or_else(|| {
   1952                 Error::invalid_value(
   1953                     Unexpected::Unsigned(u64::from(val)),
   1954                     &"integer inclusively between 4 and 63",
   1955                 )
   1956             })
   1957         })
   1958     }
   1959 }
   1960 /// Similar to [`Extension`] except [`PrfInputOwned`] is used.
   1961 ///
   1962 /// This is primarily useful to assist [`ClientCredentialCreationOptions::deserialize`].
   1963 #[derive(Debug, Default)]
   1964 pub struct ExtensionOwned {
   1965     /// See [`Extension::cred_props`].
   1966     pub cred_props: Option<ExtensionReq>,
   1967     /// See [`Extension::cred_protect`].
   1968     pub cred_protect: CredProtect,
   1969     /// See [`Extension::min_pin_length`].
   1970     pub min_pin_length: Option<(FourToSixtyThree, ExtensionInfo)>,
   1971     /// See [`Extension::prf`].
   1972     pub prf: Option<PrfInputOwned>,
   1973 }
   1974 impl ExtensionOwned {
   1975     /// Returns an `Extension` based on `self`.
   1976     ///
   1977     /// Note [`PrfInputOwned::ext_req`] is converted into an [`ExtensionInfo`] such that the value is enforced.
   1978     #[inline]
   1979     #[must_use]
   1980     pub fn as_extension(&self) -> Extension<'_, '_> {
   1981         Extension {
   1982             cred_props: self.cred_props,
   1983             cred_protect: self.cred_protect,
   1984             min_pin_length: self.min_pin_length,
   1985             prf: self.prf.as_ref().map(|prf| {
   1986                 (
   1987                     PrfInput {
   1988                         first: &prf.first,
   1989                         second: prf.second.as_deref(),
   1990                     },
   1991                     if matches!(prf.ext_req, ExtensionReq::Require) {
   1992                         ExtensionInfo::RequireEnforceValue
   1993                     } else {
   1994                         ExtensionInfo::AllowEnforceValue
   1995                     },
   1996                 )
   1997             }),
   1998         }
   1999     }
   2000     /// Returns an `Extension` based on `self` and `prf`.
   2001     ///
   2002     /// Note `prf` is used _unconditionally_ regardless if [`Self::prf`] is `Some`.
   2003     #[inline]
   2004     #[must_use]
   2005     pub const fn with_prf<'prf_first, 'prf_second>(
   2006         &self,
   2007         prf: (PrfInput<'prf_first, 'prf_second>, ExtensionInfo),
   2008     ) -> Extension<'prf_first, 'prf_second> {
   2009         Extension {
   2010             cred_props: self.cred_props,
   2011             cred_protect: self.cred_protect,
   2012             min_pin_length: self.min_pin_length,
   2013             prf: Some(prf),
   2014         }
   2015     }
   2016 }
   2017 impl<'de> Deserialize<'de> for ExtensionOwned {
   2018     /// Deserializes a `struct` according to the following pseudo-schema:
   2019     ///
   2020     /// ```json
   2021     /// {
   2022     ///   "credProps": null | false | true,
   2023     ///   "credentialProtectionPolicy": null | "userVerificationOptional" | "userVerificationOptionalWithCredentialIDList" | "userVerificationRequired",
   2024     ///   "enforceCredentialProtectionPolicy": null | false | true,
   2025     ///   "minPinLength": null | false | true,
   2026     ///   "prf": null | PRFJSON
   2027     /// }
   2028     /// // PRFJSON:
   2029     /// {
   2030     ///   "eval": PRFInputs
   2031     /// }
   2032     /// // PRFInputs:
   2033     /// {
   2034     ///   "first": <base64url-encoded string>,
   2035     ///   "second": null | <base64url-encoded string>
   2036     /// }
   2037     /// ```
   2038     ///
   2039     /// where the only required fields are `"eval"` and `"first"`. Additionally `"credentialProtectionPolicy"`
   2040     /// must exist if `"enforceCredentialProtectionPolicy"` exists, and it must not be `null` if the latter
   2041     /// is not `null`. If the former is defined and not `null` but the latter is not defined or is `null`, then
   2042     /// `false` will be used for the latter. Unknown or duplicate fields lead to an error.
   2043     ///
   2044     /// All extensions are not required to have a response sent back; but _if_ a response is sent back, its value
   2045     /// will be enforced. In the case of `"minPinLength"`, [`FourToSixtyThree::Four`] will be the minimum
   2046     /// length enforced (i.e., any valid response is guaranteed to satisfy since it will have length at least
   2047     /// as large).
   2048     ///
   2049     /// Unknown or duplicate fields lead to an error.
   2050     ///
   2051     /// # Examples
   2052     ///
   2053     /// ```
   2054     /// # use webauthn_rp::request::{ExtensionInfo, ExtensionReq, register::{CredProtect, FourToSixtyThree, ser::ExtensionOwned}};
   2055     /// let ext = serde_json::from_str::<ExtensionOwned>(
   2056     ///     r#"{"credProps":true,"credentialProtectionPolicy":"userVerificationRequired","enforceCredentialProtectionPolicy":false,"minPinLength":true,"prf":{"eval":{"first":"","second":null}}}"#,
   2057     /// )?;
   2058     /// assert!(
   2059     ///     ext.cred_props
   2060     ///         .map_or(false, |req| matches!(req, ExtensionReq::Allow))
   2061     /// );
   2062     /// assert!(
   2063     ///     matches!(ext.cred_protect, CredProtect::UserVerificationRequired(enforce, info) if !enforce && matches!(info, ExtensionInfo::AllowEnforceValue))
   2064     /// );
   2065     /// assert!(ext.min_pin_length.map_or(false, |pin| pin.0 == FourToSixtyThree::Four
   2066     ///     && matches!(pin.1, ExtensionInfo::AllowEnforceValue)));
   2067     /// assert!(ext.prf.map_or(false, |prf| prf.first.is_empty()
   2068     ///     && prf.second.is_none()
   2069     ///     && matches!(prf.ext_req, ExtensionReq::Allow)));
   2070     /// # Ok::<_, serde_json::Error>(())
   2071     /// ```
   2072     #[expect(clippy::too_many_lines, reason = "want to keep logic internal")]
   2073     #[inline]
   2074     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   2075     where
   2076         D: Deserializer<'de>,
   2077     {
   2078         /// `Visitor` for `ExtensionOwned`.
   2079         struct ExtensionOwnedVisitor;
   2080         impl<'d> Visitor<'d> for ExtensionOwnedVisitor {
   2081             type Value = ExtensionOwned;
   2082             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   2083                 formatter.write_str("ExtensionOwned")
   2084             }
   2085             #[expect(clippy::too_many_lines, reason = "want to keep logic internal")]
   2086             fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
   2087             where
   2088                 A: MapAccess<'d>,
   2089             {
   2090                 /// Field for `ExtensionOwned`.
   2091                 enum Field {
   2092                     /// `credProps`.
   2093                     CredProps,
   2094                     /// `credentialProtectionPolicy`.
   2095                     CredentialProtectionPolicy,
   2096                     /// `enforceCredentialProtectionPolicy`.
   2097                     EnforceCredentialProtectionPolicy,
   2098                     /// `minPinLength`.
   2099                     MinPinLength,
   2100                     /// `prf`
   2101                     Prf,
   2102                 }
   2103                 impl<'e> Deserialize<'e> for Field {
   2104                     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   2105                     where
   2106                         D: Deserializer<'e>,
   2107                     {
   2108                         /// `Visitor` for `Field`.
   2109                         struct FieldVisitor;
   2110                         impl Visitor<'_> for FieldVisitor {
   2111                             type Value = Field;
   2112                             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   2113                                 write!(
   2114                                     formatter,
   2115                                     "'{CRED_PROPS}', '{CREDENTIAL_PROTECTION_POLICY}', '{ENFORCE_CREDENTIAL_PROTECTION_POLICY}', '{MIN_PIN_LENGTH}', or '{PRF}'"
   2116                                 )
   2117                             }
   2118                             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
   2119                             where
   2120                                 E: Error,
   2121                             {
   2122                                 match v {
   2123                                     CRED_PROPS => Ok(Field::CredProps),
   2124                                     CREDENTIAL_PROTECTION_POLICY => {
   2125                                         Ok(Field::CredentialProtectionPolicy)
   2126                                     }
   2127                                     ENFORCE_CREDENTIAL_PROTECTION_POLICY => {
   2128                                         Ok(Field::EnforceCredentialProtectionPolicy)
   2129                                     }
   2130                                     MIN_PIN_LENGTH => Ok(Field::MinPinLength),
   2131                                     PRF => Ok(Field::Prf),
   2132                                     _ => Err(E::unknown_field(v, FIELDS)),
   2133                                 }
   2134                             }
   2135                         }
   2136                         deserializer.deserialize_identifier(FieldVisitor)
   2137                     }
   2138                 }
   2139                 /// Credential protection policy values.
   2140                 #[expect(clippy::enum_variant_names, reason = "consistent with ctap names")]
   2141                 enum Policy {
   2142                     /// `userVerificationOptional`.
   2143                     UserVerificationOptional,
   2144                     /// `userVerificationOptionalWithCredentialIdList`.
   2145                     UserVerificationOptionalWithCredentialIdLisit,
   2146                     /// `userVerificationRequired`.
   2147                     UserVerificationRequired,
   2148                 }
   2149                 impl<'e> Deserialize<'e> for Policy {
   2150                     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   2151                     where
   2152                         D: Deserializer<'e>,
   2153                     {
   2154                         /// `Visitor` for `Policy`.
   2155                         struct PolicyVisitor;
   2156                         impl Visitor<'_> for PolicyVisitor {
   2157                             type Value = Policy;
   2158                             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   2159                                 write!(
   2160                                     formatter,
   2161                                     "'{USER_VERIFICATION_OPTIONAL}', '{USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST}', or '{USER_VERIFICATION_REQUIRED}'"
   2162                                 )
   2163                             }
   2164                             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
   2165                             where
   2166                                 E: Error,
   2167                             {
   2168                                 match v {
   2169                                     USER_VERIFICATION_OPTIONAL => Ok(Policy::UserVerificationOptional),
   2170                                     USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST => Ok(Policy::UserVerificationOptionalWithCredentialIdLisit),
   2171                                     USER_VERIFICATION_REQUIRED => Ok(Policy::UserVerificationRequired),
   2172                                     _ => Err(E::invalid_value(Unexpected::Str(v), &format!("'{USER_VERIFICATION_OPTIONAL}', '{USER_VERIFICATION_OPTIONAL_WITH_CREDENTIAL_ID_LIST}', or '{USER_VERIFICATION_REQUIRED}'").as_str())),
   2173                                 }
   2174                             }
   2175                         }
   2176                         deserializer.deserialize_str(PolicyVisitor)
   2177                     }
   2178                 }
   2179                 let mut props: Option<Option<bool>> = None;
   2180                 let mut policy = None;
   2181                 let mut enforce = None;
   2182                 let mut pin: Option<Option<bool>> = None;
   2183                 let mut prf_inputs = None;
   2184                 while let Some(key) = map.next_key()? {
   2185                     match key {
   2186                         Field::CredProps => {
   2187                             if props.is_some() {
   2188                                 return Err(Error::duplicate_field(CRED_PROPS));
   2189                             }
   2190                             props = map.next_value().map(Some)?;
   2191                         }
   2192                         Field::CredentialProtectionPolicy => {
   2193                             if policy.is_some() {
   2194                                 return Err(Error::duplicate_field(CREDENTIAL_PROTECTION_POLICY));
   2195                             }
   2196                             policy = map.next_value::<Option<Policy>>().map(Some)?;
   2197                         }
   2198                         Field::EnforceCredentialProtectionPolicy => {
   2199                             if enforce.is_some() {
   2200                                 return Err(Error::duplicate_field(
   2201                                     ENFORCE_CREDENTIAL_PROTECTION_POLICY,
   2202                                 ));
   2203                             }
   2204                             enforce = map.next_value::<Option<_>>().map(Some)?;
   2205                         }
   2206                         Field::MinPinLength => {
   2207                             if pin.is_some() {
   2208                                 return Err(Error::duplicate_field(MIN_PIN_LENGTH));
   2209                             }
   2210                             pin = map.next_value().map(Some)?;
   2211                         }
   2212                         Field::Prf => {
   2213                             if prf_inputs.is_some() {
   2214                                 return Err(Error::duplicate_field(PRF));
   2215                             }
   2216                             prf_inputs = map
   2217                                 .next_value::<Option<PrfHelper>>()
   2218                                 .map(|opt| Some(opt.map(|p| p.0)))?;
   2219                         }
   2220                     }
   2221                 }
   2222                 policy.map_or_else(
   2223                     || {
   2224                         if enforce.is_some() {
   2225                             Err(Error::custom(format!("'{ENFORCE_CREDENTIAL_PROTECTION_POLICY}' must not exist when '{CREDENTIAL_PROTECTION_POLICY}' does not exist")))
   2226                         } else {
   2227                             Ok(CredProtect::None)
   2228                         }
   2229                     },
   2230                     |opt_policy| opt_policy.map_or_else(
   2231                         || {
   2232                             if enforce.is_some_and(|opt| opt.is_some()) {
   2233                                 Err(Error::custom(format!("'{ENFORCE_CREDENTIAL_PROTECTION_POLICY}' must be null or not exist when '{CREDENTIAL_PROTECTION_POLICY}' is null")))
   2234                             } else {
   2235                                 Ok(CredProtect::None)
   2236                             }
   2237                         },
   2238                         |cred_policy| {
   2239                             match cred_policy {
   2240                                 Policy::UserVerificationOptional => Ok(CredProtect::UserVerificationOptional(enforce.flatten().unwrap_or_default(), ExtensionInfo::AllowEnforceValue)),
   2241                                 Policy::UserVerificationOptionalWithCredentialIdLisit => Ok(CredProtect::UserVerificationOptionalWithCredentialIdList(enforce.flatten().unwrap_or_default(), ExtensionInfo::AllowEnforceValue)),
   2242                                 Policy::UserVerificationRequired => Ok(CredProtect::UserVerificationRequired(enforce.flatten().unwrap_or_default(), ExtensionInfo::AllowEnforceValue)),
   2243                             }
   2244                         }
   2245                     ),
   2246                 ).map(|cred_protect| {
   2247                     ExtensionOwned { cred_props: props.flatten().and_then(|p| p.then_some(ExtensionReq::Allow)), cred_protect, min_pin_length: pin.flatten().and_then(|m| m.then_some((FourToSixtyThree::Four, ExtensionInfo::AllowEnforceValue))), prf: prf_inputs.flatten(), }
   2248                 })
   2249             }
   2250         }
   2251         /// Fields for `ExtensionOwned`.
   2252         const FIELDS: &[&str; 5] = &[
   2253             CRED_PROPS,
   2254             CREDENTIAL_PROTECTION_POLICY,
   2255             ENFORCE_CREDENTIAL_PROTECTION_POLICY,
   2256             MIN_PIN_LENGTH,
   2257             PRF,
   2258         ];
   2259         deserializer.deserialize_struct("ExtensionOwned", FIELDS, ExtensionOwnedVisitor)
   2260     }
   2261 }
   2262 /// Similar to [`PublicKeyCredentialCreationOptions`] except the fields are based on owned data, and
   2263 /// [`Self::rp_id`] is optional.
   2264 ///
   2265 /// This is primarily useful to assist [`ClientCredentialCreationOptions::deserialize`].
   2266 #[derive(Debug)]
   2267 pub struct PublicKeyCredentialCreationOptionsOwned<const USER_LEN: usize> {
   2268     /// See [`PublicKeyCredentialCreationOptions::rp_id`].
   2269     pub rp_id: Option<RpId>,
   2270     /// See [`PublicKeyCredentialCreationOptions::user`].
   2271     pub user: PublicKeyCredentialUserEntityOwned<USER_LEN>,
   2272     /// See [`PublicKeyCredentialCreationOptions::pub_key_cred_params`].
   2273     pub pub_key_cred_params: CoseAlgorithmIdentifiers,
   2274     /// See [`PublicKeyCredentialCreationOptions::timeout`].
   2275     pub timeout: NonZeroU32,
   2276     /// See [`PublicKeyCredentialCreationOptions::authenticator_selection`].
   2277     pub authenticator_selection: AuthenticatorSelectionCriteria,
   2278     /// See [`PublicKeyCredentialCreationOptions::hints`].
   2279     pub hints: Hints,
   2280     /// See [`PublicKeyCredentialCreationOptions::extensions`].
   2281     pub extensions: ExtensionOwned,
   2282 }
   2283 /// Error returned when converting a [`PublicKeyCredentialCreationOptionsOwned`] into a
   2284 /// [`PublicKeyCredentialCreationOptions`] (e.g., via [`PublicKeyCredentialCreationOptionsOwned::with_rp_id`]).
   2285 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
   2286 pub enum PublicKeyCredentialCreationOptionsOwnedErr {
   2287     /// Variant returned when [`PublicKeyCredentialCreationOptionsOwned::rp_id`] is `None`.
   2288     MissingRpId,
   2289     /// Variant returned when [`PublicKeyCredentialCreationOptionsOwned::user`] cannot be converted into a
   2290     /// a [`PublicKeyCredentialCreationOptions`].
   2291     UserEntity(PublicKeyCredentialUserEntityOwnedErr),
   2292 }
   2293 impl Display for PublicKeyCredentialCreationOptionsOwnedErr {
   2294     #[inline]
   2295     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
   2296         match *self {
   2297             Self::MissingRpId => f.write_str("creation options did not have an RP ID"),
   2298             Self::UserEntity(err) => err.fmt(f),
   2299         }
   2300     }
   2301 }
   2302 impl E for PublicKeyCredentialCreationOptionsOwnedErr {}
   2303 impl From<PublicKeyCredentialUserEntityOwnedErr> for PublicKeyCredentialCreationOptionsOwnedErr {
   2304     #[inline]
   2305     fn from(value: PublicKeyCredentialUserEntityOwnedErr) -> Self {
   2306         Self::UserEntity(value)
   2307     }
   2308 }
   2309 impl<const USER_LEN: usize> PublicKeyCredentialCreationOptionsOwned<USER_LEN> {
   2310     /// Returns a `PublicKeyCredentialCreationOptions` based on `self` and `exclude_credentials`.
   2311     ///
   2312     /// # Errors
   2313     ///
   2314     /// Errors iff [`Self::rp_id`] is `None` or [`PublicKeyCredentialUserEntityOwned::as_entity`] errors.
   2315     #[inline]
   2316     pub fn as_options(
   2317         &self,
   2318         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   2319     ) -> Result<
   2320         PublicKeyCredentialCreationOptions<'_, '_, '_, '_, '_, '_, USER_LEN>,
   2321         PublicKeyCredentialCreationOptionsOwnedErr,
   2322     > {
   2323         self.rp_id
   2324             .as_ref()
   2325             .ok_or(PublicKeyCredentialCreationOptionsOwnedErr::MissingRpId)
   2326             .and_then(|rp_id| {
   2327                 self.user
   2328                     .as_entity()
   2329                     .map_err(PublicKeyCredentialCreationOptionsOwnedErr::UserEntity)
   2330                     .map(|user| PublicKeyCredentialCreationOptions {
   2331                         rp_id,
   2332                         user,
   2333                         challenge: Challenge::new(),
   2334                         pub_key_cred_params: self.pub_key_cred_params,
   2335                         timeout: self.timeout,
   2336                         exclude_credentials,
   2337                         authenticator_selection: self.authenticator_selection,
   2338                         hints: self.hints,
   2339                         extensions: self.extensions.as_extension(),
   2340                     })
   2341             })
   2342     }
   2343     /// Returns a `PublicKeyCredentialCreationOptions` based on `self`, `exclude_credentials`, and `rp_id`.
   2344     ///
   2345     /// Note `rp_id` is used _unconditionally_ regardless if [`Self::rp_id`] is `Some`.
   2346     ///
   2347     /// # Errors
   2348     ///
   2349     /// Errors iff [`PublicKeyCredentialUserEntityOwned::as_entity`] errors.
   2350     #[inline]
   2351     pub fn with_rp_id<'rp_id>(
   2352         &self,
   2353         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   2354         rp_id: &'rp_id RpId,
   2355     ) -> Result<
   2356         PublicKeyCredentialCreationOptions<'rp_id, '_, '_, '_, '_, '_, USER_LEN>,
   2357         PublicKeyCredentialCreationOptionsOwnedErr,
   2358     > {
   2359         self.user
   2360             .as_entity()
   2361             .map_err(PublicKeyCredentialCreationOptionsOwnedErr::UserEntity)
   2362             .map(|user| PublicKeyCredentialCreationOptions {
   2363                 rp_id,
   2364                 user,
   2365                 challenge: Challenge::new(),
   2366                 pub_key_cred_params: self.pub_key_cred_params,
   2367                 timeout: self.timeout,
   2368                 exclude_credentials,
   2369                 authenticator_selection: self.authenticator_selection,
   2370                 hints: self.hints,
   2371                 extensions: self.extensions.as_extension(),
   2372             })
   2373     }
   2374     /// Returns a `PublicKeyCredentialCreationOptions` based on `self`, `exclude_credentials`, and `user`.
   2375     ///
   2376     /// Note `user` is used _unconditionally_ regardless of what [`Self::user`] is.
   2377     ///
   2378     /// # Errors
   2379     ///
   2380     /// Errors iff [`Self::rp_id`] is `None`.
   2381     #[inline]
   2382     pub fn with_user<'user_name, 'user_display_name, 'user_id>(
   2383         &self,
   2384         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   2385         user: PublicKeyCredentialUserEntity<'user_name, 'user_display_name, 'user_id, USER_LEN>,
   2386     ) -> Result<
   2387         PublicKeyCredentialCreationOptions<
   2388             '_,
   2389             'user_name,
   2390             'user_display_name,
   2391             'user_id,
   2392             '_,
   2393             '_,
   2394             USER_LEN,
   2395         >,
   2396         PublicKeyCredentialCreationOptionsOwnedErr,
   2397     > {
   2398         self.rp_id
   2399             .as_ref()
   2400             .ok_or(PublicKeyCredentialCreationOptionsOwnedErr::MissingRpId)
   2401             .map(|rp_id| PublicKeyCredentialCreationOptions {
   2402                 rp_id,
   2403                 user,
   2404                 challenge: Challenge::new(),
   2405                 pub_key_cred_params: self.pub_key_cred_params,
   2406                 timeout: self.timeout,
   2407                 exclude_credentials,
   2408                 authenticator_selection: self.authenticator_selection,
   2409                 hints: self.hints,
   2410                 extensions: self.extensions.as_extension(),
   2411             })
   2412     }
   2413     /// Returns a `PublicKeyCredentialCreationOptions` based on `self`, `exclude_credentials`, and `extensions`.
   2414     ///
   2415     /// Note `extensions` is used _unconditionally_ regardless of what [`Self::extensions`] is.
   2416     ///
   2417     /// # Errors
   2418     ///
   2419     /// Errors iff [`Self::rp_id`] is `None` or [`PublicKeyCredentialUserEntityOwned::as_entity`] errors.
   2420     #[inline]
   2421     pub fn with_extensions<'prf_first, 'prf_second>(
   2422         &self,
   2423         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   2424         extensions: Extension<'prf_first, 'prf_second>,
   2425     ) -> Result<
   2426         PublicKeyCredentialCreationOptions<'_, '_, '_, '_, 'prf_first, 'prf_second, USER_LEN>,
   2427         PublicKeyCredentialCreationOptionsOwnedErr,
   2428     > {
   2429         self.rp_id
   2430             .as_ref()
   2431             .ok_or(PublicKeyCredentialCreationOptionsOwnedErr::MissingRpId)
   2432             .and_then(|rp_id| {
   2433                 self.user
   2434                     .as_entity()
   2435                     .map_err(PublicKeyCredentialCreationOptionsOwnedErr::UserEntity)
   2436                     .map(|user| PublicKeyCredentialCreationOptions {
   2437                         rp_id,
   2438                         user,
   2439                         challenge: Challenge::new(),
   2440                         pub_key_cred_params: self.pub_key_cred_params,
   2441                         timeout: self.timeout,
   2442                         exclude_credentials,
   2443                         authenticator_selection: self.authenticator_selection,
   2444                         hints: self.hints,
   2445                         extensions,
   2446                     })
   2447             })
   2448     }
   2449     /// Returns a `PublicKeyCredentialCreationOptions` based on `self`, `exclude_credentials`, `rp_id`, and `user`.
   2450     ///
   2451     /// Note `rp_id` and `user` are used _unconditionally_ regardless if [`Self::rp_id`] is `Some` or what
   2452     /// [`Self::user`] is.
   2453     #[inline]
   2454     #[must_use]
   2455     pub fn with_rp_id_and_user<'rp_id, 'user_name, 'user_display_name, 'user_id>(
   2456         &self,
   2457         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   2458         rp_id: &'rp_id RpId,
   2459         user: PublicKeyCredentialUserEntity<'user_name, 'user_display_name, 'user_id, USER_LEN>,
   2460     ) -> PublicKeyCredentialCreationOptions<
   2461         'rp_id,
   2462         'user_name,
   2463         'user_display_name,
   2464         'user_id,
   2465         '_,
   2466         '_,
   2467         USER_LEN,
   2468     > {
   2469         PublicKeyCredentialCreationOptions {
   2470             rp_id,
   2471             user,
   2472             challenge: Challenge::new(),
   2473             pub_key_cred_params: self.pub_key_cred_params,
   2474             timeout: self.timeout,
   2475             exclude_credentials,
   2476             authenticator_selection: self.authenticator_selection,
   2477             hints: self.hints,
   2478             extensions: self.extensions.as_extension(),
   2479         }
   2480     }
   2481     /// Returns a `PublicKeyCredentialCreationOptions` based on `self`, `exclude_credentials`, `rp_id`, and
   2482     /// `extensions`.
   2483     ///
   2484     /// Note `rp_id` and `extensions` are used _unconditionally_ regardless if [`Self::rp_id`] is `Some` or what
   2485     /// [`Self::extensions`] is.
   2486     ///
   2487     /// # Errors
   2488     ///
   2489     /// Errors iff [`PublicKeyCredentialUserEntityOwned::as_entity`] errors.
   2490     #[inline]
   2491     pub fn with_rp_id_and_extensions<'rp_id, 'prf_first, 'prf_second>(
   2492         &self,
   2493         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   2494         rp_id: &'rp_id RpId,
   2495         extensions: Extension<'prf_first, 'prf_second>,
   2496     ) -> Result<
   2497         PublicKeyCredentialCreationOptions<'rp_id, '_, '_, '_, 'prf_first, 'prf_second, USER_LEN>,
   2498         PublicKeyCredentialCreationOptionsOwnedErr,
   2499     > {
   2500         self.user
   2501             .as_entity()
   2502             .map_err(PublicKeyCredentialCreationOptionsOwnedErr::UserEntity)
   2503             .map(|user| PublicKeyCredentialCreationOptions {
   2504                 rp_id,
   2505                 user,
   2506                 challenge: Challenge::new(),
   2507                 pub_key_cred_params: self.pub_key_cred_params,
   2508                 timeout: self.timeout,
   2509                 exclude_credentials,
   2510                 authenticator_selection: self.authenticator_selection,
   2511                 hints: self.hints,
   2512                 extensions,
   2513             })
   2514     }
   2515     /// Returns a `PublicKeyCredentialCreationOptions` based on `self`, `exclude_credentials`, `user`, and
   2516     /// `extensions`.
   2517     ///
   2518     /// Note `user` and `extensions` are used _unconditionally_ regardless of what the values of [`Self::user`]
   2519     /// or [`Self::extensions`] are.
   2520     ///
   2521     /// # Errors
   2522     ///
   2523     /// Errors iff [`Self::rp_id`] is `None`.
   2524     #[inline]
   2525     pub fn with_user_and_extensions<
   2526         'user_name,
   2527         'user_display_name,
   2528         'user_id,
   2529         'prf_first,
   2530         'prf_second,
   2531     >(
   2532         &self,
   2533         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   2534         user: PublicKeyCredentialUserEntity<'user_name, 'user_display_name, 'user_id, USER_LEN>,
   2535         extensions: Extension<'prf_first, 'prf_second>,
   2536     ) -> Result<
   2537         PublicKeyCredentialCreationOptions<
   2538             '_,
   2539             'user_name,
   2540             'user_display_name,
   2541             'user_id,
   2542             'prf_first,
   2543             'prf_second,
   2544             USER_LEN,
   2545         >,
   2546         PublicKeyCredentialCreationOptionsOwnedErr,
   2547     > {
   2548         self.rp_id
   2549             .as_ref()
   2550             .ok_or(PublicKeyCredentialCreationOptionsOwnedErr::MissingRpId)
   2551             .map(|rp_id| PublicKeyCredentialCreationOptions {
   2552                 rp_id,
   2553                 user,
   2554                 challenge: Challenge::new(),
   2555                 pub_key_cred_params: self.pub_key_cred_params,
   2556                 timeout: self.timeout,
   2557                 exclude_credentials,
   2558                 authenticator_selection: self.authenticator_selection,
   2559                 hints: self.hints,
   2560                 extensions,
   2561             })
   2562     }
   2563     /// Returns a `PublicKeyCredentialCreationOptions` based on `self`, `exclude_credentials`, `rp_id`, `user`,
   2564     /// and `extensions`.
   2565     ///
   2566     /// Note `rp_id`, `user`, and `extensions` are used _unconditionally_ regardless if [`Self::rp_id`] is `Some`
   2567     /// or what the values of [`Self::user`] and [`Self::extensions`] are.
   2568     #[inline]
   2569     #[must_use]
   2570     pub fn with_rp_id_user_and_extensions<
   2571         'rp_id,
   2572         'user_name,
   2573         'user_display_name,
   2574         'user_id,
   2575         'prf_first,
   2576         'prf_second,
   2577     >(
   2578         &self,
   2579         exclude_credentials: Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
   2580         rp_id: &'rp_id RpId,
   2581         user: PublicKeyCredentialUserEntity<'user_name, 'user_display_name, 'user_id, USER_LEN>,
   2582         extensions: Extension<'prf_first, 'prf_second>,
   2583     ) -> PublicKeyCredentialCreationOptions<
   2584         'rp_id,
   2585         'user_name,
   2586         'user_display_name,
   2587         'user_id,
   2588         'prf_first,
   2589         'prf_second,
   2590         USER_LEN,
   2591     > {
   2592         PublicKeyCredentialCreationOptions {
   2593             rp_id,
   2594             user,
   2595             challenge: Challenge::new(),
   2596             pub_key_cred_params: self.pub_key_cred_params,
   2597             timeout: self.timeout,
   2598             exclude_credentials,
   2599             authenticator_selection: self.authenticator_selection,
   2600             hints: self.hints,
   2601             extensions,
   2602         }
   2603     }
   2604 }
   2605 impl<const USER_LEN: usize> Default for PublicKeyCredentialCreationOptionsOwned<USER_LEN> {
   2606     #[inline]
   2607     fn default() -> Self {
   2608         Self {
   2609             rp_id: None,
   2610             user: PublicKeyCredentialUserEntityOwned::default(),
   2611             pub_key_cred_params: CoseAlgorithmIdentifiers::default(),
   2612             timeout: FIVE_MINUTES,
   2613             authenticator_selection: AuthenticatorSelectionCriteria {
   2614                 authenticator_attachment: AuthenticatorAttachment::default(),
   2615                 resident_key: ResidentKeyRequirement::Discouraged,
   2616                 user_verification: UserVerificationRequirement::Preferred,
   2617             },
   2618             hints: Hints::EMPTY,
   2619             extensions: ExtensionOwned::default(),
   2620         }
   2621     }
   2622 }
   2623 impl<'de, const USER_LEN: usize> Deserialize<'de>
   2624     for PublicKeyCredentialCreationOptionsOwned<USER_LEN>
   2625 where
   2626     PublicKeyCredentialUserEntityOwned<USER_LEN>: Deserialize<'de>,
   2627 {
   2628     /// Deserializes a `struct` based on
   2629     /// [`PublicKeyCredentialCreationOptionsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialcreationoptionsjson).
   2630     ///
   2631     /// Note that none of the fields are required, and all are allowed to be `null`.
   2632     /// [`authenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorselectioncriteria-authenticatorattachment)
   2633     /// must be consistent with
   2634     /// [`hints`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptionsjson-hints)
   2635     /// (e.g., if [`"platform"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattachment-platform) is
   2636     /// requested, then `hints` must either not exist, be `null`, be empty, or be `["client-device"]`).
   2637     ///
   2638     /// If [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptionsjson-challenge)
   2639     /// exists, it must be `null`. If
   2640     /// [`excludeCredentials`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptionsjson-excludecredentials)
   2641     /// exists, it must be `null` or empty. If
   2642     /// [`attestation`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptionsjson-attestation)
   2643     /// exists, it must be `null`or `"none"`. If
   2644     /// [`attestationFormats`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptionsjson-attestationformats)
   2645     /// exists, it must be `null`, empty, or `["none"]`.
   2646     ///
   2647     /// If [`timeout`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptionsjson-timeout) exists,
   2648     /// it must be `null` or positive. When it does not exist or is `null`, [`FIVE_MINUTES`] will be used.
   2649     ///
   2650     /// Fields that are missing or `null` will be replaced with their corresponding [`Default`] value.
   2651     ///
   2652     /// Unknown or duplicate fields lead to an error.
   2653     #[expect(clippy::too_many_lines, reason = "want to keep logic internal")]
   2654     #[inline]
   2655     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   2656     where
   2657         D: Deserializer<'de>,
   2658     {
   2659         /// `Visitor` for `PublicKeyCredentialCreationOptionsOwned`.
   2660         struct PublicKeyCredentialCreationOptionsOwnedVisitor<const LEN: usize>;
   2661         impl<'d, const LEN: usize> Visitor<'d> for PublicKeyCredentialCreationOptionsOwnedVisitor<LEN>
   2662         where
   2663             PublicKeyCredentialUserEntityOwned<LEN>: Deserialize<'d>,
   2664         {
   2665             type Value = PublicKeyCredentialCreationOptionsOwned<LEN>;
   2666             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   2667                 formatter.write_str("PublicKeyCredentialCreationOptionsOwned")
   2668             }
   2669             #[expect(clippy::too_many_lines, reason = "want to keep logic internal")]
   2670             fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
   2671             where
   2672                 A: MapAccess<'d>,
   2673             {
   2674                 /// Field for `PublicKeyCredentialCreationOptionsOwned`.
   2675                 enum Field {
   2676                     /// `rp`.
   2677                     Rp,
   2678                     /// `user`.
   2679                     User,
   2680                     /// `challenge`.
   2681                     Challenge,
   2682                     /// `pubKeyCredParams`.
   2683                     PubKeyCredParams,
   2684                     /// `timeout`.
   2685                     Timeout,
   2686                     /// `excludeCredentials`.
   2687                     ExcludeCredentials,
   2688                     /// `authenticatorSelection`.
   2689                     AuthenticatorSelection,
   2690                     /// `hints`.
   2691                     Hints,
   2692                     /// `extensions`.
   2693                     Extensions,
   2694                     /// `attestation`.
   2695                     Attestation,
   2696                     /// `attestationFormats`.
   2697                     AttestationFormats,
   2698                 }
   2699                 impl<'e> Deserialize<'e> for Field {
   2700                     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   2701                     where
   2702                         D: Deserializer<'e>,
   2703                     {
   2704                         /// `Visitor` for `Field`.
   2705                         struct FieldVisitor;
   2706                         impl Visitor<'_> for FieldVisitor {
   2707                             type Value = Field;
   2708                             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   2709                                 write!(
   2710                                     formatter,
   2711                                     "'{RP}', '{USER}', '{CHALLENGE}', '{PUB_KEY_CRED_PARAMS}', '{TIMEOUT}', '{EXCLUDE_CREDENTIALS}', '{AUTHENTICATOR_SELECTION}', '{HINTS}', '{EXTENSIONS}', '{ATTESTATION}', or '{ATTESTATION_FORMATS}'"
   2712                                 )
   2713                             }
   2714                             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
   2715                             where
   2716                                 E: Error,
   2717                             {
   2718                                 match v {
   2719                                     RP => Ok(Field::Rp),
   2720                                     USER => Ok(Field::User),
   2721                                     CHALLENGE => Ok(Field::Challenge),
   2722                                     PUB_KEY_CRED_PARAMS => Ok(Field::PubKeyCredParams),
   2723                                     TIMEOUT => Ok(Field::Timeout),
   2724                                     EXCLUDE_CREDENTIALS => Ok(Field::ExcludeCredentials),
   2725                                     AUTHENTICATOR_SELECTION => Ok(Field::AuthenticatorSelection),
   2726                                     HINTS => Ok(Field::Hints),
   2727                                     EXTENSIONS => Ok(Field::Extensions),
   2728                                     ATTESTATION => Ok(Field::Attestation),
   2729                                     ATTESTATION_FORMATS => Ok(Field::AttestationFormats),
   2730                                     _ => Err(E::unknown_field(v, FIELDS)),
   2731                                 }
   2732                             }
   2733                         }
   2734                         deserializer.deserialize_identifier(FieldVisitor)
   2735                     }
   2736                 }
   2737                 let mut rp = None;
   2738                 let mut user_info = None;
   2739                 let mut chall = None;
   2740                 let mut params = None;
   2741                 let mut time = None;
   2742                 let mut exclude = None;
   2743                 let mut auth = None;
   2744                 let mut hint = None;
   2745                 let mut ext = None;
   2746                 let mut attest = None;
   2747                 let mut formats = None;
   2748                 while let Some(key) = map.next_key()? {
   2749                     match key {
   2750                         Field::Rp => {
   2751                             if rp.is_some() {
   2752                                 return Err(Error::duplicate_field(RP));
   2753                             }
   2754                             rp = map
   2755                                 .next_value::<Option<PublicKeyCredentialRpEntityHelper>>()
   2756                                 .map(|opt| opt.map(|val| val.0))
   2757                                 .map(Some)?;
   2758                         }
   2759                         Field::User => {
   2760                             if user_info.is_some() {
   2761                                 return Err(Error::duplicate_field(USER));
   2762                             }
   2763                             user_info = map.next_value::<Option<_>>().map(Some)?;
   2764                         }
   2765                         Field::Challenge => {
   2766                             if chall.is_some() {
   2767                                 return Err(Error::duplicate_field(CHALLENGE));
   2768                             }
   2769                             chall = map.next_value::<Null>().map(Some)?;
   2770                         }
   2771                         Field::PubKeyCredParams => {
   2772                             if params.is_some() {
   2773                                 return Err(Error::duplicate_field(PUB_KEY_CRED_PARAMS));
   2774                             }
   2775                             params = map.next_value::<Option<_>>().map(Some)?;
   2776                         }
   2777                         Field::Timeout => {
   2778                             if time.is_some() {
   2779                                 return Err(Error::duplicate_field(TIMEOUT));
   2780                             }
   2781                             time = map.next_value::<Option<_>>().map(Some)?;
   2782                         }
   2783                         Field::ExcludeCredentials => {
   2784                             if exclude.is_some() {
   2785                                 return Err(Error::duplicate_field(EXCLUDE_CREDENTIALS));
   2786                             }
   2787                             exclude = map.next_value::<Option<[(); 0]>>().map(Some)?;
   2788                         }
   2789                         Field::AuthenticatorSelection => {
   2790                             if auth.is_some() {
   2791                                 return Err(Error::duplicate_field(AUTHENTICATOR_SELECTION));
   2792                             }
   2793                             auth = map.next_value::<Option<_>>().map(Some)?;
   2794                         }
   2795                         Field::Hints => {
   2796                             if hint.is_some() {
   2797                                 return Err(Error::duplicate_field(HINTS));
   2798                             }
   2799                             hint = map.next_value::<Option<_>>().map(Some)?;
   2800                         }
   2801                         Field::Extensions => {
   2802                             if ext.is_some() {
   2803                                 return Err(Error::duplicate_field(EXTENSIONS));
   2804                             }
   2805                             ext = map.next_value::<Option<_>>().map(Some)?;
   2806                         }
   2807                         Field::Attestation => {
   2808                             if attest.is_some() {
   2809                                 return Err(Error::duplicate_field(ATTESTATION));
   2810                             }
   2811                             attest = map.next_value::<Option<Attestation>>().map(Some)?;
   2812                         }
   2813                         Field::AttestationFormats => {
   2814                             if formats.is_some() {
   2815                                 return Err(Error::duplicate_field(ATTESTATION_FORMATS));
   2816                             }
   2817                             formats = map.next_value::<Option<AttestationFormats>>().map(Some)?;
   2818                         }
   2819                     }
   2820                 }
   2821                 Ok(PublicKeyCredentialCreationOptionsOwned {
   2822                     rp_id: rp.flatten().flatten(),
   2823                     user: user_info.flatten().unwrap_or_default(),
   2824                     pub_key_cred_params: params.flatten().unwrap_or_default(),
   2825                     timeout: time.flatten().unwrap_or(FIVE_MINUTES),
   2826                     authenticator_selection: auth.flatten().unwrap_or(
   2827                         AuthenticatorSelectionCriteria {
   2828                             authenticator_attachment: AuthenticatorAttachment::None,
   2829                             resident_key: ResidentKeyRequirement::Discouraged,
   2830                             user_verification: UserVerificationRequirement::Preferred,
   2831                         },
   2832                     ),
   2833                     hints: hint.flatten().unwrap_or_default(),
   2834                     extensions: ext.flatten().unwrap_or_default(),
   2835                 })
   2836             }
   2837         }
   2838         /// Fields for `PublicKeyCredentialCreationOptionsOwned`.
   2839         const FIELDS: &[&str; 11] = &[
   2840             RP,
   2841             USER,
   2842             CHALLENGE,
   2843             PUB_KEY_CRED_PARAMS,
   2844             TIMEOUT,
   2845             EXCLUDE_CREDENTIALS,
   2846             AUTHENTICATOR_SELECTION,
   2847             HINTS,
   2848             EXTENSIONS,
   2849             ATTESTATION,
   2850             ATTESTATION_FORMATS,
   2851         ];
   2852         deserializer.deserialize_struct(
   2853             "PublicKeyCredentialCreationOptionsOwned",
   2854             FIELDS,
   2855             PublicKeyCredentialCreationOptionsOwnedVisitor,
   2856         )
   2857     }
   2858 }
   2859 /// Deserializes client-supplied data to assist in the creation of [`CredentialCreationOptions`].
   2860 ///
   2861 /// It's common to tailor a registration ceremony based on a user's environment. The options that should be
   2862 /// used are then sent to the server. For example, [`CredentialMediationRequirement::Conditional`] ceremonies
   2863 /// typically work best for [`AuthenticatorAttachment::Platform`] authenticators; a subset of which cannot
   2864 /// rely on [`UserVerificationRequirement::Required`]. Unfortunately one may not want to use
   2865 /// [`UserVerificationRequirement::Preferred`] unconditionally either since security keys may benefit from
   2866 /// [`CredProtect::UserVerificationRequired`] which can typically only be used when
   2867 /// [`UserVerificationRequirement::Required`] is requested since many user agents error otherwise.
   2868 ///
   2869 /// To facilitate this, [`Self::deserialize`] can be used to deserialize the data sent from the client.
   2870 #[derive(Debug)]
   2871 pub struct ClientCredentialCreationOptions<const USER_LEN: usize> {
   2872     /// See [`CredentialCreationOptions::mediation`].
   2873     pub mediation: CredentialMediationRequirement,
   2874     /// See [`CredentialCreationOptions::public_key`].
   2875     pub public_key: PublicKeyCredentialCreationOptionsOwned<USER_LEN>,
   2876 }
   2877 impl<'de, const USER_LEN: usize> Deserialize<'de> for ClientCredentialCreationOptions<USER_LEN>
   2878 where
   2879     PublicKeyCredentialCreationOptionsOwned<USER_LEN>: Deserialize<'de>,
   2880 {
   2881     /// Deserializes a `struct` according to the following pseudo-schema:
   2882     ///
   2883     /// ```json
   2884     /// {
   2885     ///   "mediation": null | "required" | "conditional",
   2886     ///   "publicKey": null | <PublicKeyCredentialCreationOptionsOwned>
   2887     /// }
   2888     /// ```
   2889     ///
   2890     /// where none of the fields are required and `"publicKey"` is deserialized according to
   2891     /// [`PublicKeyCredentialCreationOptionsOwned::deserialize`]. If any field is missing or is `null`, then
   2892     /// the corresponding [`Default`] `impl` will be used.
   2893     ///
   2894     /// Unknown or duplicate fields lead to an error.
   2895     #[inline]
   2896     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   2897     where
   2898         D: Deserializer<'de>,
   2899     {
   2900         /// `Visitor` for `ClientCredentialCreationOptions`.
   2901         struct ClientCredentialCreationOptionsVisitor<const LEN: usize>;
   2902         impl<'d, const LEN: usize> Visitor<'d> for ClientCredentialCreationOptionsVisitor<LEN>
   2903         where
   2904             PublicKeyCredentialCreationOptionsOwned<LEN>: Deserialize<'d>,
   2905         {
   2906             type Value = ClientCredentialCreationOptions<LEN>;
   2907             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   2908                 formatter.write_str("ClientCredentialCreationOptions")
   2909             }
   2910             fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
   2911             where
   2912                 A: MapAccess<'d>,
   2913             {
   2914                 /// Field in `ClientCredentialCreationOptions`.
   2915                 enum Field {
   2916                     /// `mediation`.
   2917                     Mediation,
   2918                     /// `publicKey`
   2919                     PublicKey,
   2920                 }
   2921                 impl<'e> Deserialize<'e> for Field {
   2922                     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   2923                     where
   2924                         D: Deserializer<'e>,
   2925                     {
   2926                         /// `Visitor` for `Field`.
   2927                         struct FieldVisitor;
   2928                         impl Visitor<'_> for FieldVisitor {
   2929                             type Value = Field;
   2930                             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   2931                                 write!(formatter, "'{MEDIATION}' or '{PUBLIC_KEY_NO_HYPEN}'")
   2932                             }
   2933                             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
   2934                             where
   2935                                 E: Error,
   2936                             {
   2937                                 match v {
   2938                                     MEDIATION => Ok(Field::Mediation),
   2939                                     PUBLIC_KEY_NO_HYPEN => Ok(Field::PublicKey),
   2940                                     _ => Err(E::unknown_field(v, FIELDS)),
   2941                                 }
   2942                             }
   2943                         }
   2944                         deserializer.deserialize_identifier(FieldVisitor)
   2945                     }
   2946                 }
   2947                 let mut med = None;
   2948                 let mut key = None;
   2949                 while let Some(k) = map.next_key()? {
   2950                     match k {
   2951                         Field::Mediation => {
   2952                             if med.is_some() {
   2953                                 return Err(Error::duplicate_field(MEDIATION));
   2954                             }
   2955                             med = map.next_value::<Option<_>>().map(Some)?;
   2956                         }
   2957                         Field::PublicKey => {
   2958                             if key.is_some() {
   2959                                 return Err(Error::duplicate_field(PUBLIC_KEY_NO_HYPEN));
   2960                             }
   2961                             key = map.next_value::<Option<_>>().map(Some)?;
   2962                         }
   2963                     }
   2964                 }
   2965                 Ok(ClientCredentialCreationOptions {
   2966                     mediation: med.flatten().unwrap_or_default(),
   2967                     public_key: key.flatten().unwrap_or_default(),
   2968                 })
   2969             }
   2970         }
   2971         /// Fields for `ClientCredentialCreationOptions`.
   2972         const FIELDS: &[&str; 2] = &[MEDIATION, PUBLIC_KEY_NO_HYPEN];
   2973         deserializer.deserialize_struct(
   2974             "ClientCredentialCreationOptions",
   2975             FIELDS,
   2976             ClientCredentialCreationOptionsVisitor,
   2977         )
   2978     }
   2979 }