webauthn_rp

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

ser.rs (47171B)


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