webauthn_rp

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

ser.rs (64885B)


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