webauthn_rp

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

ser.rs (64333B)


      1 use super::{
      2     super::{
      3         super::response::ser::{Base64DecodedVal, PublicKeyCredential},
      4         ser::{
      5             AuthenticationExtensionsPrfOutputsHelper, AuthenticationExtensionsPrfValues,
      6             ClientExtensions,
      7         },
      8     },
      9     Authentication, AuthenticatorAssertion, UserHandle,
     10     error::UnknownCredentialOptions,
     11 };
     12 #[cfg(doc)]
     13 use super::{AuthenticatorAttachment, CredentialId};
     14 use core::{
     15     fmt::{self, Formatter},
     16     marker::PhantomData,
     17     str,
     18 };
     19 use rsa::sha2::{Sha256, digest::OutputSizeUser as _};
     20 use serde::{
     21     de::{Deserialize, Deserializer, Error, IgnoredAny, MapAccess, Unexpected, Visitor},
     22     ser::{Serialize, SerializeStruct as _, Serializer},
     23 };
     24 /// Authenticator data.
     25 pub(super) struct AuthData(pub Vec<u8>);
     26 impl<'e> Deserialize<'e> for AuthData {
     27     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     28     where
     29         D: Deserializer<'e>,
     30     {
     31         /// `Visitor` for `AuthData`.
     32         struct AuthDataVisitor;
     33         impl Visitor<'_> for AuthDataVisitor {
     34             type Value = AuthData;
     35             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
     36                 formatter.write_str("AuthenticatorData")
     37             }
     38             #[expect(
     39                 clippy::arithmetic_side_effects,
     40                 reason = "comment justifies its correctness"
     41             )]
     42             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
     43             where
     44                 E: Error,
     45             {
     46                 base64url_nopad::decode_len(v.len())
     47                     .ok_or_else(|| E::invalid_value(Unexpected::Str(v), &"base64url-encoded value"))
     48                     .and_then(|len| {
     49                         // The decoded length is 3/4 of the encoded length, so overflow could only occur
     50                         // if usize::MAX / 4 < 32 => usize::MAX < 128 < u16::MAX; thus overflow is not
     51                         // possible.
     52                         // We add 32 since the SHA-256 hash of `clientDataJSON` will be added to the
     53                         // raw authenticator data by `AuthenticatorDataAssertion::new`.
     54                         let mut auth_data = vec![0; len + Sha256::output_size()];
     55                         auth_data.truncate(len);
     56                         base64url_nopad::decode_buffer_exact(v.as_bytes(), auth_data.as_mut_slice())
     57                             .map_err(E::custom)
     58                             .map(|()| AuthData(auth_data))
     59                     })
     60             }
     61         }
     62         deserializer.deserialize_str(AuthDataVisitor)
     63     }
     64 }
     65 /// `Visitor` for `AuthenticatorAssertion`.
     66 ///
     67 /// Unknown fields are ignored and only `clientDataJSON`, `authenticatorData`, and `signature` are required iff
     68 /// `RELAXED`.
     69 pub(super) struct AuthenticatorAssertionVisitor<
     70     const RELAXED: bool,
     71     const USER_LEN: usize,
     72     const DISCOVERABLE: bool,
     73 >;
     74 impl<'d, const R: bool, const LEN: usize, const DISC: bool> Visitor<'d>
     75     for AuthenticatorAssertionVisitor<R, LEN, DISC>
     76 where
     77     UserHandle<LEN>: Deserialize<'d>,
     78 {
     79     type Value = AuthenticatorAssertion<LEN, DISC>;
     80     fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
     81         formatter.write_str("AuthenticatorAssertion")
     82     }
     83     #[expect(clippy::too_many_lines, reason = "107 lines is fine")]
     84     fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
     85     where
     86         A: MapAccess<'d>,
     87     {
     88         /// Fields in `AuthenticatorAssertionResponseJSON`.
     89         enum Field<const IGNORE_UNKNOWN: bool> {
     90             /// `clientDataJSON`.
     91             ClientDataJson,
     92             /// `authenticatorData`.
     93             AuthenticatorData,
     94             /// `signature`.
     95             Signature,
     96             /// `userHandle`.
     97             UserHandle,
     98             /// Unknown field.
     99             Other,
    100         }
    101         impl<'e, const I: bool> Deserialize<'e> for Field<I> {
    102             fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    103             where
    104                 D: Deserializer<'e>,
    105             {
    106                 /// `Visitor` for `Field`.
    107                 struct FieldVisitor<const IGNORE_UNKNOWN: bool>;
    108                 impl<const IG: bool> Visitor<'_> for FieldVisitor<IG> {
    109                     type Value = Field<IG>;
    110                     fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    111                         write!(
    112                             formatter,
    113                             "'{CLIENT_DATA_JSON}', '{AUTHENTICATOR_DATA}', '{SIGNATURE}', or '{USER_HANDLE}'"
    114                         )
    115                     }
    116                     fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    117                     where
    118                         E: Error,
    119                     {
    120                         match v {
    121                             CLIENT_DATA_JSON => Ok(Field::ClientDataJson),
    122                             AUTHENTICATOR_DATA => Ok(Field::AuthenticatorData),
    123                             SIGNATURE => Ok(Field::Signature),
    124                             USER_HANDLE => Ok(Field::UserHandle),
    125                             _ => {
    126                                 if IG {
    127                                     Ok(Field::Other)
    128                                 } else {
    129                                     Err(E::unknown_field(v, AUTH_ASSERT_FIELDS))
    130                                 }
    131                             }
    132                         }
    133                     }
    134                 }
    135                 deserializer.deserialize_identifier(FieldVisitor::<I>)
    136             }
    137         }
    138         let mut client_data = None;
    139         let mut auth = None;
    140         let mut sig = None;
    141         let mut user_handle = None;
    142         while let Some(key) = map.next_key::<Field<R>>()? {
    143             match key {
    144                 Field::ClientDataJson => {
    145                     if client_data.is_some() {
    146                         return Err(Error::duplicate_field(CLIENT_DATA_JSON));
    147                     }
    148                     client_data = map
    149                         .next_value::<Base64DecodedVal>()
    150                         .map(|c_data| c_data.0)
    151                         .map(Some)?;
    152                 }
    153                 Field::AuthenticatorData => {
    154                     if auth.is_some() {
    155                         return Err(Error::duplicate_field(AUTHENTICATOR_DATA));
    156                     }
    157                     auth = map
    158                         .next_value::<AuthData>()
    159                         .map(|auth_data| Some(auth_data.0))?;
    160                 }
    161                 Field::Signature => {
    162                     if sig.is_some() {
    163                         return Err(Error::duplicate_field(SIGNATURE));
    164                     }
    165                     sig = map
    166                         .next_value::<Base64DecodedVal>()
    167                         .map(|signature| signature.0)
    168                         .map(Some)?;
    169                 }
    170                 Field::UserHandle => {
    171                     if user_handle.is_some() {
    172                         return Err(Error::duplicate_field(USER_HANDLE));
    173                     }
    174                     user_handle = map.next_value().map(Some)?;
    175                 }
    176                 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?,
    177             }
    178         }
    179         client_data
    180             .ok_or_else(|| Error::missing_field(CLIENT_DATA_JSON))
    181             .and_then(|client_data_json| {
    182                 auth.ok_or_else(|| Error::missing_field(AUTHENTICATOR_DATA))
    183                     .and_then(|authenticator_data| {
    184                         sig.ok_or_else(|| Error::missing_field(SIGNATURE))
    185                             .and_then(|signature| {
    186                                 if DISC {
    187                                     user_handle.ok_or_else(|| Error::missing_field(USER_HANDLE))
    188                                 } else {
    189                                     user_handle.map_or_else(|| Ok(None), Ok)
    190                                 }
    191                                 .map(|user| {
    192                                     AuthenticatorAssertion::new_inner(
    193                                         client_data_json,
    194                                         authenticator_data,
    195                                         signature,
    196                                         user,
    197                                     )
    198                                 })
    199                             })
    200                     })
    201             })
    202     }
    203 }
    204 /// `"clientDataJSON"`.
    205 const CLIENT_DATA_JSON: &str = "clientDataJSON";
    206 /// `"authenticatorData"`.
    207 const AUTHENTICATOR_DATA: &str = "authenticatorData";
    208 /// `"signature"`.
    209 const SIGNATURE: &str = "signature";
    210 /// `"userHandle"`.
    211 const USER_HANDLE: &str = "userHandle";
    212 /// Fields in `AuthenticatorAssertionResponseJSON`.
    213 pub(super) const AUTH_ASSERT_FIELDS: &[&str; 4] =
    214     &[CLIENT_DATA_JSON, AUTHENTICATOR_DATA, SIGNATURE, USER_HANDLE];
    215 impl<'de, const USER_LEN: usize, const DISCOVERABLE: bool> Deserialize<'de>
    216     for AuthenticatorAssertion<USER_LEN, DISCOVERABLE>
    217 where
    218     UserHandle<USER_LEN>: Deserialize<'de>,
    219 {
    220     /// Deserializes a `struct` based on
    221     /// [`AuthenticatorAssertionResponseJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticatorassertionresponsejson).
    222     ///
    223     /// Note unknown keys and duplicate keys are forbidden;
    224     /// [`clientDataJSON`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorassertionresponsejson-clientdatajson),
    225     /// [`authenticatorData`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorassertionresponsejson-authenticatordata),
    226     /// and
    227     /// [`signature`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorassertionresponsejson-signature) are
    228     /// base64url-decoded;
    229     /// [`userHandle`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorassertionresponsejson-userhandle) is
    230     /// required and must not be `null` iff `DISCOVERABLE`. When it exists and is not `null`, it is deserialized
    231     /// via [`UserHandle::deserialize`]. All `required` fields in the `AuthenticatorAssertionResponseJSON` Web IDL
    232     /// `dictionary` exist (and are not `null`).
    233     #[inline]
    234     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    235     where
    236         D: Deserializer<'de>,
    237     {
    238         deserializer.deserialize_struct(
    239             "AuthenticatorAssertion",
    240             AUTH_ASSERT_FIELDS,
    241             AuthenticatorAssertionVisitor::<false, USER_LEN, DISCOVERABLE>,
    242         )
    243     }
    244 }
    245 /// Empty map of client extensions.
    246 pub(super) struct ClientExtensionsOutputs;
    247 /// `Visitor` for `ClientExtensionsOutputs`.
    248 ///
    249 /// Unknown fields are ignored iff `RELAXED`.
    250 pub(super) struct ClientExtensionsOutputsVisitor<const RELAXED: bool, PRF>(
    251     pub PhantomData<fn() -> PRF>,
    252 );
    253 impl<'d, const R: bool, P> Visitor<'d> for ClientExtensionsOutputsVisitor<R, P>
    254 where
    255     P: for<'a> Deserialize<'a>,
    256 {
    257     type Value = ClientExtensionsOutputs;
    258     fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    259         formatter.write_str("ClientExtensionsOutputs")
    260     }
    261     fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
    262     where
    263         A: MapAccess<'d>,
    264     {
    265         /// Allowed fields.
    266         enum Field<const IGNORE_UNKNOWN: bool> {
    267             /// `prf`.
    268             Prf,
    269             /// Unknown field.
    270             Other,
    271         }
    272         impl<'e, const I: bool> Deserialize<'e> for Field<I> {
    273             fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    274             where
    275                 D: Deserializer<'e>,
    276             {
    277                 /// `Visitor` for `Field`.
    278                 ///
    279                 /// Unknown fields are ignored iff `IGNORE_UNKNOWN`.
    280                 struct FieldVisitor<const IGNORE_UNKNOWN: bool>;
    281                 impl<const IG: bool> Visitor<'_> for FieldVisitor<IG> {
    282                     type Value = Field<IG>;
    283                     fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    284                         write!(formatter, "'{PRF}'")
    285                     }
    286                     fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    287                     where
    288                         E: Error,
    289                     {
    290                         match v {
    291                             PRF => Ok(Field::Prf),
    292                             _ => {
    293                                 if IG {
    294                                     Ok(Field::Other)
    295                                 } else {
    296                                     Err(E::unknown_field(v, EXT_FIELDS))
    297                                 }
    298                             }
    299                         }
    300                     }
    301                 }
    302                 deserializer.deserialize_identifier(FieldVisitor::<I>)
    303             }
    304         }
    305         let mut prf = None;
    306         while let Some(key) = map.next_key::<Field<R>>()? {
    307             match key {
    308                 Field::Prf => {
    309                     if prf.is_some() {
    310                         return Err(Error::duplicate_field(PRF));
    311                     }
    312                     prf = map.next_value::<Option<P>>().map(Some)?;
    313                 }
    314                 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?,
    315             }
    316         }
    317         Ok(ClientExtensionsOutputs)
    318     }
    319 }
    320 impl ClientExtensions for ClientExtensionsOutputs {
    321     fn empty() -> Self {
    322         Self
    323     }
    324 }
    325 /// `"prf"`
    326 const PRF: &str = "prf";
    327 /// `AuthenticationExtensionsClientOutputsJSON` fields.
    328 pub(super) const EXT_FIELDS: &[&str; 1] = &[PRF];
    329 impl<'de> Deserialize<'de> for ClientExtensionsOutputs {
    330     /// Deserializes a `struct` based on
    331     /// [`AuthenticationExtensionsClientOutputsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsclientoutputsjson).
    332     ///
    333     /// Note that unknown and duplicate keys are forbidden and
    334     /// [`prf`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsclientoutputs-prf) is `null`
    335     /// or deserialized via [`AuthenticationExtensionsPrfOutputs::deserialize`].
    336     #[inline]
    337     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    338     where
    339         D: Deserializer<'de>,
    340     {
    341         deserializer.deserialize_struct(
    342             "ClientExtensionsOutputs",
    343             EXT_FIELDS,
    344             ClientExtensionsOutputsVisitor::<
    345                 false,
    346                 AuthenticationExtensionsPrfOutputsHelper<
    347                     false,
    348                     false,
    349                     AuthenticationExtensionsPrfValues,
    350                 >,
    351             >(PhantomData),
    352         )
    353     }
    354 }
    355 impl<'de, const USER_LEN: usize, const DISCOVERABLE: bool> Deserialize<'de>
    356     for Authentication<USER_LEN, DISCOVERABLE>
    357 where
    358     UserHandle<USER_LEN>: Deserialize<'de>,
    359 {
    360     /// Deserializes a `struct` based on
    361     /// [`AuthenticationResponseJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationresponsejson).
    362     ///
    363     /// Note that unknown and duplicate keys are forbidden;
    364     /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-id) and
    365     /// [`rawId`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-rawid) are deserialized
    366     /// via [`CredentialId::deserialize`];
    367     /// [`response`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-response) is deserialized
    368     /// via [`AuthenticatorAssertion::deserialize`];
    369     /// [`authenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-authenticatorattachment)
    370     /// is `null` or deserialized via [`AuthenticatorAttachment::deserialize`];
    371     /// [`clientExtensionResults`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-clientextensionresults)
    372     /// is deserialized such that it is an empty map or a map that only contains
    373     /// [`prf`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsclientoutputs-prf) which additionally must be
    374     /// `null` or an
    375     /// [`AuthenticationExtensionsPRFOutputs`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfoutputs)
    376     /// such that unknown and duplicate keys are forbidden,
    377     /// [`enabled`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-enabled)
    378     /// is forbidden (including being assigned `null`),
    379     /// [`results`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-results) must not exist,
    380     /// be `null`, or be an
    381     /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues)
    382     /// with no unknown or duplicate keys,
    383     /// [`first`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-first) must exist but be
    384     /// `null`, and
    385     /// [`second`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-second) can exist but
    386     /// must be `null` if so; all `required` fields in the `AuthenticationResponseJSON` Web IDL `dictionary` exist
    387     /// (and are not `null`); [`type`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-type) is
    388     /// `"public-key"`; and the decoded `id` and decoded `rawId` are the same.
    389     #[expect(clippy::unreachable, reason = "when there is a bug, we want to crash")]
    390     #[inline]
    391     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    392     where
    393         D: Deserializer<'de>,
    394     {
    395         PublicKeyCredential::<
    396             false,
    397             false,
    398             AuthenticatorAssertion<USER_LEN, DISCOVERABLE>,
    399             ClientExtensionsOutputs,
    400         >::deserialize(deserializer)
    401         .map(|cred| Self {
    402             raw_id: cred.id.unwrap_or_else(|| {
    403                 unreachable!("there is a bug in PublicKeyCredential::deserialize")
    404             }),
    405             response: cred.response,
    406             authenticator_attachment: cred.authenticator_attachment,
    407         })
    408     }
    409 }
    410 impl Serialize for UnknownCredentialOptions<'_, '_> {
    411     /// Serializes `self` to conform with
    412     /// [`UnknownCredentialOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-unknowncredentialoptions).
    413     ///
    414     /// # Examples
    415     ///
    416     /// ```
    417     /// # use core::str::FromStr;
    418     /// # use webauthn_rp::{request::{AsciiDomain, RpId}, response::{auth::error::UnknownCredentialOptions, CredentialId}};
    419     /// # #[cfg(feature = "custom")]
    420     /// let credential_id = CredentialId::try_from(vec![0; 16])?;
    421     /// # #[cfg(feature = "custom")]
    422     /// assert_eq!(
    423     ///     serde_json::to_string(&UnknownCredentialOptions {
    424     ///         rp_id: &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
    425     ///         credential_id: (&credential_id).into(),
    426     ///     })
    427     ///     .unwrap(),
    428     ///     r#"{"rpId":"example.com","credentialId":"AAAAAAAAAAAAAAAAAAAAAA"}"#
    429     /// );
    430     /// # Ok::<_, webauthn_rp::AggErr>(())
    431     /// ```
    432     #[inline]
    433     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    434     where
    435         S: Serializer,
    436     {
    437         serializer
    438             .serialize_struct("UnknownCredentialOptions", 2)
    439             .and_then(|mut ser| {
    440                 ser.serialize_field("rpId", self.rp_id).and_then(|()| {
    441                     ser.serialize_field("credentialId", &self.credential_id)
    442                         .and_then(|()| ser.end())
    443                 })
    444             })
    445     }
    446 }
    447 #[cfg(test)]
    448 mod tests {
    449     use super::super::{
    450         super::super::request::register::USER_HANDLE_MIN_LEN, AuthenticatorAttachment,
    451         DiscoverableAuthentication, NonDiscoverableAuthentication,
    452     };
    453     use rsa::sha2::{Digest as _, Sha256};
    454     use serde::de::{Error as _, Unexpected};
    455     use serde_json::Error;
    456     #[test]
    457     fn eddsa_authentication_deserialize_data_mismatch() {
    458         let c_data_json = serde_json::json!({}).to_string();
    459         let auth_data = [
    460             // `rpIdHash`.
    461             0,
    462             0,
    463             0,
    464             0,
    465             0,
    466             0,
    467             0,
    468             0,
    469             0,
    470             0,
    471             0,
    472             0,
    473             0,
    474             0,
    475             0,
    476             0,
    477             0,
    478             0,
    479             0,
    480             0,
    481             0,
    482             0,
    483             0,
    484             0,
    485             0,
    486             0,
    487             0,
    488             0,
    489             0,
    490             0,
    491             0,
    492             0,
    493             // `flags`.
    494             0b0000_0101,
    495             // `signCount`.
    496             0,
    497             0,
    498             0,
    499             0,
    500         ];
    501         let b64_cdata = base64url_nopad::encode(c_data_json.as_bytes());
    502         let b64_adata = base64url_nopad::encode(auth_data.as_slice());
    503         let b64_sig = base64url_nopad::encode([].as_slice());
    504         let b64_user = base64url_nopad::encode(b"\x00".as_slice());
    505         // Base case is valid.
    506         assert!(
    507             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    508                 serde_json::json!({
    509                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    510                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    511                     "response": {
    512                         "clientDataJSON": b64_cdata,
    513                         "authenticatorData": b64_adata,
    514                         "signature": b64_sig,
    515                         "userHandle": b64_user,
    516                     },
    517                     "authenticatorAttachment": "cross-platform",
    518                     "clientExtensionResults": {},
    519                     "type": "public-key"
    520                 })
    521                 .to_string()
    522                 .as_str()
    523             )
    524             .map_or(false, |auth| auth.response.client_data_json
    525                 == c_data_json.as_bytes()
    526                 && auth.response.authenticator_data_and_c_data_hash[..37] == auth_data
    527                 && auth.response.authenticator_data_and_c_data_hash[37..]
    528                     == *Sha256::digest(c_data_json.as_bytes()).as_slice()
    529                 && matches!(
    530                     auth.authenticator_attachment,
    531                     AuthenticatorAttachment::CrossPlatform
    532                 ))
    533         );
    534         // `id` and `rawId` mismatch.
    535         let mut err = Error::invalid_value(
    536             Unexpected::Bytes(
    537                 base64url_nopad::decode("ABABABABABABABABABABAA".as_bytes())
    538                     .unwrap()
    539                     .as_slice(),
    540             ),
    541             &format!("id and rawId to match: CredentialId({:?})", [0; 16]).as_str(),
    542         )
    543         .to_string()
    544         .into_bytes();
    545         assert_eq!(
    546             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    547                 serde_json::json!({
    548                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    549                     "rawId": "ABABABABABABABABABABAA",
    550                     "response": {
    551                         "clientDataJSON": b64_cdata,
    552                         "authenticatorData": b64_adata,
    553                         "signature": b64_sig,
    554                         "userHandle": b64_user,
    555                     },
    556                     "authenticatorAttachment": "cross-platform",
    557                     "clientExtensionResults": {},
    558                     "type": "public-key"
    559                 })
    560                 .to_string()
    561                 .as_str()
    562             )
    563             .unwrap_err()
    564             .to_string()
    565             .into_bytes()[..err.len()],
    566             err
    567         );
    568         // missing `id`.
    569         err = Error::missing_field("id").to_string().into_bytes();
    570         assert_eq!(
    571             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    572                 serde_json::json!({
    573                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    574                     "response": {
    575                         "clientDataJSON": b64_cdata,
    576                         "authenticatorData": b64_adata,
    577                         "signature": b64_sig,
    578                         "userHandle": b64_user,
    579                     },
    580                     "authenticatorAttachment": "cross-platform",
    581                     "clientExtensionResults": {},
    582                     "type": "public-key"
    583                 })
    584                 .to_string()
    585                 .as_str()
    586             )
    587             .unwrap_err()
    588             .to_string()
    589             .into_bytes()[..err.len()],
    590             err
    591         );
    592         // `null` `id`.
    593         err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId")
    594             .to_string()
    595             .into_bytes();
    596         assert_eq!(
    597             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    598                 serde_json::json!({
    599                     "id": null,
    600                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    601                     "response": {
    602                         "clientDataJSON": b64_cdata,
    603                         "authenticatorData": b64_adata,
    604                         "signature": b64_sig,
    605                         "userHandle": b64_user,
    606                     },
    607                     "clientExtensionResults": {},
    608                     "type": "public-key"
    609                 })
    610                 .to_string()
    611                 .as_str()
    612             )
    613             .unwrap_err()
    614             .to_string()
    615             .into_bytes()[..err.len()],
    616             err
    617         );
    618         // missing `rawId`.
    619         err = Error::missing_field("rawId").to_string().into_bytes();
    620         assert_eq!(
    621             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    622                 serde_json::json!({
    623                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    624                     "response": {
    625                         "clientDataJSON": b64_cdata,
    626                         "authenticatorData": b64_adata,
    627                         "signature": b64_sig,
    628                         "userHandle": b64_user,
    629                     },
    630                     "clientExtensionResults": {},
    631                     "type": "public-key"
    632                 })
    633                 .to_string()
    634                 .as_str()
    635             )
    636             .unwrap_err()
    637             .to_string()
    638             .into_bytes()[..err.len()],
    639             err
    640         );
    641         // `null` `rawId`.
    642         err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId")
    643             .to_string()
    644             .into_bytes();
    645         assert_eq!(
    646             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    647                 serde_json::json!({
    648                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    649                     "rawId": null,
    650                     "response": {
    651                         "clientDataJSON": b64_cdata,
    652                         "authenticatorData": b64_adata,
    653                         "signature": b64_sig,
    654                         "userHandle": b64_user,
    655                     },
    656                     "clientExtensionResults": {},
    657                     "type": "public-key"
    658                 })
    659                 .to_string()
    660                 .as_str()
    661             )
    662             .unwrap_err()
    663             .to_string()
    664             .into_bytes()[..err.len()],
    665             err
    666         );
    667         // Missing `authenticatorData`.
    668         err = Error::missing_field("authenticatorData")
    669             .to_string()
    670             .into_bytes();
    671         assert_eq!(
    672             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    673                 serde_json::json!({
    674                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    675                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    676                     "response": {
    677                         "clientDataJSON": b64_cdata,
    678                         "signature": b64_sig,
    679                         "userHandle": b64_user,
    680                     },
    681                     "clientExtensionResults": {},
    682                     "type": "public-key"
    683                 })
    684                 .to_string()
    685                 .as_str()
    686             )
    687             .unwrap_err()
    688             .to_string()
    689             .into_bytes()[..err.len()],
    690             err
    691         );
    692         // `null` `authenticatorData`.
    693         err = Error::invalid_type(Unexpected::Other("null"), &"AuthenticatorData")
    694             .to_string()
    695             .into_bytes();
    696         assert_eq!(
    697             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    698                 serde_json::json!({
    699                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    700                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    701                     "response": {
    702                         "clientDataJSON": b64_cdata,
    703                         "authenticatorData": null,
    704                         "signature": b64_sig,
    705                         "userHandle": b64_user,
    706                     },
    707                     "clientExtensionResults": {},
    708                     "type": "public-key"
    709                 })
    710                 .to_string()
    711                 .as_str()
    712             )
    713             .unwrap_err()
    714             .to_string()
    715             .into_bytes()[..err.len()],
    716             err
    717         );
    718         // Missing `signature`.
    719         err = Error::missing_field("signature").to_string().into_bytes();
    720         assert_eq!(
    721             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    722                 serde_json::json!({
    723                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    724                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    725                     "response": {
    726                         "clientDataJSON": b64_cdata,
    727                         "authenticatorData": b64_adata,
    728                         "userHandle": b64_user,
    729                     },
    730                     "clientExtensionResults": {},
    731                     "type": "public-key"
    732                 })
    733                 .to_string()
    734                 .as_str()
    735             )
    736             .unwrap_err()
    737             .to_string()
    738             .into_bytes()[..err.len()],
    739             err
    740         );
    741         // `null` `signature`.
    742         err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data")
    743             .to_string()
    744             .into_bytes();
    745         assert_eq!(
    746             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    747                 serde_json::json!({
    748                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    749                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    750                     "response": {
    751                         "clientDataJSON": b64_cdata,
    752                         "authenticatorData": b64_adata,
    753                         "signature": null,
    754                         "userHandle": b64_user,
    755                     },
    756                     "clientExtensionResults": {},
    757                     "type": "public-key"
    758                 })
    759                 .to_string()
    760                 .as_str()
    761             )
    762             .unwrap_err()
    763             .to_string()
    764             .into_bytes()[..err.len()],
    765             err
    766         );
    767         // Missing `userHandle`.
    768         assert!(
    769             serde_json::from_str::<NonDiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    770                 serde_json::json!({
    771                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    772                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    773                     "response": {
    774                         "clientDataJSON": b64_cdata,
    775                         "authenticatorData": b64_adata,
    776                         "signature": b64_sig,
    777                     },
    778                     "clientExtensionResults": {},
    779                     "type": "public-key"
    780                 })
    781                 .to_string()
    782                 .as_str()
    783             )
    784             .is_ok()
    785         );
    786         // `null` `userHandle`.
    787         assert!(
    788             serde_json::from_str::<NonDiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    789                 serde_json::json!({
    790                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    791                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    792                     "response": {
    793                         "clientDataJSON": b64_cdata,
    794                         "authenticatorData": b64_adata,
    795                         "signature": b64_sig,
    796                         "userHandle": null,
    797                     },
    798                     "clientExtensionResults": {},
    799                     "type": "public-key"
    800                 })
    801                 .to_string()
    802                 .as_str()
    803             )
    804             .is_ok()
    805         );
    806         // `null` `authenticatorAttachment`.
    807         assert!(
    808             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    809                 serde_json::json!({
    810                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    811                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    812                     "response": {
    813                         "clientDataJSON": b64_cdata,
    814                         "authenticatorData": b64_adata,
    815                         "signature": b64_sig,
    816                         "userHandle": b64_user,
    817                     },
    818                     "authenticatorAttachment": null,
    819                     "clientExtensionResults": {},
    820                     "type": "public-key"
    821                 })
    822                 .to_string()
    823                 .as_str()
    824             )
    825             .map_or(false, |auth| matches!(
    826                 auth.authenticator_attachment,
    827                 AuthenticatorAttachment::None
    828             ))
    829         );
    830         // Unknown `authenticatorAttachment`.
    831         err = Error::invalid_value(
    832             Unexpected::Str("Platform"),
    833             &"'platform' or 'cross-platform'",
    834         )
    835         .to_string()
    836         .into_bytes();
    837         assert_eq!(
    838             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    839                 serde_json::json!({
    840                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    841                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    842                     "response": {
    843                         "clientDataJSON": b64_cdata,
    844                         "authenticatorData": b64_adata,
    845                         "signature": b64_sig,
    846                         "userHandle": b64_user,
    847                     },
    848                     "authenticatorAttachment": "Platform",
    849                     "clientExtensionResults": {},
    850                     "type": "public-key"
    851                 })
    852                 .to_string()
    853                 .as_str()
    854             )
    855             .unwrap_err()
    856             .to_string()
    857             .into_bytes()[..err.len()],
    858             err
    859         );
    860         // Missing `clientDataJSON`.
    861         err = Error::missing_field("clientDataJSON")
    862             .to_string()
    863             .into_bytes();
    864         assert_eq!(
    865             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    866                 serde_json::json!({
    867                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    868                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    869                     "response": {
    870                         "authenticatorData": b64_adata,
    871                         "signature": b64_sig,
    872                         "userHandle": b64_user,
    873                     },
    874                     "clientExtensionResults": {},
    875                     "type": "public-key"
    876                 })
    877                 .to_string()
    878                 .as_str()
    879             )
    880             .unwrap_err()
    881             .to_string()
    882             .into_bytes()[..err.len()],
    883             err
    884         );
    885         // `null` `clientDataJSON`.
    886         err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data")
    887             .to_string()
    888             .into_bytes();
    889         assert_eq!(
    890             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    891                 serde_json::json!({
    892                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    893                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    894                     "response": {
    895                         "clientDataJSON": null,
    896                         "authenticatorData": b64_adata,
    897                         "signature": b64_sig,
    898                         "userHandle": b64_user,
    899                     },
    900                     "clientExtensionResults": {},
    901                     "type": "public-key"
    902                 })
    903                 .to_string()
    904                 .as_str()
    905             )
    906             .unwrap_err()
    907             .to_string()
    908             .into_bytes()[..err.len()],
    909             err
    910         );
    911         // Missing `response`.
    912         err = Error::missing_field("response").to_string().into_bytes();
    913         assert_eq!(
    914             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    915                 serde_json::json!({
    916                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    917                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    918                     "clientExtensionResults": {},
    919                     "type": "public-key"
    920                 })
    921                 .to_string()
    922                 .as_str()
    923             )
    924             .unwrap_err()
    925             .to_string()
    926             .into_bytes()[..err.len()],
    927             err
    928         );
    929         // `null` `response`.
    930         err = Error::invalid_type(Unexpected::Other("null"), &"AuthenticatorAssertion")
    931             .to_string()
    932             .into_bytes();
    933         assert_eq!(
    934             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    935                 serde_json::json!({
    936                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    937                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    938                     "response": null,
    939                     "clientExtensionResults": {},
    940                     "type": "public-key"
    941                 })
    942                 .to_string()
    943                 .as_str()
    944             )
    945             .unwrap_err()
    946             .to_string()
    947             .into_bytes()[..err.len()],
    948             err
    949         );
    950         // Empty `response`.
    951         err = Error::missing_field("clientDataJSON")
    952             .to_string()
    953             .into_bytes();
    954         assert_eq!(
    955             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    956                 serde_json::json!({
    957                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    958                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    959                     "response": {},
    960                     "clientExtensionResults": {},
    961                     "type": "public-key"
    962                 })
    963                 .to_string()
    964                 .as_str()
    965             )
    966             .unwrap_err()
    967             .to_string()
    968             .into_bytes()[..err.len()],
    969             err
    970         );
    971         // Missing `clientExtensionResults`.
    972         err = Error::missing_field("clientExtensionResults")
    973             .to_string()
    974             .into_bytes();
    975         assert_eq!(
    976             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
    977                 serde_json::json!({
    978                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    979                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    980                     "response": {
    981                         "clientDataJSON": b64_cdata,
    982                         "authenticatorData": b64_adata,
    983                         "signature": b64_sig,
    984                         "userHandle": b64_user,
    985                     },
    986                     "type": "public-key"
    987                 })
    988                 .to_string()
    989                 .as_str()
    990             )
    991             .unwrap_err()
    992             .to_string()
    993             .into_bytes()[..err.len()],
    994             err
    995         );
    996         // `null` `clientExtensionResults`.
    997         err = Error::invalid_type(
    998             Unexpected::Other("null"),
    999             &"clientExtensionResults to be a map of allowed client extensions",
   1000         )
   1001         .to_string()
   1002         .into_bytes();
   1003         assert_eq!(
   1004             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1005                 serde_json::json!({
   1006                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1007                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1008                     "response": {
   1009                         "clientDataJSON": b64_cdata,
   1010                         "authenticatorData": b64_adata,
   1011                         "signature": b64_sig,
   1012                         "userHandle": b64_user,
   1013                     },
   1014                     "clientExtensionResults": null,
   1015                     "type": "public-key"
   1016                 })
   1017                 .to_string()
   1018                 .as_str()
   1019             )
   1020             .unwrap_err()
   1021             .to_string()
   1022             .into_bytes()[..err.len()],
   1023             err
   1024         );
   1025         // Missing `type`.
   1026         err = Error::missing_field("type").to_string().into_bytes();
   1027         assert_eq!(
   1028             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1029                 serde_json::json!({
   1030                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1031                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1032                     "response": {
   1033                         "clientDataJSON": b64_cdata,
   1034                         "authenticatorData": b64_adata,
   1035                         "signature": b64_sig,
   1036                         "userHandle": b64_user,
   1037                     },
   1038                     "clientExtensionResults": {},
   1039                 })
   1040                 .to_string()
   1041                 .as_str()
   1042             )
   1043             .unwrap_err()
   1044             .to_string()
   1045             .into_bytes()[..err.len()],
   1046             err
   1047         );
   1048         // `null` `type`.
   1049         err = Error::invalid_type(Unexpected::Other("null"), &"public-key")
   1050             .to_string()
   1051             .into_bytes();
   1052         assert_eq!(
   1053             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1054                 serde_json::json!({
   1055                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1056                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1057                     "response": {
   1058                         "clientDataJSON": b64_cdata,
   1059                         "authenticatorData": b64_adata,
   1060                         "signature": b64_sig,
   1061                         "userHandle": b64_user,
   1062                     },
   1063                     "clientExtensionResults": {},
   1064                     "type": null
   1065                 })
   1066                 .to_string()
   1067                 .as_str()
   1068             )
   1069             .unwrap_err()
   1070             .to_string()
   1071             .into_bytes()[..err.len()],
   1072             err
   1073         );
   1074         // Not exactly `public-type` `type`.
   1075         err = Error::invalid_value(Unexpected::Str("Public-key"), &"public-key")
   1076             .to_string()
   1077             .into_bytes();
   1078         assert_eq!(
   1079             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1080                 serde_json::json!({
   1081                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1082                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1083                     "response": {
   1084                         "clientDataJSON": b64_cdata,
   1085                         "authenticatorData": b64_adata,
   1086                         "signature": b64_sig,
   1087                         "userHandle": b64_user,
   1088                     },
   1089                     "clientExtensionResults": {},
   1090                     "type": "Public-key"
   1091                 })
   1092                 .to_string()
   1093                 .as_str()
   1094             )
   1095             .unwrap_err()
   1096             .to_string()
   1097             .into_bytes()[..err.len()],
   1098             err
   1099         );
   1100         // `null`.
   1101         err = Error::invalid_type(Unexpected::Other("null"), &"PublicKeyCredential")
   1102             .to_string()
   1103             .into_bytes();
   1104         assert_eq!(
   1105             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1106                 serde_json::json!(null).to_string().as_str()
   1107             )
   1108             .unwrap_err()
   1109             .to_string()
   1110             .into_bytes()[..err.len()],
   1111             err
   1112         );
   1113         // Empty.
   1114         err = Error::missing_field("response").to_string().into_bytes();
   1115         assert_eq!(
   1116             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1117                 serde_json::json!({}).to_string().as_str()
   1118             )
   1119             .unwrap_err()
   1120             .to_string()
   1121             .into_bytes()[..err.len()],
   1122             err
   1123         );
   1124         // Unknown field in `response`.
   1125         err = Error::unknown_field(
   1126             "foo",
   1127             [
   1128                 "clientDataJSON",
   1129                 "authenticatorData",
   1130                 "signature",
   1131                 "userHandle",
   1132             ]
   1133             .as_slice(),
   1134         )
   1135         .to_string()
   1136         .into_bytes();
   1137         assert_eq!(
   1138             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1139                 serde_json::json!({
   1140                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1141                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1142                     "response": {
   1143                         "clientDataJSON": b64_cdata,
   1144                         "authenticatorData": b64_adata,
   1145                         "signature": b64_sig,
   1146                         "userHandle": b64_user,
   1147                         "foo": true,
   1148                     },
   1149                     "clientExtensionResults": {},
   1150                     "type": "public-key"
   1151                 })
   1152                 .to_string()
   1153                 .as_str()
   1154             )
   1155             .unwrap_err()
   1156             .to_string()
   1157             .into_bytes()[..err.len()],
   1158             err
   1159         );
   1160         // Duplicate field in `response`.
   1161         err = Error::duplicate_field("userHandle")
   1162             .to_string()
   1163             .into_bytes();
   1164         assert_eq!(
   1165             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1166                 format!(
   1167                     "{{
   1168                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1169                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1170                        \"response\": {{
   1171                            \"clientDataJSON\": \"{b64_cdata}\",
   1172                            \"authenticatorData\": \"{b64_adata}\",
   1173                            \"signature\": \"{b64_sig}\",
   1174                            \"userHandle\": \"{b64_user}\",
   1175                            \"userHandle\": \"{b64_user}\"
   1176                        }},
   1177                        \"clientExtensionResults\": {{}},
   1178                        \"type\": \"public-key\"
   1179 
   1180                      }}"
   1181                 )
   1182                 .as_str()
   1183             )
   1184             .unwrap_err()
   1185             .to_string()
   1186             .into_bytes()[..err.len()],
   1187             err
   1188         );
   1189         // Unknown field in `PublicKeyCredential`.
   1190         err = Error::unknown_field(
   1191             "foo",
   1192             [
   1193                 "id",
   1194                 "type",
   1195                 "rawId",
   1196                 "response",
   1197                 "authenticatorAttachment",
   1198                 "clientExtensionResults",
   1199             ]
   1200             .as_slice(),
   1201         )
   1202         .to_string()
   1203         .into_bytes();
   1204         assert_eq!(
   1205             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1206                 serde_json::json!({
   1207                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1208                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1209                     "response": {
   1210                         "clientDataJSON": b64_cdata,
   1211                         "authenticatorData": b64_adata,
   1212                         "signature": b64_sig,
   1213                         "userHandle": b64_user,
   1214                     },
   1215                     "clientExtensionResults": {},
   1216                     "type": "public-key",
   1217                     "foo": true,
   1218                 })
   1219                 .to_string()
   1220                 .as_str()
   1221             )
   1222             .unwrap_err()
   1223             .to_string()
   1224             .into_bytes()[..err.len()],
   1225             err
   1226         );
   1227         // Duplicate field in `PublicKeyCredential`.
   1228         err = Error::duplicate_field("id").to_string().into_bytes();
   1229         assert_eq!(
   1230             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1231                 format!(
   1232                     "{{
   1233                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1234                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1235                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1236                        \"response\": {{
   1237                            \"clientDataJSON\": \"{b64_cdata}\",
   1238                            \"authenticatorData\": \"{b64_adata}\",
   1239                            \"signature\": \"{b64_sig}\",
   1240                            \"userHandle\": \"{b64_user}\"
   1241                        }},
   1242                        \"clientExtensionResults\": {{}},
   1243                        \"type\": \"public-key\"
   1244 
   1245                      }}"
   1246                 )
   1247                 .as_str()
   1248             )
   1249             .unwrap_err()
   1250             .to_string()
   1251             .into_bytes()[..err.len()],
   1252             err
   1253         );
   1254     }
   1255     #[test]
   1256     fn client_extensions() {
   1257         let c_data_json = serde_json::json!({}).to_string();
   1258         let auth_data = [
   1259             // `rpIdHash`.
   1260             0,
   1261             0,
   1262             0,
   1263             0,
   1264             0,
   1265             0,
   1266             0,
   1267             0,
   1268             0,
   1269             0,
   1270             0,
   1271             0,
   1272             0,
   1273             0,
   1274             0,
   1275             0,
   1276             0,
   1277             0,
   1278             0,
   1279             0,
   1280             0,
   1281             0,
   1282             0,
   1283             0,
   1284             0,
   1285             0,
   1286             0,
   1287             0,
   1288             0,
   1289             0,
   1290             0,
   1291             0,
   1292             // `flags`.
   1293             0b0000_0101,
   1294             // `signCount`.
   1295             0,
   1296             0,
   1297             0,
   1298             0,
   1299         ];
   1300         let b64_cdata = base64url_nopad::encode(c_data_json.as_bytes());
   1301         let b64_adata = base64url_nopad::encode(auth_data.as_slice());
   1302         let b64_sig = base64url_nopad::encode([].as_slice());
   1303         let b64_user = base64url_nopad::encode(b"\x00".as_slice());
   1304         // Base case is valid.
   1305         assert!(
   1306             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1307                 serde_json::json!({
   1308                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1309                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1310                     "response": {
   1311                         "clientDataJSON": b64_cdata,
   1312                         "authenticatorData": b64_adata,
   1313                         "signature": b64_sig,
   1314                         "userHandle": b64_user,
   1315                     },
   1316                     "authenticatorAttachment": "cross-platform",
   1317                     "clientExtensionResults": {},
   1318                     "type": "public-key"
   1319                 })
   1320                 .to_string()
   1321                 .as_str()
   1322             )
   1323             .map_or(false, |auth| auth.response.client_data_json
   1324                 == c_data_json.as_bytes()
   1325                 && auth.response.authenticator_data_and_c_data_hash[..37] == auth_data
   1326                 && auth.response.authenticator_data_and_c_data_hash[37..]
   1327                     == *Sha256::digest(c_data_json.as_bytes()).as_slice()
   1328                 && matches!(
   1329                     auth.authenticator_attachment,
   1330                     AuthenticatorAttachment::CrossPlatform
   1331                 ))
   1332         );
   1333         // `null` `prf`.
   1334         assert!(
   1335             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1336                 serde_json::json!({
   1337                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1338                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1339                     "response": {
   1340                         "clientDataJSON": b64_cdata,
   1341                         "authenticatorData": b64_adata,
   1342                         "signature": b64_sig,
   1343                         "userHandle": b64_user,
   1344                     },
   1345                     "clientExtensionResults": {
   1346                         "prf": null
   1347                     },
   1348                     "type": "public-key"
   1349                 })
   1350                 .to_string()
   1351                 .as_str()
   1352             )
   1353             .is_ok()
   1354         );
   1355         // Unknown `clientExtensionResults`.
   1356         let mut err = Error::unknown_field("Prf", ["prf"].as_slice())
   1357             .to_string()
   1358             .into_bytes();
   1359         assert_eq!(
   1360             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1361                 serde_json::json!({
   1362                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1363                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1364                     "response": {
   1365                         "clientDataJSON": b64_cdata,
   1366                         "authenticatorData": b64_adata,
   1367                         "signature": b64_sig,
   1368                         "userHandle": b64_user,
   1369                     },
   1370                     "clientExtensionResults": {
   1371                         "Prf": null
   1372                     },
   1373                     "type": "public-key"
   1374                 })
   1375                 .to_string()
   1376                 .as_str()
   1377             )
   1378             .unwrap_err()
   1379             .to_string()
   1380             .into_bytes()[..err.len()],
   1381             err
   1382         );
   1383         // Duplicate field.
   1384         err = Error::duplicate_field("prf").to_string().into_bytes();
   1385         assert_eq!(
   1386             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1387                 format!(
   1388                     "{{
   1389                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1390                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1391                        \"response\": {{
   1392                            \"clientDataJSON\": \"{b64_cdata}\",
   1393                            \"authenticatorData\": \"{b64_adata}\",
   1394                            \"signature\": \"{b64_sig}\",
   1395                            \"userHandle\": \"{b64_user}\"
   1396                        }},
   1397                        \"clientExtensionResults\": {{
   1398                            \"prf\": null,
   1399                            \"prf\": null
   1400                        }},
   1401                        \"type\": \"public-key\"
   1402                      }}"
   1403                 )
   1404                 .as_str()
   1405             )
   1406             .unwrap_err()
   1407             .to_string()
   1408             .into_bytes()[..err.len()],
   1409             err
   1410         );
   1411         // `null` `results`.
   1412         assert!(
   1413             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1414                 serde_json::json!({
   1415                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1416                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1417                     "response": {
   1418                         "clientDataJSON": b64_cdata,
   1419                         "authenticatorData": b64_adata,
   1420                         "signature": b64_sig,
   1421                         "userHandle": b64_user,
   1422                     },
   1423                     "clientExtensionResults": {
   1424                         "prf": {
   1425                             "results": null,
   1426                         }
   1427                     },
   1428                     "type": "public-key"
   1429                 })
   1430                 .to_string()
   1431                 .as_str()
   1432             )
   1433             .is_ok()
   1434         );
   1435         // Duplicate field in `prf`.
   1436         err = Error::duplicate_field("results").to_string().into_bytes();
   1437         assert_eq!(
   1438             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1439                 format!(
   1440                     "{{
   1441                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1442                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1443                        \"response\": {{
   1444                            \"clientDataJSON\": \"{b64_cdata}\",
   1445                            \"authenticatorData\": \"{b64_adata}\",
   1446                            \"signature\": \"{b64_sig}\",
   1447                            \"userHandle\": \"{b64_user}\"
   1448                        }},
   1449                        \"clientExtensionResults\": {{
   1450                            \"prf\": {{
   1451                                \"results\": null,
   1452                                \"results\": null
   1453                            }}
   1454                        }},
   1455                        \"type\": \"public-key\"
   1456                      }}"
   1457                 )
   1458                 .as_str()
   1459             )
   1460             .unwrap_err()
   1461             .to_string()
   1462             .into_bytes()[..err.len()],
   1463             err
   1464         );
   1465         // Missing `first`.
   1466         err = Error::missing_field("first").to_string().into_bytes();
   1467         assert_eq!(
   1468             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1469                 serde_json::json!({
   1470                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1471                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1472                     "response": {
   1473                         "clientDataJSON": b64_cdata,
   1474                         "authenticatorData": b64_adata,
   1475                         "signature": b64_sig,
   1476                         "userHandle": b64_user,
   1477                     },
   1478                     "clientExtensionResults": {
   1479                         "prf": {
   1480                             "results": {},
   1481                         }
   1482                     },
   1483                     "type": "public-key"
   1484                 })
   1485                 .to_string()
   1486                 .as_str()
   1487             )
   1488             .unwrap_err()
   1489             .to_string()
   1490             .into_bytes()[..err.len()],
   1491             err
   1492         );
   1493         // `null` `first`.
   1494         assert!(
   1495             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1496                 serde_json::json!({
   1497                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1498                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1499                     "response": {
   1500                         "clientDataJSON": b64_cdata,
   1501                         "authenticatorData": b64_adata,
   1502                         "signature": b64_sig,
   1503                         "userHandle": b64_user,
   1504                     },
   1505                     "clientExtensionResults": {
   1506                         "prf": {
   1507                             "results": {
   1508                                 "first": null
   1509                             },
   1510                         }
   1511                     },
   1512                     "type": "public-key"
   1513                 })
   1514                 .to_string()
   1515                 .as_str()
   1516             )
   1517             .is_ok()
   1518         );
   1519         // `null` `second`.
   1520         assert!(
   1521             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1522                 serde_json::json!({
   1523                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1524                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1525                     "response": {
   1526                         "clientDataJSON": b64_cdata,
   1527                         "authenticatorData": b64_adata,
   1528                         "signature": b64_sig,
   1529                         "userHandle": b64_user,
   1530                     },
   1531                     "clientExtensionResults": {
   1532                         "prf": {
   1533                             "results": {
   1534                                 "first": null,
   1535                                 "second": null
   1536                             },
   1537                         }
   1538                     },
   1539                     "type": "public-key"
   1540                 })
   1541                 .to_string()
   1542                 .as_str()
   1543             )
   1544             .is_ok()
   1545         );
   1546         // Non-`null` `first`.
   1547         err = Error::invalid_type(Unexpected::Option, &"null")
   1548             .to_string()
   1549             .into_bytes();
   1550         assert_eq!(
   1551             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1552                 serde_json::json!({
   1553                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1554                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1555                     "response": {
   1556                         "clientDataJSON": b64_cdata,
   1557                         "authenticatorData": b64_adata,
   1558                         "signature": b64_sig,
   1559                         "userHandle": b64_user,
   1560                     },
   1561                     "clientExtensionResults": {
   1562                         "prf": {
   1563                             "results": {
   1564                                 "first": ""
   1565                             },
   1566                         }
   1567                     },
   1568                     "type": "public-key"
   1569                 })
   1570                 .to_string()
   1571                 .as_str()
   1572             )
   1573             .unwrap_err()
   1574             .to_string()
   1575             .into_bytes()[..err.len()],
   1576             err
   1577         );
   1578         // Non-`null` `second`.
   1579         err = Error::invalid_type(Unexpected::Option, &"null")
   1580             .to_string()
   1581             .into_bytes();
   1582         assert_eq!(
   1583             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1584                 serde_json::json!({
   1585                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1586                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1587                     "response": {
   1588                         "clientDataJSON": b64_cdata,
   1589                         "authenticatorData": b64_adata,
   1590                         "signature": b64_sig,
   1591                         "userHandle": b64_user,
   1592                     },
   1593                     "clientExtensionResults": {
   1594                         "prf": {
   1595                             "results": {
   1596                                 "first": null,
   1597                                 "second": ""
   1598                             },
   1599                         }
   1600                     },
   1601                     "type": "public-key"
   1602                 })
   1603                 .to_string()
   1604                 .as_str()
   1605             )
   1606             .unwrap_err()
   1607             .to_string()
   1608             .into_bytes()[..err.len()],
   1609             err
   1610         );
   1611         // Unknown `prf` field.
   1612         err = Error::unknown_field("enabled", ["results"].as_slice())
   1613             .to_string()
   1614             .into_bytes();
   1615         assert_eq!(
   1616             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1617                 serde_json::json!({
   1618                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1619                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1620                     "response": {
   1621                         "clientDataJSON": b64_cdata,
   1622                         "authenticatorData": b64_adata,
   1623                         "signature": b64_sig,
   1624                         "userHandle": b64_user,
   1625                     },
   1626                     "clientExtensionResults": {
   1627                         "prf": {
   1628                             "enabled": true,
   1629                             "results": null
   1630                         }
   1631                     },
   1632                     "type": "public-key"
   1633                 })
   1634                 .to_string()
   1635                 .as_str()
   1636             )
   1637             .unwrap_err()
   1638             .to_string()
   1639             .into_bytes()[..err.len()],
   1640             err
   1641         );
   1642         // Unknown `results` field.
   1643         err = Error::unknown_field("Second", ["first", "second"].as_slice())
   1644             .to_string()
   1645             .into_bytes();
   1646         assert_eq!(
   1647             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1648                 serde_json::json!({
   1649                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1650                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1651                     "response": {
   1652                         "clientDataJSON": b64_cdata,
   1653                         "authenticatorData": b64_adata,
   1654                         "signature": b64_sig,
   1655                         "userHandle": b64_user,
   1656                     },
   1657                     "clientExtensionResults": {
   1658                         "prf": {
   1659                             "results": {
   1660                                 "first": null,
   1661                                 "Second": null
   1662                             }
   1663                         }
   1664                     },
   1665                     "type": "public-key"
   1666                 })
   1667                 .to_string()
   1668                 .as_str()
   1669             )
   1670             .unwrap_err()
   1671             .to_string()
   1672             .into_bytes()[..err.len()],
   1673             err
   1674         );
   1675         // Duplicate field in `results`.
   1676         err = Error::duplicate_field("first").to_string().into_bytes();
   1677         assert_eq!(
   1678             serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>(
   1679                 format!(
   1680                     "{{
   1681                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1682                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1683                        \"response\": {{
   1684                            \"clientDataJSON\": \"{b64_cdata}\",
   1685                            \"authenticatorData\": \"{b64_adata}\",
   1686                            \"signature\": \"{b64_sig}\",
   1687                            \"userHandle\": \"{b64_user}\"
   1688                        }},
   1689                        \"clientExtensionResults\": {{
   1690                            \"prf\": {{
   1691                                \"results\": {{
   1692                                    \"first\": null,
   1693                                    \"first\": null
   1694                                }}
   1695                            }}
   1696                        }},
   1697                        \"type\": \"public-key\"
   1698                      }}"
   1699                 )
   1700                 .as_str()
   1701             )
   1702             .unwrap_err()
   1703             .to_string()
   1704             .into_bytes()[..err.len()],
   1705             err
   1706         );
   1707     }
   1708 }