webauthn_rp

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

ser.rs (165703B)


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