webauthn_rp

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

ser.rs (61912B)


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