webauthn_rp

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

ser.rs (181589B)


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