webauthn_rp

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

ser.rs (48738B)


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