webauthn_rp

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

ser.rs (49055B)


      1 #![expect(
      2     clippy::question_mark_used,
      3     reason = "noisy, opinionated, and likely doesn't prevent bugs or improve APIs"
      4 )]
      5 extern crate alloc;
      6 use super::{
      7     AllAcceptedCredentialsOptions, AuthTransports, AuthenticatorAttachment, AuthenticatorTransport,
      8     Challenge, CredentialId, CurrentUserDetailsOptions, Origin, SentChallenge,
      9 };
     10 use alloc::borrow::Cow;
     11 use core::{
     12     fmt::{self, Formatter},
     13     marker::PhantomData,
     14 };
     15 use data_encoding::BASE64URL_NOPAD;
     16 use serde::{
     17     de::{Deserialize, Deserializer, Error, IgnoredAny, MapAccess, SeqAccess, Unexpected, Visitor},
     18     ser::{Serialize, SerializeSeq as _, SerializeStruct as _, Serializer},
     19 };
     20 /// [`"ble"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-ble).
     21 const BLE: &str = "ble";
     22 /// [`"hybrid"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-hybrid).
     23 const HYBRID: &str = "hybrid";
     24 /// [`"internal"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-internal).
     25 const INTERNAL: &str = "internal";
     26 /// [`"nfc"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-nfc).
     27 const NFC: &str = "nfc";
     28 /// [`"smart-card"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-smart-card).
     29 const SMART_CARD: &str = "smart-card";
     30 /// [`"usb"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-usb).
     31 const USB: &str = "usb";
     32 impl Serialize for AuthenticatorTransport {
     33     /// Serializes `self` as
     34     /// [`AuthenticatorTransport`](https://www.w3.org/TR/webauthn-3/#enumdef-authenticatortransport).
     35     ///
     36     /// # Examples
     37     ///
     38     /// ```
     39     /// # use webauthn_rp::response::AuthenticatorTransport;
     40     /// assert_eq!(
     41     ///     serde_json::to_string(&AuthenticatorTransport::Usb)?,
     42     ///     r#""usb""#
     43     /// );
     44     /// # Ok::<_, serde_json::Error>(())
     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(match *self {
     52             Self::Ble => BLE,
     53             Self::Hybrid => HYBRID,
     54             Self::Internal => INTERNAL,
     55             Self::Nfc => NFC,
     56             Self::SmartCard => SMART_CARD,
     57             Self::Usb => USB,
     58         })
     59     }
     60 }
     61 impl<'de> Deserialize<'de> for AuthenticatorTransport {
     62     /// Deserializes [`prim@str`] based on
     63     /// [`AuthenticatorTransport`](https://www.w3.org/TR/webauthn-3/#enumdef-authenticatortransport).
     64     ///
     65     /// # Examples
     66     ///
     67     /// ```
     68     /// # use webauthn_rp::response::AuthenticatorTransport;
     69     /// assert!(matches!(
     70     ///     serde_json::from_str::<AuthenticatorTransport>(r#""usb""#)?,
     71     ///     AuthenticatorTransport::Usb
     72     /// ));
     73     /// // Case matters.
     74     /// assert!(serde_json::from_str::<AuthenticatorTransport>(r#""Usb""#).is_err());
     75     /// # Ok::<_, serde_json::Error>(())
     76     /// ```
     77     #[inline]
     78     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     79     where
     80         D: Deserializer<'de>,
     81     {
     82         /// `Visitor` for `AuthenticatorTransport`.
     83         struct AuthenticatorTransportVisitor;
     84         impl Visitor<'_> for AuthenticatorTransportVisitor {
     85             type Value = AuthenticatorTransport;
     86             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
     87                 formatter.write_str("AuthenticatorTransport")
     88             }
     89             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
     90             where
     91                 E: Error,
     92             {
     93                 match v {
     94                     BLE => Ok(AuthenticatorTransport::Ble),
     95                     HYBRID => Ok(AuthenticatorTransport::Hybrid),
     96                     INTERNAL => Ok(AuthenticatorTransport::Internal),
     97                     NFC => Ok(AuthenticatorTransport::Nfc),
     98                     SMART_CARD => Ok(AuthenticatorTransport::SmartCard),
     99                     USB => Ok(AuthenticatorTransport::Usb),
    100                     _ => Err(E::invalid_value(
    101                         Unexpected::Str(v),
    102                         &format!("'{BLE}', '{HYBRID}', '{INTERNAL}', '{NFC}', '{SMART_CARD}', or '{USB}'").as_str(),
    103                     )),
    104                 }
    105             }
    106         }
    107         deserializer.deserialize_str(AuthenticatorTransportVisitor)
    108     }
    109 }
    110 impl Serialize for AuthTransports {
    111     /// Serializes `self` based on
    112     /// [`transports`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-transports).
    113     ///
    114     /// # Examples
    115     ///
    116     /// ```
    117     /// # use webauthn_rp::response::{AuthTransports, AuthenticatorTransport};
    118     /// # #[cfg(feature = "custom")]
    119     /// assert_eq!(
    120     ///     serde_json::to_string(&AuthTransports::ALL)?,
    121     ///     r#"["ble","hybrid","internal","nfc","smart-card","usb"]"#
    122     /// );
    123     /// # Ok::<_, serde_json::Error>(())
    124     /// ```
    125     #[expect(clippy::unreachable, reason = "there is a bug, so we want to crash")]
    126     #[inline]
    127     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    128     where
    129         S: Serializer,
    130     {
    131         let count = usize::try_from(self.count())
    132             .unwrap_or_else(|_e| unreachable!("there is a bug in AuthenticatorTransports::count"));
    133         serializer.serialize_seq(Some(count)).and_then(|mut ser| {
    134             if self.contains(AuthenticatorTransport::Ble) {
    135                 ser.serialize_element(&AuthenticatorTransport::Ble)
    136             } else {
    137                 Ok(())
    138             }
    139             .and_then(|()| {
    140                 if self.contains(AuthenticatorTransport::Hybrid) {
    141                     ser.serialize_element(&AuthenticatorTransport::Hybrid)
    142                 } else {
    143                     Ok(())
    144                 }
    145                 .and_then(|()| {
    146                     if self.contains(AuthenticatorTransport::Internal) {
    147                         ser.serialize_element(&AuthenticatorTransport::Internal)
    148                     } else {
    149                         Ok(())
    150                     }
    151                     .and_then(|()| {
    152                         if self.contains(AuthenticatorTransport::Nfc) {
    153                             ser.serialize_element(&AuthenticatorTransport::Nfc)
    154                         } else {
    155                             Ok(())
    156                         }
    157                         .and_then(|()| {
    158                             if self.contains(AuthenticatorTransport::SmartCard) {
    159                                 ser.serialize_element(&AuthenticatorTransport::SmartCard)
    160                             } else {
    161                                 Ok(())
    162                             }
    163                             .and_then(|()| {
    164                                 if self.contains(AuthenticatorTransport::Usb) {
    165                                     ser.serialize_element(&AuthenticatorTransport::Usb)
    166                                 } else {
    167                                     Ok(())
    168                                 }
    169                                 .and_then(|()| ser.end())
    170                             })
    171                         })
    172                     })
    173                 })
    174             })
    175         })
    176     }
    177 }
    178 impl<'de> Deserialize<'de> for AuthTransports {
    179     /// Deserializes a sequence based on
    180     /// [`transports`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-transports).
    181     ///
    182     /// # Examples
    183     ///
    184     /// ```
    185     /// # use webauthn_rp::response::{AuthTransports, AuthenticatorTransport};
    186     /// # #[cfg(feature = "custom")]
    187     /// assert_eq!(
    188     ///     serde_json::from_str::<AuthTransports>(
    189     ///         r#"["ble","hybrid","internal","nfc","smart-card","usb"]"#
    190     ///     )
    191     ///     ?.count(),
    192     ///     6
    193     /// );
    194     /// // Errors since `"foo"` is not valid.
    195     /// assert!(serde_json::from_str::<AuthTransports>(r#"["foo"]"#).is_err());
    196     /// # Ok::<_, serde_json::Error>(())
    197     /// ```
    198     #[inline]
    199     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    200     where
    201         D: Deserializer<'de>,
    202     {
    203         /// `Visitor` for `AuthTransports`.
    204         struct AuthTransportsVisitor;
    205         impl<'d> Visitor<'d> for AuthTransportsVisitor {
    206             type Value = AuthTransports;
    207             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    208                 formatter.write_str("AuthTransports")
    209             }
    210             fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
    211             where
    212                 A: SeqAccess<'d>,
    213             {
    214                 let mut transports = AuthTransports::new();
    215                 while let Some(val) = seq.next_element::<AuthenticatorTransport>()? {
    216                     transports = transports.add_transport(val);
    217                 }
    218                 Ok(transports)
    219             }
    220         }
    221         deserializer.deserialize_seq(AuthTransportsVisitor)
    222     }
    223 }
    224 impl<T: AsRef<[u8]>> Serialize for CredentialId<T> {
    225     /// Serializes `self` into a [`prim@str`] based on
    226     /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptorjson-id).
    227     ///
    228     /// # Examples
    229     ///
    230     /// ```
    231     /// # use webauthn_rp::response::CredentialId;
    232     /// // `CredentialId::try_from` only exists when `custom` is enabled; and even then, it is
    233     /// // likely never needed since the `CredentialId` was originally sent from the client and is likely
    234     /// // stored in a database which would be fetched by `UserHandle` or `Authentication::raw_id`.
    235     /// # #[cfg(feature = "custom")]
    236     /// assert_eq!(
    237     ///     serde_json::to_string(&CredentialId::try_from(vec![0; 16])?).unwrap(),
    238     ///     r#""AAAAAAAAAAAAAAAAAAAAAA""#
    239     /// );
    240     /// # Ok::<_, webauthn_rp::AggErr>(())
    241     ///```
    242     #[inline]
    243     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    244     where
    245         S: Serializer,
    246     {
    247         serializer.serialize_str(BASE64URL_NOPAD.encode(self.0.as_ref()).as_str())
    248     }
    249 }
    250 impl<'de> Deserialize<'de> for CredentialId<Vec<u8>> {
    251     /// Deserializes [`prim@str`] based on
    252     /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptorjson-id).
    253     ///
    254     /// # Examples
    255     ///
    256     /// ```
    257     /// # use webauthn_rp::response::CredentialId;
    258     /// # #[cfg(feature = "custom")]
    259     /// assert_eq!(
    260     ///     serde_json::from_str::<CredentialId<_>>(r#""AAAAAAAAAAAAAAAAAAAAAA""#).unwrap(),
    261     ///     CredentialId::try_from(vec![0; 16])?
    262     /// );
    263     /// # Ok::<_, webauthn_rp::AggErr>(())
    264     ///```
    265     #[inline]
    266     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    267     where
    268         D: Deserializer<'de>,
    269     {
    270         /// `Visitor` for `CredentialId`.
    271         struct CredentialIdVisitor;
    272         impl Visitor<'_> for CredentialIdVisitor {
    273             type Value = CredentialId<Vec<u8>>;
    274             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    275                 formatter.write_str("CredentialId")
    276             }
    277             #[expect(clippy::unreachable, reason = "we want to crash when there is a bug")]
    278             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    279             where
    280                 E: Error,
    281             {
    282                 // Any value between `super::CRED_ID_MIN_LEN` and `super::CRED_ID_MIN_LEN` can be base64url encoded
    283                 // without fear since that range is just 16 to 1023, and
    284                 // 4/3 of 1023 is less than `usize::MAX`.
    285                 if (crate::base64url_nopad_len(super::CRED_ID_MIN_LEN).unwrap_or_else(|| {
    286                     unreachable!("there is a bug in webauthn_rp::base64url_nopad_len")
    287                 })
    288                     ..=crate::base64url_nopad_len(super::CRED_ID_MAX_LEN).unwrap_or_else(|| {
    289                         unreachable!("there is a bug in webauthn_rp::base64url_nopad_len")
    290                     }))
    291                     .contains(&v.len())
    292                 {
    293                     BASE64URL_NOPAD
    294                         .decode(v.as_bytes())
    295                         .map_err(E::custom)
    296                         .map(CredentialId)
    297                 } else {
    298                     Err(E::invalid_value(
    299                         Unexpected::Str(v),
    300                         &"16 to 1023 bytes encoded in base64url without padding",
    301                     ))
    302                 }
    303             }
    304         }
    305         deserializer.deserialize_str(CredentialIdVisitor)
    306     }
    307 }
    308 impl<'de> Deserialize<'de> for AuthenticatorAttachment {
    309     /// Deserializes [`prim@str`] based on
    310     /// [`AuthenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#enumdef-authenticatorattachment).
    311     ///
    312     /// # Examples
    313     ///
    314     /// ```
    315     /// # use webauthn_rp::response::AuthenticatorAttachment;
    316     /// assert!(matches!(
    317     ///     serde_json::from_str::<AuthenticatorAttachment>(r#""cross-platform""#)?,
    318     ///     AuthenticatorAttachment::CrossPlatform)
    319     /// );
    320     /// assert!(matches!(
    321     ///     serde_json::from_str::<AuthenticatorAttachment>(r#""platform""#)?,
    322     ///     AuthenticatorAttachment::Platform)
    323     /// );
    324     /// // Case matters.
    325     /// assert!(serde_json::from_str::<AuthenticatorAttachment>(r#""Platform""#).is_err());
    326     /// // `AuthenticatorAttachment::None` is not deserializable.
    327     /// assert!(serde_json::from_str::<AuthenticatorAttachment>(r#""""#).is_err());
    328     /// assert!(serde_json::from_str::<AuthenticatorAttachment>("null").is_err());
    329     /// assert!(serde_json::from_str::<AuthenticatorAttachment>(r#""none""#).is_err());
    330     /// assert!(serde_json::from_str::<AuthenticatorAttachment>(r#""None""#).is_err());
    331     /// # Ok::<_, serde_json::Error>(())
    332     ///```
    333     #[inline]
    334     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    335     where
    336         D: Deserializer<'de>,
    337     {
    338         /// `Visitor` for `AuthenticatorAttachment`.
    339         struct AuthenticatorAttachmentVisitor;
    340         impl Visitor<'_> for AuthenticatorAttachmentVisitor {
    341             type Value = AuthenticatorAttachment;
    342             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    343                 formatter.write_str("AuthenticatorAttachment")
    344             }
    345             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    346             where
    347                 E: Error,
    348             {
    349                 /// `"platform"`
    350                 const PLATFORM: &str = "platform";
    351                 /// `"cross-platform"`
    352                 const CROSS_PLATFORM: &str = "cross-platform";
    353                 match v {
    354                     PLATFORM => Ok(AuthenticatorAttachment::Platform),
    355                     CROSS_PLATFORM => Ok(AuthenticatorAttachment::CrossPlatform),
    356                     _ => Err(E::invalid_value(
    357                         Unexpected::Str(v),
    358                         &format!("'{PLATFORM}' or '{CROSS_PLATFORM}'").as_str(),
    359                     )),
    360                 }
    361             }
    362         }
    363         deserializer.deserialize_str(AuthenticatorAttachmentVisitor)
    364     }
    365 }
    366 /// Container of data that was encoded in base64url.
    367 pub(super) struct Base64DecodedVal(pub Vec<u8>);
    368 impl<'de> Deserialize<'de> for Base64DecodedVal {
    369     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    370     where
    371         D: Deserializer<'de>,
    372     {
    373         /// `Visitor` for `Base64DecodedVal`.
    374         struct Base64DecodedValVisitor;
    375         impl Visitor<'_> for Base64DecodedValVisitor {
    376             type Value = Base64DecodedVal;
    377             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    378                 formatter.write_str("base64url-encoded data")
    379             }
    380             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    381             where
    382                 E: Error,
    383             {
    384                 BASE64URL_NOPAD
    385                     .decode(v.as_bytes())
    386                     .map_err(E::custom)
    387                     .map(Base64DecodedVal)
    388             }
    389         }
    390         deserializer.deserialize_str(Base64DecodedValVisitor)
    391     }
    392 }
    393 impl<'de> Deserialize<'de> for SentChallenge {
    394     /// Deserializes `[u8]` or [`prim@str`] based on
    395     /// [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-collectedclientdata-challenge).
    396     ///
    397     /// Specifically a `[u8]` or `str` is base64url-decoded and interpreted as a little-endian
    398     /// `u128`.
    399     ///
    400     /// # Examples
    401     ///
    402     /// ```
    403     /// # use webauthn_rp::response::SentChallenge;
    404     /// assert_eq!(
    405     ///     serde_json::from_slice::<SentChallenge>(br#""AAAAAAAAAAAAAAAAAAAAAA""#)?,
    406     ///     SentChallenge(0)
    407     /// );
    408     /// # Ok::<_, serde_json::Error>(())
    409     ///```
    410     #[inline]
    411     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    412     where
    413         D: Deserializer<'de>,
    414     {
    415         /// `Visitor` for `SentChallenge`.
    416         struct ChallengeVisitor;
    417         impl Visitor<'_> for ChallengeVisitor {
    418             type Value = SentChallenge;
    419             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    420                 formatter.write_str(
    421                     "base64 encoding of the 16-byte challenge in a URL safe way without padding",
    422                 )
    423             }
    424             #[expect(
    425                 clippy::panic_in_result_fn,
    426                 reason = "we want to crash when there is a bug"
    427             )]
    428             #[expect(
    429                 clippy::little_endian_bytes,
    430                 reason = "SentChallenge::deserialize and Challenge::serialize need to be consistent across architectures"
    431             )]
    432             fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
    433             where
    434                 E: Error,
    435             {
    436                 if v.len() == Challenge::BASE64_LEN {
    437                     let mut data = [0; 16];
    438                     BASE64URL_NOPAD
    439                         .decode_mut(v, data.as_mut_slice())
    440                         .map_err(|err| E::custom(err.error))
    441                         .map(|len| {
    442                             assert_eq!(len, 16, "there is a bug in BASE64URL_NOPAD::decode_mut");
    443                             SentChallenge(u128::from_le_bytes(data))
    444                         })
    445                 } else {
    446                     Err(E::invalid_value(
    447                         Unexpected::Bytes(v),
    448                         &"22 bytes encoded in base64url without padding",
    449                     ))
    450                 }
    451             }
    452             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    453             where
    454                 E: Error,
    455             {
    456                 self.visit_bytes(v.as_bytes())
    457             }
    458         }
    459         deserializer.deserialize_bytes(ChallengeVisitor)
    460     }
    461 }
    462 impl<'de: 'a, 'a> Deserialize<'de> for Origin<'a> {
    463     /// Deserializes [`prim@str`] by borrowing the data when possible.
    464     ///
    465     /// # Examples
    466     ///
    467     /// ```
    468     /// # extern crate alloc;
    469     /// # use alloc::borrow::Cow;
    470     /// # use webauthn_rp::response::Origin;
    471     /// let origin_borrowed = "https://example.com";
    472     /// let origin_owned = "\\\\https://example.com";
    473     /// assert!(
    474     ///     matches!(serde_json::from_str::<Origin<'_>>(format!("\"{origin_borrowed}\"").as_str())?.0, Cow::Borrowed(val) if val == origin_borrowed)
    475     /// );
    476     /// assert!(
    477     ///     matches!(serde_json::from_str::<Origin<'_>>(format!("\"{origin_owned}\"").as_str())?.0, Cow::Owned(val) if *val.as_bytes() == origin_owned.as_bytes()[1..])
    478     /// );
    479     /// # Ok::<_, serde_json::Error>(())
    480     ///```
    481     #[inline]
    482     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    483     where
    484         D: Deserializer<'de>,
    485     {
    486         /// `Visitor` for `Origin`.
    487         struct OriginVisitor<'b>(PhantomData<fn() -> &'b ()>);
    488         impl<'d: 'b, 'b> Visitor<'d> for OriginVisitor<'b> {
    489             type Value = Origin<'b>;
    490             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    491                 formatter.write_str("Origin")
    492             }
    493             fn visit_borrowed_str<E>(self, v: &'d str) -> Result<Self::Value, E>
    494             where
    495                 E: Error,
    496             {
    497                 Ok(Origin(Cow::Borrowed(v)))
    498             }
    499             fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
    500             where
    501                 E: Error,
    502             {
    503                 Ok(Origin(Cow::Owned(v)))
    504             }
    505             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    506             where
    507                 E: Error,
    508             {
    509                 self.visit_string(v.to_owned())
    510             }
    511         }
    512         deserializer.deserialize_str(OriginVisitor(PhantomData))
    513     }
    514 }
    515 /// `trait` that returns an empty instance of `Self`.
    516 pub(super) trait ClientExtensions: Sized {
    517     /// Returns an empty instance of `Self`.
    518     fn empty() -> Self;
    519 }
    520 /// Response for both registration and authentication ceremonies.
    521 ///
    522 /// [`Self::raw_id`] is always `Some` when `!RELAXED` or `!REG`.
    523 ///
    524 /// `RELAXED` and `REG` are used purely for deserialization purposes.
    525 pub(super) struct PublicKeyCredential<const RELAXED: bool, const REG: bool, AuthResp, Ext> {
    526     /// [`rawId`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-rawid).
    527     pub id: Option<CredentialId<Vec<u8>>>,
    528     /// [`response`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-response).
    529     pub response: AuthResp,
    530     /// [`authenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-authenticatorattachment).
    531     pub authenticator_attachment: AuthenticatorAttachment,
    532     /// [`getClientExtensionResults`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-getclientextensionresults).
    533     pub client_extension_results: Ext,
    534 }
    535 /// `Visitor` for `PublicKeyCredential`.
    536 ///
    537 /// When `!RELAXED`, `REG` is ignored and all fields must exist and unknown fields are not allowed.
    538 /// When `RELAXED`, unknown fields are ignored.
    539 /// When `RELAXED` and `REG`, only `response` is required.
    540 /// When `RELAXED` and `!REG`, only `id` and `response` are required.
    541 struct PublicKeyCredentialVisitor<const RELAXED: bool, const REG: bool, R, E>(
    542     pub PhantomData<fn() -> (R, E)>,
    543 );
    544 impl<'d, const REL: bool, const REGI: bool, R, E> Visitor<'d>
    545     for PublicKeyCredentialVisitor<REL, REGI, R, E>
    546 where
    547     R: for<'a> Deserialize<'a>,
    548     E: for<'a> Deserialize<'a> + ClientExtensions,
    549 {
    550     type Value = PublicKeyCredential<REL, REGI, R, E>;
    551     fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    552         formatter.write_str("PublicKeyCredential")
    553     }
    554     #[expect(
    555         clippy::too_many_lines,
    556         reason = "rather hide all the internal logic instead instead of moving into an outer scope"
    557     )]
    558     fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
    559     where
    560         A: MapAccess<'d>,
    561     {
    562         /// `PublicKeyCredentialJSON` fields.
    563         enum Field<const IGNORE_UNKNOWN: bool> {
    564             /// `id`.
    565             Id,
    566             /// `type`.
    567             Type,
    568             /// `rawId`.
    569             RawId,
    570             /// `response`.
    571             Response,
    572             /// `authenticatorAttachment`.
    573             AuthenticatorAttachment,
    574             /// `clientExtensionResults`.
    575             ClientExtensionResults,
    576             /// Unknown field.
    577             Other,
    578         }
    579         impl<'e, const IGNORE: bool> Deserialize<'e> for Field<IGNORE> {
    580             fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    581             where
    582                 D: Deserializer<'e>,
    583             {
    584                 /// `Visitor` for `Field`.
    585                 struct FieldVisitor<const IGNORE_UNKNOWN: bool>;
    586                 impl<const IGN: bool> Visitor<'_> for FieldVisitor<IGN> {
    587                     type Value = Field<IGN>;
    588                     fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    589                         write!(formatter, "'{ID}', '{TYPE}', '{RAW_ID}', '{RESPONSE}', '{AUTHENTICATOR_ATTACHMENT}', or '{CLIENT_EXTENSION_RESULTS}'")
    590                     }
    591                     fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    592                     where
    593                         E: Error,
    594                     {
    595                         match v {
    596                             ID => Ok(Field::Id),
    597                             TYPE => Ok(Field::Type),
    598                             RAW_ID => Ok(Field::RawId),
    599                             RESPONSE => Ok(Field::Response),
    600                             AUTHENTICATOR_ATTACHMENT => Ok(Field::AuthenticatorAttachment),
    601                             CLIENT_EXTENSION_RESULTS => Ok(Field::ClientExtensionResults),
    602                             _ => {
    603                                 if IGN {
    604                                     Ok(Field::Other)
    605                                 } else {
    606                                     Err(E::unknown_field(v, REG_FIELDS))
    607                                 }
    608                             }
    609                         }
    610                     }
    611                 }
    612                 deserializer.deserialize_identifier(FieldVisitor::<IGNORE>)
    613             }
    614         }
    615         /// Deserializes the value for type.
    616         struct Type;
    617         impl<'e> Deserialize<'e> for Type {
    618             fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    619             where
    620                 D: Deserializer<'e>,
    621             {
    622                 /// `Visitor` for `Type`.
    623                 struct TypeVisitor;
    624                 impl Visitor<'_> for TypeVisitor {
    625                     type Value = Type;
    626                     fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    627                         formatter.write_str(PUBLIC_KEY)
    628                     }
    629                     fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    630                     where
    631                         E: Error,
    632                     {
    633                         if v == PUBLIC_KEY {
    634                             Ok(Type)
    635                         } else {
    636                             Err(E::invalid_value(Unexpected::Str(v), &PUBLIC_KEY))
    637                         }
    638                     }
    639                 }
    640                 deserializer.deserialize_str(TypeVisitor)
    641             }
    642         }
    643         let mut opt_id = None;
    644         let mut typ = None;
    645         let mut raw = None;
    646         let mut resp = None;
    647         let mut attach = None;
    648         let mut ext = None;
    649         while let Some(key) = map.next_key::<Field<REL>>()? {
    650             match key {
    651                 Field::Id => {
    652                     if opt_id.is_some() {
    653                         return Err(Error::duplicate_field(ID));
    654                     }
    655                     opt_id = map.next_value::<Option<_>>().map(Some)?;
    656                 }
    657                 Field::Type => {
    658                     if typ.is_some() {
    659                         return Err(Error::duplicate_field(TYPE));
    660                     }
    661                     typ = map.next_value::<Option<Type>>().map(Some)?;
    662                 }
    663                 Field::RawId => {
    664                     if raw.is_some() {
    665                         return Err(Error::duplicate_field(RAW_ID));
    666                     }
    667                     raw = map.next_value::<Option<CredentialId<_>>>().map(Some)?;
    668                 }
    669                 Field::Response => {
    670                     if resp.is_some() {
    671                         return Err(Error::duplicate_field(RESPONSE));
    672                     }
    673                     resp = map.next_value::<R>().map(Some)?;
    674                 }
    675                 Field::AuthenticatorAttachment => {
    676                     if attach.is_some() {
    677                         return Err(Error::duplicate_field(AUTHENTICATOR_ATTACHMENT));
    678                     }
    679                     attach = map.next_value().map(Some)?;
    680                 }
    681                 Field::ClientExtensionResults => {
    682                     if ext.is_some() {
    683                         return Err(Error::duplicate_field(CLIENT_EXTENSION_RESULTS));
    684                     }
    685                     ext = map.next_value::<Option<E>>().map(Some)?;
    686                 }
    687                 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?,
    688             }
    689         }
    690         resp.ok_or_else(|| Error::missing_field(RESPONSE))
    691             .and_then(|response| {
    692                 opt_id.ok_or(false).and_then(|id| id.ok_or(true)).map_or_else(
    693                     |flag| {
    694                         if REL && REGI {
    695                             Ok(None)
    696                         } else if flag {
    697                             Err(Error::invalid_type(Unexpected::Other("null"), &format!("{ID} to be a base64url-encoded CredentialId").as_str()))
    698                         } else {
    699                             Err(Error::missing_field(ID))
    700                         }
    701                     },
    702                     |id| Ok(Some(id)),
    703                 )
    704                 .and_then(|id| {
    705                     raw.ok_or(false).and_then(|opt_raw_id| opt_raw_id.ok_or(true)).map_or_else(
    706                         |flag| {
    707                             if REL {
    708                                 Ok(())
    709                             } else if flag {
    710                                 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{RAW_ID} to be a base64url-encoded CredentialId").as_str()))
    711                             } else {
    712                                 Err(Error::missing_field(RAW_ID))
    713                             }
    714                         },
    715                         |raw_id| {
    716                             id.as_ref().map_or_else(
    717                                 || Ok(()),
    718                                 |i| {
    719                                     if raw_id == i {
    720                                         Ok(())
    721                                     } else {
    722                                         Err(Error::invalid_value(
    723                                             Unexpected::Bytes(raw_id.as_ref()),
    724                                             &format!("{ID} and {RAW_ID} to match: {i:?}").as_str(),
    725                                         ))
    726                                     }
    727                                 },
    728                             )
    729                         }
    730                     )
    731                     .and_then(|()| {
    732                         ext.ok_or(false).and_then(|opt_ext| opt_ext.ok_or(true)).map_or_else(
    733                             |flag| {
    734                                 if REL {
    735                                     Ok(E::empty())
    736                                 } else if flag {
    737                                     Err(Error::invalid_type(Unexpected::Other("null"), &format!("{CLIENT_EXTENSION_RESULTS} to be a map of allowed client extensions").as_str()))
    738                                 } else {
    739                                     Err(Error::missing_field(CLIENT_EXTENSION_RESULTS))
    740                                 }
    741                             },
    742                             Ok
    743                         )
    744                         .and_then(|client_extension_results| {
    745                             typ.ok_or(false).and_then(|opt_typ| opt_typ.ok_or(true)).map_or_else(
    746                                 |flag| {
    747                                     if REL {
    748                                         Ok(())
    749                                     } else if flag {
    750                                         Err(Error::invalid_type(Unexpected::Other("null"), &format!("{TYPE} to be '{PUBLIC_KEY}'").as_str()))
    751                                     } else {
    752                                         Err(Error::missing_field(TYPE))
    753                                     }
    754                                 },
    755                                 |_| Ok(()),
    756                             ).map(|()| PublicKeyCredential {
    757                                 id,
    758                                 response,
    759                                 authenticator_attachment: attach.flatten().unwrap_or(AuthenticatorAttachment::None),
    760                                 client_extension_results,
    761                             })
    762                         })
    763                     })
    764                 })
    765             })
    766     }
    767 }
    768 /// `"id"`.
    769 const ID: &str = "id";
    770 /// `"type"`.
    771 const TYPE: &str = "type";
    772 /// `"rawId"`.
    773 const RAW_ID: &str = "rawId";
    774 /// `"response"`.
    775 const RESPONSE: &str = "response";
    776 /// `"authenticatorAttachment"`.
    777 const AUTHENTICATOR_ATTACHMENT: &str = "authenticatorAttachment";
    778 /// `"clientExtensionResults"`.
    779 const CLIENT_EXTENSION_RESULTS: &str = "clientExtensionResults";
    780 /// `"public-key"`.
    781 const PUBLIC_KEY: &str = "public-key";
    782 /// Fields for `PublicKeyCredentialJSON`.
    783 const REG_FIELDS: &[&str; 6] = &[
    784     ID,
    785     TYPE,
    786     RAW_ID,
    787     RESPONSE,
    788     AUTHENTICATOR_ATTACHMENT,
    789     CLIENT_EXTENSION_RESULTS,
    790 ];
    791 impl<'de, const REL: bool, const REGI: bool, R, E> Deserialize<'de>
    792     for PublicKeyCredential<REL, REGI, R, E>
    793 where
    794     R: for<'a> Deserialize<'a>,
    795     E: for<'a> Deserialize<'a> + ClientExtensions,
    796 {
    797     /// Deserializes a `struct` based on
    798     /// [`PublicKeyCredentialJSON`](https://www.w3.org/TR/webauthn-3/#typedefdef-publickeycredentialjson).
    799     ///
    800     /// `REL` iff unknown fields should be ignored and not cause an error.
    801     /// `REGI` iff `Self` is from a registration ceremony.
    802     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    803     where
    804         D: Deserializer<'de>,
    805     {
    806         deserializer.deserialize_struct(
    807             "PublicKeyCredential",
    808             REG_FIELDS,
    809             PublicKeyCredentialVisitor::<REL, REGI, _, _>(PhantomData),
    810         )
    811     }
    812 }
    813 impl Serialize for AllAcceptedCredentialsOptions<'_, '_> {
    814     /// Serializes `self` to conform with
    815     /// [`AllAcceptedCredentialsOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-allacceptedcredentialsoptions).
    816     ///
    817     /// # Examples
    818     ///
    819     /// ```
    820     /// # use core::str::FromStr;
    821     /// # #[cfg(feature = "bin")]
    822     /// # use webauthn_rp::bin::Decode;
    823     /// # use webauthn_rp::{
    824     /// #     request::{register::UserHandle, AsciiDomain, RpId},
    825     /// #     response::{error::CredentialIdErr, AllAcceptedCredentialsOptions, CredentialId},
    826     /// # };
    827     /// /// Retrieves the `CredentialId`s associated with `user_id` from the database.
    828     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    829     /// fn get_credential_ids(user_id: UserHandle<&[u8]>) -> Result<Vec<CredentialId<Vec<u8>>>, CredentialIdErr> {
    830     ///     // ⋮
    831     /// #     CredentialId::decode(vec![0; 16]).map(|cred_id| vec![cred_id])
    832     /// }
    833     /// /// Retrieves the `UserHandle` from a session cookie.
    834     /// # #[cfg(feature = "custom")]
    835     /// fn get_user_handle() -> UserHandle<Vec<u8>> {
    836     ///     // ⋮
    837     /// #     UserHandle::try_from(vec![0]).unwrap()
    838     /// }
    839     /// # #[cfg(feature = "custom")]
    840     /// let user_id = get_user_handle();
    841     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    842     /// assert_eq!(
    843     ///     serde_json::to_string(&AllAcceptedCredentialsOptions {
    844     ///         rp_id: &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
    845     ///         user_id: (&user_id).into(),
    846     ///         all_accepted_credential_ids: get_credential_ids((&user_id).into())?,
    847     ///     })
    848     ///     .unwrap(),
    849     ///     r#"{"rpId":"example.com","userId":"AA","allAcceptedCredentialIds":["AAAAAAAAAAAAAAAAAAAAAA"]}"#
    850     /// );
    851     /// # Ok::<_, webauthn_rp::AggErr>(())
    852     /// ```
    853     #[inline]
    854     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    855     where
    856         S: Serializer,
    857     {
    858         serializer
    859             .serialize_struct("AllAcceptedCredentialsOptions", 3)
    860             .and_then(|mut ser| {
    861                 ser.serialize_field("rpId", self.rp_id).and_then(|()| {
    862                     ser.serialize_field("userId", &self.user_id).and_then(|()| {
    863                         ser.serialize_field(
    864                             "allAcceptedCredentialIds",
    865                             &self.all_accepted_credential_ids,
    866                         )
    867                         .and_then(|()| ser.end())
    868                     })
    869                 })
    870             })
    871     }
    872 }
    873 impl Serialize for CurrentUserDetailsOptions<'_, '_, '_, '_> {
    874     /// Serializes `self` to conform with
    875     /// [`CurrentUserDetailsOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-currentuserdetailsoptions).
    876     ///
    877     /// # Examples
    878     ///
    879     /// ```
    880     /// # use core::str::FromStr;
    881     /// # #[cfg(feature = "bin")]
    882     /// # use webauthn_rp::bin::Decode;
    883     /// # use webauthn_rp::{
    884     /// #     request::{register::{Nickname, PublicKeyCredentialUserEntity, UserHandle, Username}, AsciiDomain, RpId},
    885     /// #     response::CurrentUserDetailsOptions,
    886     /// #     AggErr,
    887     /// # };
    888     /// /// Retrieves the `PublicKeyCredentialUserEntity` info associated with `user_id` from the database.
    889     /// # #[cfg(feature = "bin")]
    890     /// fn get_user_info(user_id: UserHandle<&[u8]>) -> Result<(Username, Option<Nickname>), AggErr> {
    891     ///     // ⋮
    892     /// #     Ok((Username::decode("foo".to_owned()).unwrap(), Some(Nickname::decode("foo".to_owned()).unwrap())))
    893     /// }
    894     /// /// Retrieves the `UserHandle` from a session cookie.
    895     /// # #[cfg(feature = "custom")]
    896     /// fn get_user_handle() -> UserHandle<Vec<u8>> {
    897     ///     // ⋮
    898     /// #     UserHandle::try_from(vec![0]).unwrap()
    899     /// }
    900     /// # #[cfg(feature = "custom")]
    901     /// let user_handle = get_user_handle();
    902     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    903     /// let id = (&user_handle).into();
    904     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    905     /// let (name, display_name) = get_user_info(id)?;
    906     /// # #[cfg(all(feature = "bin", feature = "custom"))]
    907     /// assert_eq!(
    908     ///     serde_json::to_string(&CurrentUserDetailsOptions {
    909     ///         rp_id: &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
    910     ///         user: PublicKeyCredentialUserEntity { name, id, display_name, },
    911     ///     })
    912     ///     .unwrap(),
    913     ///     r#"{"rpId":"example.com","userId":"AA","name":"foo","displayName":"foo"}"#
    914     /// );
    915     /// # Ok::<_, AggErr>(())
    916     /// ```
    917     #[inline]
    918     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    919     where
    920         S: Serializer,
    921     {
    922         serializer
    923             .serialize_struct("CurrentUserDetailsOptions", 4)
    924             .and_then(|mut ser| {
    925                 ser.serialize_field("rpId", self.rp_id).and_then(|()| {
    926                     ser.serialize_field("userId", &self.user.id).and_then(|()| {
    927                         ser.serialize_field("name", &self.user.name).and_then(|()| {
    928                             ser.serialize_field("displayName", &self.user.display_name)
    929                                 .and_then(|()| ser.end())
    930                         })
    931                     })
    932                 })
    933             })
    934     }
    935 }
    936 /// JSON `null`.
    937 struct Null;
    938 impl<'de> Deserialize<'de> for Null {
    939     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    940     where
    941         D: Deserializer<'de>,
    942     {
    943         /// `Visitor` for `Null`.
    944         struct NullVisitor;
    945         impl Visitor<'_> for NullVisitor {
    946             type Value = Null;
    947             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    948                 formatter.write_str("null")
    949             }
    950             fn visit_none<E>(self) -> Result<Self::Value, E>
    951             where
    952                 E: Error,
    953             {
    954                 Ok(Null)
    955             }
    956         }
    957         deserializer.deserialize_option(NullVisitor)
    958     }
    959 }
    960 /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues).
    961 pub(super) struct AuthenticationExtensionsPrfValues;
    962 /// `Visitor` for `AuthenticationExtensionsPrfValues`.
    963 ///
    964 /// Unknown fields are ignored iff `RELAXED`.`first` must always exist if `second` does.
    965 /// `first` and `second` must be `null` if they exist. `first` must exist iff `!RELAXED`.
    966 pub(super) struct AuthenticationExtensionsPrfValuesVisitor<const RELAXED: bool>;
    967 impl<'d, const R: bool> Visitor<'d> for AuthenticationExtensionsPrfValuesVisitor<R> {
    968     type Value = AuthenticationExtensionsPrfValues;
    969     fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    970         formatter.write_str("AuthenticationExtensionsPrfValues")
    971     }
    972     fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
    973     where
    974         A: MapAccess<'d>,
    975     {
    976         /// Fields.
    977         enum Field<const IGNORE_UNKNOWN: bool> {
    978             /// `first` field.
    979             First,
    980             /// `second` field.
    981             Second,
    982             /// Unknown field.
    983             Other,
    984         }
    985         impl<'e, const I: bool> Deserialize<'e> for Field<I> {
    986             fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    987             where
    988                 D: Deserializer<'e>,
    989             {
    990                 /// `Visitor` for `Field`.
    991                 ///
    992                 /// Unknown fields are ignored iff `IGNORE_UNKNOWN`.
    993                 struct FieldVisitor<const IGNORE_UNKNOWN: bool>;
    994                 impl<const IG: bool> Visitor<'_> for FieldVisitor<IG> {
    995                     type Value = Field<IG>;
    996                     fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    997                         write!(formatter, "'{FIRST}' or '{SECOND}'")
    998                     }
    999                     fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
   1000                     where
   1001                         E: Error,
   1002                     {
   1003                         match v {
   1004                             FIRST => Ok(Field::First),
   1005                             SECOND => Ok(Field::Second),
   1006                             _ => {
   1007                                 if IG {
   1008                                     Ok(Field::Other)
   1009                                 } else {
   1010                                     Err(E::unknown_field(v, PRF_VALUES_FIELDS))
   1011                                 }
   1012                             }
   1013                         }
   1014                     }
   1015                 }
   1016                 deserializer.deserialize_identifier(FieldVisitor)
   1017             }
   1018         }
   1019         let mut first = None;
   1020         let mut second = None;
   1021         while let Some(key) = map.next_key::<Field<R>>()? {
   1022             match key {
   1023                 Field::First => {
   1024                     if first.is_some() {
   1025                         return Err(Error::duplicate_field(FIRST));
   1026                     }
   1027                     first = map.next_value::<Null>().map(Some)?;
   1028                 }
   1029                 Field::Second => {
   1030                     if second.is_some() {
   1031                         return Err(Error::duplicate_field(SECOND));
   1032                     }
   1033                     second = map.next_value::<Null>().map(Some)?;
   1034                 }
   1035                 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?,
   1036             }
   1037         }
   1038         if first.is_some() || (R && second.is_none()) {
   1039             Ok(AuthenticationExtensionsPrfValues)
   1040         } else {
   1041             Err(Error::missing_field(FIRST))
   1042         }
   1043     }
   1044 }
   1045 /// `"first"`
   1046 const FIRST: &str = "first";
   1047 /// `"second"`
   1048 const SECOND: &str = "second";
   1049 /// `AuthenticationExtensionsPrfValues` fields.
   1050 pub(super) const PRF_VALUES_FIELDS: &[&str; 2] = &[FIRST, SECOND];
   1051 impl<'de> Deserialize<'de> for AuthenticationExtensionsPrfValues {
   1052     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1053     where
   1054         D: Deserializer<'de>,
   1055     {
   1056         deserializer.deserialize_struct(
   1057             "AuthenticationExtensionsPrfValues",
   1058             PRF_VALUES_FIELDS,
   1059             AuthenticationExtensionsPrfValuesVisitor::<false>,
   1060         )
   1061     }
   1062 }
   1063 /// [`AuthenticationExtensionsPRFOutputs`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfoutputs).
   1064 ///
   1065 /// `RELAXED` iff unknown fields are ignored.
   1066 ///
   1067 /// `REGISTRATION` iff
   1068 /// [`enabled`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-enabled)
   1069 /// is required (and must not be `null`); otherwise it's forbidden.
   1070 ///
   1071 /// The contained `Option` is `Some` iff `REGISTRATION`.
   1072 pub(super) struct AuthenticationExtensionsPrfOutputsHelper<
   1073     const RELAXED: bool,
   1074     const REGISTRATION: bool,
   1075     Prf,
   1076 >(pub Option<bool>, pub PhantomData<fn() -> Prf>);
   1077 /// `Visitor` for `AuthenticationExtensionsPrfOutputs`.
   1078 ///
   1079 /// Unknown fields are ignored iff `RELAXED`.`enabled` must exist and not be `null` iff `REGISTRATION`.
   1080 struct AuthenticationExtensionsPrfOutputsVisitor<const RELAXED: bool, const REGISTRATION: bool, Prf>(
   1081     PhantomData<fn() -> Prf>,
   1082 );
   1083 impl<'d, const REL: bool, const REG: bool, Prf> Visitor<'d>
   1084     for AuthenticationExtensionsPrfOutputsVisitor<REL, REG, Prf>
   1085 where
   1086     Prf: for<'a> Deserialize<'a>,
   1087 {
   1088     type Value = AuthenticationExtensionsPrfOutputsHelper<REL, REG, Prf>;
   1089     fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1090         formatter.write_str("AuthenticationExtensionsPrfOutputs")
   1091     }
   1092     fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
   1093     where
   1094         A: MapAccess<'d>,
   1095     {
   1096         /// Fields.
   1097         enum Field<const IGNORE_UNKNOWN: bool, const REGI: bool> {
   1098             /// `enabled` field.
   1099             Enabled,
   1100             /// `results` field.
   1101             Results,
   1102             /// Unknown field.
   1103             Other,
   1104         }
   1105         impl<'e, const I: bool, const R: bool> Deserialize<'e> for Field<I, R> {
   1106             fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1107             where
   1108                 D: Deserializer<'e>,
   1109             {
   1110                 /// `Visitor` for `Field`.
   1111                 ///
   1112                 /// Unknown fields are ignored iff `IGNORE_UNKNOWN`.
   1113                 /// `enabled` is allowed to exist iff `REGI`.
   1114                 struct FieldVisitor<const IGNORE_UNKNOWN: bool, const REGI: bool>;
   1115                 impl<const IG: bool, const RE: bool> Visitor<'_> for FieldVisitor<IG, RE> {
   1116                     type Value = Field<IG, RE>;
   1117                     fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1118                         if RE {
   1119                             write!(formatter, "'{ENABLED}' or '{RESULTS}'")
   1120                         } else {
   1121                             write!(formatter, "'{RESULTS}'")
   1122                         }
   1123                     }
   1124                     fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
   1125                     where
   1126                         E: Error,
   1127                     {
   1128                         match v {
   1129                             ENABLED => {
   1130                                 if RE {
   1131                                     Ok(Field::Enabled)
   1132                                 } else {
   1133                                     Err(E::unknown_field(v, PRF_AUTH_OUTPUTS_FIELDS))
   1134                                 }
   1135                             }
   1136                             RESULTS => Ok(Field::Results),
   1137                             _ => {
   1138                                 if IG {
   1139                                     Ok(Field::Other)
   1140                                 } else if RE {
   1141                                     Err(E::unknown_field(v, PRF_REG_OUTPUTS_FIELDS))
   1142                                 } else {
   1143                                     Err(E::unknown_field(v, PRF_AUTH_OUTPUTS_FIELDS))
   1144                                 }
   1145                             }
   1146                         }
   1147                     }
   1148                 }
   1149                 deserializer.deserialize_identifier(FieldVisitor)
   1150             }
   1151         }
   1152         let mut enabled = None;
   1153         let mut results = None;
   1154         while let Some(key) = map.next_key::<Field<REL, REG>>()? {
   1155             match key {
   1156                 Field::Enabled => {
   1157                     if enabled.is_some() {
   1158                         return Err(Error::duplicate_field(ENABLED));
   1159                     }
   1160                     enabled = map.next_value().map(Some)?;
   1161                 }
   1162                 Field::Results => {
   1163                     if results.is_some() {
   1164                         return Err(Error::duplicate_field(RESULTS));
   1165                     }
   1166                     results = map.next_value::<Option<Prf>>().map(Some)?;
   1167                 }
   1168                 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?,
   1169             }
   1170         }
   1171         if REG {
   1172             enabled.ok_or_else(|| Error::missing_field(ENABLED)).and_then(|e| {
   1173                 if e || results.is_none() {
   1174                     Ok(())
   1175                 } else {
   1176                     Err(Error::custom("prf must not have 'results', including a null 'results', if 'enabled' is false"))
   1177                 }
   1178             })
   1179         } else {
   1180             Ok(())
   1181         }.map(|()| AuthenticationExtensionsPrfOutputsHelper(enabled, PhantomData))
   1182     }
   1183 }
   1184 /// `"enabled"`
   1185 const ENABLED: &str = "enabled";
   1186 /// `"results"`
   1187 const RESULTS: &str = "results";
   1188 /// `AuthenticationExtensionsPrfOutputs` field during registration.
   1189 const PRF_REG_OUTPUTS_FIELDS: &[&str; 2] = &[ENABLED, RESULTS];
   1190 /// `AuthenticationExtensionsPrfOutputs` field during authentication.
   1191 const PRF_AUTH_OUTPUTS_FIELDS: &[&str; 1] = &[RESULTS];
   1192 impl<'de, const REL: bool, const REG: bool, Prf> Deserialize<'de>
   1193     for AuthenticationExtensionsPrfOutputsHelper<REL, REG, Prf>
   1194 where
   1195     for<'a> Prf: Deserialize<'a>,
   1196 {
   1197     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1198     where
   1199         D: Deserializer<'de>,
   1200     {
   1201         deserializer.deserialize_struct(
   1202             "AuthenticationExtensionsPrfOutputs",
   1203             if REG {
   1204                 PRF_REG_OUTPUTS_FIELDS
   1205             } else {
   1206                 PRF_AUTH_OUTPUTS_FIELDS
   1207             },
   1208             AuthenticationExtensionsPrfOutputsVisitor::<REL, REG, Prf>(PhantomData),
   1209         )
   1210     }
   1211 }