webauthn_rp

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

ser_relaxed.rs (83697B)


      1 #![expect(
      2     clippy::question_mark_used,
      3     reason = "noisy, opinionated, and likely doesn't prevent bugs or improve APIs"
      4 )]
      5 #[cfg(doc)]
      6 use super::super::{Challenge, CredentialId};
      7 use super::{
      8     super::{
      9         super::request::register::{User, UserHandle16, UserHandle64},
     10         auth::ser::{
     11             AUTH_ASSERT_FIELDS, AuthData, AuthenticatorAssertionVisitor, ClientExtensionsOutputs,
     12             ClientExtensionsOutputsVisitor, EXT_FIELDS,
     13         },
     14         ser::{
     15             AuthenticationExtensionsPrfOutputsHelper, Base64DecodedVal, ClientExtensions,
     16             PublicKeyCredential, Type,
     17         },
     18         ser_relaxed::AuthenticationExtensionsPrfValuesRelaxed,
     19     },
     20     Authentication, AuthenticatorAssertion, AuthenticatorAttachment, UserHandle,
     21 };
     22 use core::{
     23     fmt::{self, Formatter},
     24     marker::PhantomData,
     25 };
     26 #[cfg(doc)]
     27 use data_encoding::BASE64URL_NOPAD;
     28 use serde::de::{Deserialize, Deserializer, Error, MapAccess, Visitor};
     29 /// `newtype` around `ClientExtensionsOutputs` with a "relaxed" [`Self::deserialize`] implementation.
     30 struct ClientExtensionsOutputsRelaxed(pub ClientExtensionsOutputs);
     31 impl ClientExtensions for ClientExtensionsOutputsRelaxed {
     32     fn empty() -> Self {
     33         Self(ClientExtensionsOutputs::empty())
     34     }
     35 }
     36 impl<'de> Deserialize<'de> for ClientExtensionsOutputsRelaxed {
     37     /// Same as [`ClientExtensionsOutputs::deserialize`] except unknown keys are ignored.
     38     ///
     39     /// Note that duplicate keys are still forbidden.
     40     #[inline]
     41     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     42     where
     43         D: Deserializer<'de>,
     44     {
     45         deserializer
     46             .deserialize_struct(
     47                 "ClientExtensionsOutputsRelaxed",
     48                 EXT_FIELDS,
     49                 ClientExtensionsOutputsVisitor::<
     50                     true,
     51                     AuthenticationExtensionsPrfOutputsHelper<
     52                         true,
     53                         false,
     54                         AuthenticationExtensionsPrfValuesRelaxed,
     55                     >,
     56                 >(PhantomData),
     57             )
     58             .map(Self)
     59     }
     60 }
     61 /// `newtype` around `AuthenticatorAssertion` with a "relaxed" [`Self::deserialize`] implementation.
     62 #[derive(Debug)]
     63 pub struct AuthenticatorAssertionRelaxed<U>(pub AuthenticatorAssertion<U>);
     64 impl<'de, U> Deserialize<'de> for AuthenticatorAssertionRelaxed<U>
     65 where
     66     U: Deserialize<'de> + User + Default,
     67 {
     68     /// Same as [`AuthenticatorAssertion::deserialize`] except unknown keys are ignored.
     69     ///
     70     /// Note that duplicate keys are still forbidden.
     71     #[inline]
     72     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     73     where
     74         D: Deserializer<'de>,
     75     {
     76         deserializer
     77             .deserialize_struct(
     78                 "AuthenticatorAssertionRelaxed",
     79                 AUTH_ASSERT_FIELDS,
     80                 AuthenticatorAssertionVisitor::<true, U>::new(),
     81             )
     82             .map(Self)
     83     }
     84 }
     85 /// `newtype` around `Authentication` with a "relaxed" [`Self::deserialize`] implementation.
     86 #[derive(Debug)]
     87 pub struct AuthenticationRelaxed<U>(pub Authentication<U>);
     88 impl<'de, U> Deserialize<'de> for AuthenticationRelaxed<U>
     89 where
     90     U: Deserialize<'de> + User + Default,
     91 {
     92     /// Same as [`Authentication::deserialize`] except unknown keys are ignored;
     93     /// [`response`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-response) is deserialized
     94     /// via [`AuthenticatorAssertionRelaxed::deserialize`];
     95     /// [`clientExtensionResults`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-clientextensionresults)
     96     /// is deserialized such unknown keys are ignored but duplicate keys are forbidden,
     97     /// [`prf`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsclientoutputs-prf) is `null` or an
     98     /// [`AuthenticationExtensionsPRFOutputs`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfoutputs)
     99     /// such that unknown keys are allowed but duplicate keys are forbidden,
    100     /// [`enabled`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-enabled)
    101     /// is forbidden (including being assigned `null`),
    102     /// [`results`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-results) must not exist,
    103     /// be `null`, or be an
    104     /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues)
    105     /// where unknown keys are ignored, duplicate keys are forbidden,
    106     /// [`first`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-first) is not required but
    107     /// if it exists it must be `null`, and
    108     /// [`second`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-second) can exist but
    109     /// must be `null` if so; and only
    110     /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-id) and `response` are required.
    111     /// `rawId` and `type` and allowed to not exist. For the other fields, they are allowed to not exist or be `null`.
    112     ///
    113     /// Note that duplicate keys are still forbidden, and data matching still applies when applicable.
    114     #[expect(clippy::unreachable, reason = "when there is a bug, we want to crash")]
    115     #[inline]
    116     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    117     where
    118         D: Deserializer<'de>,
    119     {
    120         PublicKeyCredential::<
    121             true,
    122             false,
    123             AuthenticatorAssertionRelaxed<U>,
    124             ClientExtensionsOutputsRelaxed,
    125         >::deserialize(deserializer)
    126         .map(|cred| {
    127             Self(Authentication {
    128                 raw_id: cred.id.unwrap_or_else(|| {
    129                     unreachable!("there is a bug in PublicKeyCredential::deserialize")
    130                 }),
    131                 response: cred.response.0,
    132                 authenticator_attachment: cred.authenticator_attachment,
    133             })
    134         })
    135     }
    136 }
    137 /// `AuthenticationRelaxed` with a required `UserHandle`.
    138 pub type PasskeyAuthenticationRelaxed<T> = AuthenticationRelaxed<UserHandle<T>>;
    139 /// `AuthenticationRelaxed` with a required `UserHandle64`.
    140 pub type PasskeyAuthenticationRelaxed64 = AuthenticationRelaxed<UserHandle64>;
    141 /// `AuthenticationRelaxed` with a required `UserHandle16`.
    142 pub type PasskeyAuthenticationRelaxed16 = AuthenticationRelaxed<UserHandle16>;
    143 /// `newtype` around `Authentication` with a custom [`Self::deserialize`] implementation.
    144 #[derive(Debug)]
    145 pub struct CustomAuthentication<U>(pub Authentication<U>);
    146 impl<'de, U> Deserialize<'de> for CustomAuthentication<U>
    147 where
    148     U: Deserialize<'de> + User + Default,
    149 {
    150     /// Despite the spec having a
    151     /// [pre-defined format](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationresponsejson) that clients
    152     /// can follow, the downside is the superfluous data it contains.
    153     ///
    154     /// There simply is no reason to send the [`CredentialId`] twice. This redundant data puts RPs in
    155     /// a position where they either ignore the data or parse the data to ensure no contradictions exist
    156     /// (e.g., [FIDO conformance requires one to verify `id` and `rawId` exist and match](https://github.com/w3c/webauthn/issues/2119#issuecomment-2287875401)).
    157     ///
    158     /// While [`Authentication::deserialize`] _strictly_ adheres to the JSON definition, this implementation
    159     /// strictly disallows superfluous data. Specifically the following JSON is required to be sent where duplicate
    160     /// and unknown keys are disallowed:
    161     ///
    162     /// ```json
    163     /// {
    164     ///   "authenticatorAttachment": null | "platform" | "cross-platform",
    165     ///   "authenticatorData": <base64url string>,
    166     ///   "clientDataJSON": <base64url string>,
    167     ///   "clientExtensionResults": {
    168     ///     "prf": null | PRFJSON
    169     ///   },
    170     ///   "id": <see CredentialId::deserialize>,
    171     ///   "signature": <base64url string>,
    172     ///   "type": "public-key",
    173     ///   "userHandle": null | <see UserHandle::deserialize>
    174     /// }
    175     /// // PRFJSON:
    176     /// {
    177     ///   "results": null | PRFOutputsJSON
    178     /// }
    179     /// // PRFOutputsJSON:
    180     /// {
    181     ///   "first": null,
    182     ///   "second": null
    183     /// }
    184     /// ```
    185     ///
    186     /// `"userHandle"` is required to exist and not be `null` iff `U` is [`UserHandle`]. When it does exist and
    187     /// is not `null`, then it is deserialized via [`UserHandle::deserialize`]. All of the remaining keys are
    188     /// required with the exceptions of `"authenticatorAttachment"` and `"type"`. `"prf"` is not required in the
    189     /// `clientExtensionResults` object, `"results"` is required in the `PRFJSON` object, and `"first"`
    190     /// (but not `"second"`) is required in `PRFOutputsJSON`.
    191     ///
    192     /// # Examples
    193     ///
    194     /// ```
    195     /// # use webauthn_rp::{request::register::{UserHandle, USER_HANDLE_MIN_LEN}, response::auth::ser_relaxed::CustomAuthentication};
    196     /// assert!(
    197     ///     // The below payload is technically valid, but `AuthenticationServerState::verify` will fail
    198     ///     // since the authenticatorData is not valid. This is true for `Authentication::deserialize`
    199     ///     // as well since authenticatorData parsing is always deferred.
    200     ///     serde_json::from_str::<CustomAuthentication<UserHandle<[u8; USER_HANDLE_MIN_LEN]>>>(
    201     ///         r#"{
    202     ///             "authenticatorData": "AA",
    203     ///             "authenticatorAttachment": "cross-platform",
    204     ///             "clientExtensionResults": {},
    205     ///             "clientDataJSON": "AA",
    206     ///             "id": "AAAAAAAAAAAAAAAAAAAAAA",
    207     ///             "signature": "AA",
    208     ///             "type": "public-key",
    209     ///             "userHandle": "AA"
    210     ///         }"#
    211     ///     ).is_ok());
    212     /// ```
    213     #[expect(
    214         clippy::too_many_lines,
    215         reason = "want to hide; thus don't put in outer scope"
    216     )]
    217     #[inline]
    218     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    219     where
    220         D: Deserializer<'de>,
    221     {
    222         /// `Visitor` for `CustomAuthentication`.
    223         struct CustomAuthenticationVisitor<UHand>(PhantomData<fn() -> UHand>);
    224         impl<'d, UHand> Visitor<'d> for CustomAuthenticationVisitor<UHand>
    225         where
    226             UHand: Deserialize<'d> + User + Default,
    227         {
    228             type Value = CustomAuthentication<UHand>;
    229             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    230                 formatter.write_str("CustomAuthentication")
    231             }
    232             #[expect(
    233                 clippy::too_many_lines,
    234                 reason = "want to hide; thus don't put in outer scope"
    235             )]
    236             fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
    237             where
    238                 A: MapAccess<'d>,
    239             {
    240                 /// Fields in the JSON.
    241                 enum Field {
    242                     /// `authenticatorAttachment` key.
    243                     AuthenticatorAttachment,
    244                     /// `authenticatorData` key.
    245                     AuthenticatorData,
    246                     /// `clientDataJSON` key.
    247                     ClientDataJson,
    248                     /// `clientExtensionResults` key.
    249                     ClientExtensionResults,
    250                     /// `id` key.
    251                     Id,
    252                     /// `signature` key.
    253                     Signature,
    254                     /// `type` key.
    255                     Type,
    256                     /// `userHandle` key.
    257                     UserHandle,
    258                 }
    259                 impl<'e> Deserialize<'e> for Field {
    260                     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    261                     where
    262                         D: Deserializer<'e>,
    263                     {
    264                         /// `Visitor` for `Field`.
    265                         struct FieldVisitor;
    266                         impl Visitor<'_> for FieldVisitor {
    267                             type Value = Field;
    268                             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    269                                 write!(
    270                                     formatter,
    271                                     "'{AUTHENTICATOR_ATTACHMENT}', '{AUTHENTICATOR_DATA}', '{CLIENT_DATA_JSON}', '{CLIENT_EXTENSION_RESULTS}', '{ID}', '{SIGNATURE}', '{TYPE}', or '{USER_HANDLE}'"
    272                                 )
    273                             }
    274                             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    275                             where
    276                                 E: Error,
    277                             {
    278                                 match v {
    279                                     AUTHENTICATOR_ATTACHMENT => Ok(Field::AuthenticatorAttachment),
    280                                     AUTHENTICATOR_DATA => Ok(Field::AuthenticatorData),
    281                                     CLIENT_DATA_JSON => Ok(Field::ClientDataJson),
    282                                     CLIENT_EXTENSION_RESULTS => Ok(Field::ClientExtensionResults),
    283                                     ID => Ok(Field::Id),
    284                                     SIGNATURE => Ok(Field::Signature),
    285                                     TYPE => Ok(Field::Type),
    286                                     USER_HANDLE => Ok(Field::UserHandle),
    287                                     _ => Err(E::unknown_field(v, FIELDS)),
    288                                 }
    289                             }
    290                         }
    291                         deserializer.deserialize_identifier(FieldVisitor)
    292                     }
    293                 }
    294                 let mut authenticator_attachment = None;
    295                 let mut authenticator_data = None;
    296                 let mut client_data_json = None;
    297                 let mut ext = false;
    298                 let mut id = None;
    299                 let mut signature = None;
    300                 let mut typ = false;
    301                 let mut user_handle = None;
    302                 while let Some(key) = map.next_key()? {
    303                     match key {
    304                         Field::AuthenticatorAttachment => {
    305                             if authenticator_attachment.is_some() {
    306                                 return Err(Error::duplicate_field(AUTHENTICATOR_ATTACHMENT));
    307                             }
    308                             authenticator_attachment = map.next_value::<Option<_>>().map(Some)?;
    309                         }
    310                         Field::AuthenticatorData => {
    311                             if authenticator_data.is_some() {
    312                                 return Err(Error::duplicate_field(AUTHENTICATOR_DATA));
    313                             }
    314                             authenticator_data =
    315                                 map.next_value::<AuthData>().map(|val| Some(val.0))?;
    316                         }
    317                         Field::ClientDataJson => {
    318                             if client_data_json.is_some() {
    319                                 return Err(Error::duplicate_field(CLIENT_DATA_JSON));
    320                             }
    321                             client_data_json = map
    322                                 .next_value::<Base64DecodedVal>()
    323                                 .map(|val| Some(val.0))?;
    324                         }
    325                         Field::ClientExtensionResults => {
    326                             if ext {
    327                                 return Err(Error::duplicate_field(CLIENT_EXTENSION_RESULTS));
    328                             }
    329                             ext = map.next_value::<ClientExtensionsOutputs>().map(|_| true)?;
    330                         }
    331                         Field::Id => {
    332                             if id.is_some() {
    333                                 return Err(Error::duplicate_field(ID));
    334                             }
    335                             id = map.next_value().map(Some)?;
    336                         }
    337                         Field::Signature => {
    338                             if signature.is_some() {
    339                                 return Err(Error::duplicate_field(SIGNATURE));
    340                             }
    341                             signature = map
    342                                 .next_value::<Base64DecodedVal>()
    343                                 .map(|val| Some(val.0))?;
    344                         }
    345                         Field::Type => {
    346                             if typ {
    347                                 return Err(Error::duplicate_field(TYPE));
    348                             }
    349                             typ = map.next_value::<Type>().map(|_| true)?;
    350                         }
    351                         Field::UserHandle => {
    352                             if user_handle.is_some() {
    353                                 return Err(Error::duplicate_field(USER_HANDLE));
    354                             }
    355                             user_handle = map.next_value().map(Some)?;
    356                         }
    357                     }
    358                 }
    359                 authenticator_data
    360                     .ok_or_else(|| Error::missing_field(AUTHENTICATOR_DATA))
    361                     .and_then(|auth_data| {
    362                         client_data_json
    363                             .ok_or_else(|| Error::missing_field(CLIENT_DATA_JSON))
    364                             .and_then(|c_data| {
    365                                 id.ok_or_else(|| Error::missing_field(ID))
    366                                     .and_then(|raw_id| {
    367                                         signature
    368                                             .ok_or_else(|| Error::missing_field(SIGNATURE))
    369                                             .and_then(|sig| {
    370                                                 if ext {
    371                                                     if UHand::must_exist() {
    372                                                         user_handle.ok_or_else(|| Error::missing_field(USER_HANDLE))
    373                                                     } else {
    374                                                         user_handle.map_or_else(|| Ok(UHand::default()), Ok)
    375                                                     }.map(|user| {
    376                                                         CustomAuthentication(Authentication {
    377                                                             response: AuthenticatorAssertion::new_inner(
    378                                                                 c_data,
    379                                                                 auth_data,
    380                                                                 sig,
    381                                                                 user,
    382                                                             ),
    383                                                             authenticator_attachment:
    384                                                                 authenticator_attachment.map_or(
    385                                                                     AuthenticatorAttachment::None,
    386                                                                     |auth_attach| {
    387                                                                         auth_attach.unwrap_or(
    388                                                                         AuthenticatorAttachment::None,
    389                                                                     )
    390                                                                     },
    391                                                                 ),
    392                                                             raw_id,
    393                                                         })
    394                                                     })
    395                                                 } else {
    396                                                     Err(Error::missing_field(
    397                                                         CLIENT_EXTENSION_RESULTS,
    398                                                     ))
    399                                                 }
    400                                             })
    401                                     })
    402                             })
    403                     })
    404             }
    405         }
    406         /// `authenticatorAttachment` key.
    407         const AUTHENTICATOR_ATTACHMENT: &str = "authenticatorAttachment";
    408         /// `authenticatorData` key.
    409         const AUTHENTICATOR_DATA: &str = "authenticatorData";
    410         /// `clientDataJSON` key.
    411         const CLIENT_DATA_JSON: &str = "clientDataJSON";
    412         /// `clientExtensionResults` key.
    413         const CLIENT_EXTENSION_RESULTS: &str = "clientExtensionResults";
    414         /// `id` key.
    415         const ID: &str = "id";
    416         /// `signature` key.
    417         const SIGNATURE: &str = "signature";
    418         /// `type` key.
    419         const TYPE: &str = "type";
    420         /// `userHandle` key.
    421         const USER_HANDLE: &str = "userHandle";
    422         /// Fields.
    423         const FIELDS: &[&str; 8] = &[
    424             AUTHENTICATOR_ATTACHMENT,
    425             AUTHENTICATOR_DATA,
    426             CLIENT_DATA_JSON,
    427             CLIENT_EXTENSION_RESULTS,
    428             ID,
    429             SIGNATURE,
    430             TYPE,
    431             USER_HANDLE,
    432         ];
    433         deserializer.deserialize_struct(
    434             "CustomAuthentication",
    435             FIELDS,
    436             CustomAuthenticationVisitor(PhantomData),
    437         )
    438     }
    439 }
    440 /// `CustomAuthentication` with a required `UserHandle`.
    441 pub type PasskeyCustomAuthentication<T> = CustomAuthentication<UserHandle<T>>;
    442 /// `CustomAuthentication` with a required `UserHandle64`.
    443 pub type PasskeyCustomAuthentication64 = CustomAuthentication<UserHandle64>;
    444 /// `CustomAuthentication` with a required `UserHandle16`.
    445 pub type PasskeyCustomAuthentication16 = CustomAuthentication<UserHandle16>;
    446 #[cfg(test)]
    447 mod tests {
    448     use super::{
    449         super::{
    450             super::super::request::register::USER_HANDLE_MIN_LEN, AuthenticatorAttachment,
    451             UserHandle,
    452         },
    453         AuthenticationRelaxed, CustomAuthentication, PasskeyAuthenticationRelaxed,
    454         PasskeyCustomAuthentication,
    455     };
    456     use data_encoding::BASE64URL_NOPAD;
    457     use rsa::sha2::{Digest as _, Sha256};
    458     use serde::de::{Error as _, Unexpected};
    459     use serde_json::Error;
    460     #[test]
    461     fn eddsa_authentication_deserialize_data_mismatch() {
    462         let c_data_json = serde_json::json!({}).to_string();
    463         let auth_data = [
    464             // `rpIdHash`.
    465             0,
    466             0,
    467             0,
    468             0,
    469             0,
    470             0,
    471             0,
    472             0,
    473             0,
    474             0,
    475             0,
    476             0,
    477             0,
    478             0,
    479             0,
    480             0,
    481             0,
    482             0,
    483             0,
    484             0,
    485             0,
    486             0,
    487             0,
    488             0,
    489             0,
    490             0,
    491             0,
    492             0,
    493             0,
    494             0,
    495             0,
    496             0,
    497             // `flags`.
    498             0b0000_0101,
    499             // `signCount`.
    500             0,
    501             0,
    502             0,
    503             0,
    504         ];
    505         let b64_cdata = BASE64URL_NOPAD.encode(c_data_json.as_bytes());
    506         let b64_adata = BASE64URL_NOPAD.encode(auth_data.as_slice());
    507         let b64_sig = BASE64URL_NOPAD.encode([].as_slice());
    508         let b64_user = BASE64URL_NOPAD.encode(b"\x00".as_slice());
    509         // Base case is valid.
    510         assert!(
    511             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    512                 serde_json::json!({
    513                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    514                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    515                     "response": {
    516                         "clientDataJSON": b64_cdata,
    517                         "authenticatorData": b64_adata,
    518                         "signature": b64_sig,
    519                         "userHandle": b64_user,
    520                     },
    521                     "authenticatorAttachment": "cross-platform",
    522                     "clientExtensionResults": {},
    523                     "type": "public-key"
    524                 })
    525                 .to_string()
    526                 .as_str()
    527             )
    528             .map_or(false, |auth| auth.0.response.client_data_json
    529                 == c_data_json.as_bytes()
    530                 && auth.0.response.authenticator_data_and_c_data_hash[..37] == auth_data
    531                 && auth.0.response.authenticator_data_and_c_data_hash[37..]
    532                     == *Sha256::digest(c_data_json.as_bytes()).as_slice()
    533                 && matches!(
    534                     auth.0.authenticator_attachment,
    535                     AuthenticatorAttachment::CrossPlatform
    536                 ))
    537         );
    538         // `id` and `rawId` mismatch.
    539         let mut err = Error::invalid_value(
    540             Unexpected::Bytes(
    541                 BASE64URL_NOPAD
    542                     .decode("ABABABABABABABABABABAA".as_bytes())
    543                     .unwrap()
    544                     .as_slice(),
    545             ),
    546             &format!("id and rawId to match: CredentialId({:?})", [0; 16]).as_str(),
    547         )
    548         .to_string()
    549         .into_bytes();
    550         assert_eq!(
    551             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    552                 serde_json::json!({
    553                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    554                     "rawId": "ABABABABABABABABABABAA",
    555                     "response": {
    556                         "clientDataJSON": b64_cdata,
    557                         "authenticatorData": b64_adata,
    558                         "signature": b64_sig,
    559                         "userHandle": b64_user,
    560                     },
    561                     "authenticatorAttachment": "cross-platform",
    562                     "clientExtensionResults": {},
    563                     "type": "public-key"
    564                 })
    565                 .to_string()
    566                 .as_str()
    567             )
    568             .unwrap_err()
    569             .to_string()
    570             .into_bytes()[..err.len()],
    571             err
    572         );
    573         // missing `id`.
    574         err = Error::missing_field("id").to_string().into_bytes();
    575         assert_eq!(
    576             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    577                 serde_json::json!({
    578                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    579                     "response": {
    580                         "clientDataJSON": b64_cdata,
    581                         "authenticatorData": b64_adata,
    582                         "signature": b64_sig,
    583                         "userHandle": b64_user,
    584                     },
    585                     "authenticatorAttachment": "cross-platform",
    586                     "clientExtensionResults": {},
    587                     "type": "public-key"
    588                 })
    589                 .to_string()
    590                 .as_str()
    591             )
    592             .unwrap_err()
    593             .to_string()
    594             .into_bytes()[..err.len()],
    595             err
    596         );
    597         // `null` `id`.
    598         err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId")
    599             .to_string()
    600             .into_bytes();
    601         assert_eq!(
    602             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    603                 serde_json::json!({
    604                     "id": null,
    605                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    606                     "response": {
    607                         "clientDataJSON": b64_cdata,
    608                         "authenticatorData": b64_adata,
    609                         "signature": b64_sig,
    610                         "userHandle": b64_user,
    611                     },
    612                     "clientExtensionResults": {},
    613                     "type": "public-key"
    614                 })
    615                 .to_string()
    616                 .as_str()
    617             )
    618             .unwrap_err()
    619             .to_string()
    620             .into_bytes()[..err.len()],
    621             err
    622         );
    623         // missing `rawId`.
    624         assert!(
    625             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    626                 serde_json::json!({
    627                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    628                     "response": {
    629                         "clientDataJSON": b64_cdata,
    630                         "authenticatorData": b64_adata,
    631                         "signature": b64_sig,
    632                         "userHandle": b64_user,
    633                     },
    634                     "clientExtensionResults": {},
    635                     "type": "public-key"
    636                 })
    637                 .to_string()
    638                 .as_str()
    639             )
    640             .is_ok()
    641         );
    642         // `null` `rawId`.
    643         err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId")
    644             .to_string()
    645             .into_bytes();
    646         assert_eq!(
    647             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    648                 serde_json::json!({
    649                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    650                     "rawId": null,
    651                     "response": {
    652                         "clientDataJSON": b64_cdata,
    653                         "authenticatorData": b64_adata,
    654                         "signature": b64_sig,
    655                         "userHandle": b64_user,
    656                     },
    657                     "clientExtensionResults": {},
    658                     "type": "public-key"
    659                 })
    660                 .to_string()
    661                 .as_str()
    662             )
    663             .unwrap_err()
    664             .to_string()
    665             .into_bytes()[..err.len()],
    666             err
    667         );
    668         // Missing `authenticatorData`.
    669         err = Error::missing_field("authenticatorData")
    670             .to_string()
    671             .into_bytes();
    672         assert_eq!(
    673             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    674                 serde_json::json!({
    675                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    676                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    677                     "response": {
    678                         "clientDataJSON": b64_cdata,
    679                         "signature": b64_sig,
    680                         "userHandle": b64_user,
    681                     },
    682                     "clientExtensionResults": {},
    683                     "type": "public-key"
    684                 })
    685                 .to_string()
    686                 .as_str()
    687             )
    688             .unwrap_err()
    689             .to_string()
    690             .into_bytes()[..err.len()],
    691             err
    692         );
    693         // `null` `authenticatorData`.
    694         err = Error::invalid_type(Unexpected::Other("null"), &"AuthenticatorData")
    695             .to_string()
    696             .into_bytes();
    697         assert_eq!(
    698             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    699                 serde_json::json!({
    700                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    701                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    702                     "response": {
    703                         "clientDataJSON": b64_cdata,
    704                         "authenticatorData": null,
    705                         "signature": b64_sig,
    706                         "userHandle": b64_user,
    707                     },
    708                     "clientExtensionResults": {},
    709                     "type": "public-key"
    710                 })
    711                 .to_string()
    712                 .as_str()
    713             )
    714             .unwrap_err()
    715             .to_string()
    716             .into_bytes()[..err.len()],
    717             err
    718         );
    719         // Missing `signature`.
    720         err = Error::missing_field("signature").to_string().into_bytes();
    721         assert_eq!(
    722             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    723                 serde_json::json!({
    724                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    725                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    726                     "response": {
    727                         "clientDataJSON": b64_cdata,
    728                         "authenticatorData": b64_adata,
    729                         "userHandle": b64_user,
    730                     },
    731                     "clientExtensionResults": {},
    732                     "type": "public-key"
    733                 })
    734                 .to_string()
    735                 .as_str()
    736             )
    737             .unwrap_err()
    738             .to_string()
    739             .into_bytes()[..err.len()],
    740             err
    741         );
    742         // `null` `signature`.
    743         err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data")
    744             .to_string()
    745             .into_bytes();
    746         assert_eq!(
    747             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    748                 serde_json::json!({
    749                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    750                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    751                     "response": {
    752                         "clientDataJSON": b64_cdata,
    753                         "authenticatorData": b64_adata,
    754                         "signature": null,
    755                         "userHandle": b64_user,
    756                     },
    757                     "clientExtensionResults": {},
    758                     "type": "public-key"
    759                 })
    760                 .to_string()
    761                 .as_str()
    762             )
    763             .unwrap_err()
    764             .to_string()
    765             .into_bytes()[..err.len()],
    766             err
    767         );
    768         // Missing `userHandle`.
    769         assert!(
    770             serde_json::from_str::<
    771                 AuthenticationRelaxed<Option<UserHandle<[u8; USER_HANDLE_MIN_LEN]>>>,
    772             >(
    773                 serde_json::json!({
    774                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    775                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    776                     "response": {
    777                         "clientDataJSON": b64_cdata,
    778                         "authenticatorData": b64_adata,
    779                         "signature": b64_sig,
    780                     },
    781                     "clientExtensionResults": {},
    782                     "type": "public-key"
    783                 })
    784                 .to_string()
    785                 .as_str()
    786             )
    787             .is_ok()
    788         );
    789         // `null` `userHandle`.
    790         assert!(
    791             serde_json::from_str::<
    792                 AuthenticationRelaxed<Option<UserHandle<[u8; USER_HANDLE_MIN_LEN]>>>,
    793             >(
    794                 serde_json::json!({
    795                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    796                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    797                     "response": {
    798                         "clientDataJSON": b64_cdata,
    799                         "authenticatorData": b64_adata,
    800                         "signature": b64_sig,
    801                         "userHandle": null,
    802                     },
    803                     "clientExtensionResults": {},
    804                     "type": "public-key"
    805                 })
    806                 .to_string()
    807                 .as_str()
    808             )
    809             .is_ok()
    810         );
    811         // `null` `authenticatorAttachment`.
    812         assert!(
    813             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    814                 serde_json::json!({
    815                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    816                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    817                     "response": {
    818                         "clientDataJSON": b64_cdata,
    819                         "authenticatorData": b64_adata,
    820                         "signature": b64_sig,
    821                         "userHandle": b64_user,
    822                     },
    823                     "authenticatorAttachment": null,
    824                     "clientExtensionResults": {},
    825                     "type": "public-key"
    826                 })
    827                 .to_string()
    828                 .as_str()
    829             )
    830             .map_or(false, |auth| matches!(
    831                 auth.0.authenticator_attachment,
    832                 AuthenticatorAttachment::None
    833             ))
    834         );
    835         // Unknown `authenticatorAttachment`.
    836         err = Error::invalid_value(
    837             Unexpected::Str("Platform"),
    838             &"'platform' or 'cross-platform'",
    839         )
    840         .to_string()
    841         .into_bytes();
    842         assert_eq!(
    843             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    844                 serde_json::json!({
    845                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    846                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    847                     "response": {
    848                         "clientDataJSON": b64_cdata,
    849                         "authenticatorData": b64_adata,
    850                         "signature": b64_sig,
    851                         "userHandle": b64_user,
    852                     },
    853                     "authenticatorAttachment": "Platform",
    854                     "clientExtensionResults": {},
    855                     "type": "public-key"
    856                 })
    857                 .to_string()
    858                 .as_str()
    859             )
    860             .unwrap_err()
    861             .to_string()
    862             .into_bytes()[..err.len()],
    863             err
    864         );
    865         // Missing `clientDataJSON`.
    866         err = Error::missing_field("clientDataJSON")
    867             .to_string()
    868             .into_bytes();
    869         assert_eq!(
    870             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    871                 serde_json::json!({
    872                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    873                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    874                     "response": {
    875                         "authenticatorData": b64_adata,
    876                         "signature": b64_sig,
    877                         "userHandle": b64_user,
    878                     },
    879                     "clientExtensionResults": {},
    880                     "type": "public-key"
    881                 })
    882                 .to_string()
    883                 .as_str()
    884             )
    885             .unwrap_err()
    886             .to_string()
    887             .into_bytes()[..err.len()],
    888             err
    889         );
    890         // `null` `clientDataJSON`.
    891         err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data")
    892             .to_string()
    893             .into_bytes();
    894         assert_eq!(
    895             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    896                 serde_json::json!({
    897                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    898                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    899                     "response": {
    900                         "clientDataJSON": null,
    901                         "authenticatorData": b64_adata,
    902                         "signature": b64_sig,
    903                         "userHandle": b64_user,
    904                     },
    905                     "clientExtensionResults": {},
    906                     "type": "public-key"
    907                 })
    908                 .to_string()
    909                 .as_str()
    910             )
    911             .unwrap_err()
    912             .to_string()
    913             .into_bytes()[..err.len()],
    914             err
    915         );
    916         // Missing `response`.
    917         err = Error::missing_field("response").to_string().into_bytes();
    918         assert_eq!(
    919             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    920                 serde_json::json!({
    921                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    922                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    923                     "clientExtensionResults": {},
    924                     "type": "public-key"
    925                 })
    926                 .to_string()
    927                 .as_str()
    928             )
    929             .unwrap_err()
    930             .to_string()
    931             .into_bytes()[..err.len()],
    932             err
    933         );
    934         // `null` `response`.
    935         err = Error::invalid_type(Unexpected::Other("null"), &"AuthenticatorAssertion")
    936             .to_string()
    937             .into_bytes();
    938         assert_eq!(
    939             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    940                 serde_json::json!({
    941                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    942                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    943                     "response": null,
    944                     "clientExtensionResults": {},
    945                     "type": "public-key"
    946                 })
    947                 .to_string()
    948                 .as_str()
    949             )
    950             .unwrap_err()
    951             .to_string()
    952             .into_bytes()[..err.len()],
    953             err
    954         );
    955         // Empty `response`.
    956         err = Error::missing_field("clientDataJSON")
    957             .to_string()
    958             .into_bytes();
    959         assert_eq!(
    960             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    961                 serde_json::json!({
    962                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    963                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    964                     "response": {},
    965                     "clientExtensionResults": {},
    966                     "type": "public-key"
    967                 })
    968                 .to_string()
    969                 .as_str()
    970             )
    971             .unwrap_err()
    972             .to_string()
    973             .into_bytes()[..err.len()],
    974             err
    975         );
    976         // Missing `clientExtensionResults`.
    977         assert!(
    978             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    979                 serde_json::json!({
    980                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    981                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    982                     "response": {
    983                         "clientDataJSON": b64_cdata,
    984                         "authenticatorData": b64_adata,
    985                         "signature": b64_sig,
    986                         "userHandle": b64_user,
    987                     },
    988                     "type": "public-key"
    989                 })
    990                 .to_string()
    991                 .as_str()
    992             )
    993             .is_ok()
    994         );
    995         // `null` `clientExtensionResults`.
    996         assert!(
    997             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
    998                 serde_json::json!({
    999                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1000                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1001                     "response": {
   1002                         "clientDataJSON": b64_cdata,
   1003                         "authenticatorData": b64_adata,
   1004                         "signature": b64_sig,
   1005                         "userHandle": b64_user,
   1006                     },
   1007                     "clientExtensionResults": null,
   1008                     "type": "public-key"
   1009                 })
   1010                 .to_string()
   1011                 .as_str()
   1012             )
   1013             .is_ok()
   1014         );
   1015         // Missing `type`.
   1016         assert!(
   1017             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1018                 serde_json::json!({
   1019                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1020                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1021                     "response": {
   1022                         "clientDataJSON": b64_cdata,
   1023                         "authenticatorData": b64_adata,
   1024                         "signature": b64_sig,
   1025                         "userHandle": b64_user,
   1026                     },
   1027                     "clientExtensionResults": {},
   1028                 })
   1029                 .to_string()
   1030                 .as_str()
   1031             )
   1032             .is_ok()
   1033         );
   1034         // `null` `type`.
   1035         err = Error::invalid_type(Unexpected::Other("null"), &"public-key")
   1036             .to_string()
   1037             .into_bytes();
   1038         assert_eq!(
   1039             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1040                 serde_json::json!({
   1041                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1042                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1043                     "response": {
   1044                         "clientDataJSON": b64_cdata,
   1045                         "authenticatorData": b64_adata,
   1046                         "signature": b64_sig,
   1047                         "userHandle": b64_user,
   1048                     },
   1049                     "clientExtensionResults": {},
   1050                     "type": null
   1051                 })
   1052                 .to_string()
   1053                 .as_str()
   1054             )
   1055             .unwrap_err()
   1056             .to_string()
   1057             .into_bytes()[..err.len()],
   1058             err
   1059         );
   1060         // Not exactly `public-type` `type`.
   1061         err = Error::invalid_value(Unexpected::Str("Public-key"), &"public-key")
   1062             .to_string()
   1063             .into_bytes();
   1064         assert_eq!(
   1065             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1066                 serde_json::json!({
   1067                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1068                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1069                     "response": {
   1070                         "clientDataJSON": b64_cdata,
   1071                         "authenticatorData": b64_adata,
   1072                         "signature": b64_sig,
   1073                         "userHandle": b64_user,
   1074                     },
   1075                     "clientExtensionResults": {},
   1076                     "type": "Public-key"
   1077                 })
   1078                 .to_string()
   1079                 .as_str()
   1080             )
   1081             .unwrap_err()
   1082             .to_string()
   1083             .into_bytes()[..err.len()],
   1084             err
   1085         );
   1086         // `null`.
   1087         err = Error::invalid_type(Unexpected::Other("null"), &"PublicKeyCredential")
   1088             .to_string()
   1089             .into_bytes();
   1090         assert_eq!(
   1091             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1092                 serde_json::json!(null).to_string().as_str()
   1093             )
   1094             .unwrap_err()
   1095             .to_string()
   1096             .into_bytes()[..err.len()],
   1097             err
   1098         );
   1099         // Empty.
   1100         err = Error::missing_field("response").to_string().into_bytes();
   1101         assert_eq!(
   1102             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1103                 serde_json::json!({}).to_string().as_str()
   1104             )
   1105             .unwrap_err()
   1106             .to_string()
   1107             .into_bytes()[..err.len()],
   1108             err
   1109         );
   1110         // Unknown field in `response`.
   1111         assert!(
   1112             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1113                 serde_json::json!({
   1114                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1115                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1116                     "response": {
   1117                         "clientDataJSON": b64_cdata,
   1118                         "authenticatorData": b64_adata,
   1119                         "signature": b64_sig,
   1120                         "userHandle": b64_user,
   1121                         "foo": true,
   1122                     },
   1123                     "clientExtensionResults": {},
   1124                     "type": "public-key"
   1125                 })
   1126                 .to_string()
   1127                 .as_str()
   1128             )
   1129             .is_ok()
   1130         );
   1131         // Duplicate field in `response`.
   1132         err = Error::duplicate_field("userHandle")
   1133             .to_string()
   1134             .into_bytes();
   1135         assert_eq!(
   1136             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1137                 format!(
   1138                     "{{
   1139                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1140                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1141                        \"response\": {{
   1142                            \"clientDataJSON\": \"{b64_cdata}\",
   1143                            \"authenticatorData\": \"{b64_adata}\",
   1144                            \"signature\": \"{b64_sig}\",
   1145                            \"userHandle\": \"{b64_user}\",
   1146                            \"userHandle\": \"{b64_user}\"
   1147                        }},
   1148                        \"clientExtensionResults\": {{}},
   1149                        \"type\": \"public-key\"
   1150 
   1151                      }}"
   1152                 )
   1153                 .as_str()
   1154             )
   1155             .unwrap_err()
   1156             .to_string()
   1157             .into_bytes()[..err.len()],
   1158             err
   1159         );
   1160         // Unknown field in `PublicKeyCredential`.
   1161         assert!(
   1162             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1163                 serde_json::json!({
   1164                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1165                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1166                     "response": {
   1167                         "clientDataJSON": b64_cdata,
   1168                         "authenticatorData": b64_adata,
   1169                         "signature": b64_sig,
   1170                         "userHandle": b64_user,
   1171                     },
   1172                     "clientExtensionResults": {},
   1173                     "type": "public-key",
   1174                     "foo": true,
   1175                 })
   1176                 .to_string()
   1177                 .as_str()
   1178             )
   1179             .is_ok()
   1180         );
   1181         // Duplicate field in `PublicKeyCredential`.
   1182         err = Error::duplicate_field("id").to_string().into_bytes();
   1183         assert_eq!(
   1184             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1185                 format!(
   1186                     "{{
   1187                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1188                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1189                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1190                        \"response\": {{
   1191                            \"clientDataJSON\": \"{b64_cdata}\",
   1192                            \"authenticatorData\": \"{b64_adata}\",
   1193                            \"signature\": \"{b64_sig}\",
   1194                            \"userHandle\": \"{b64_user}\"
   1195                        }},
   1196                        \"clientExtensionResults\": {{}},
   1197                        \"type\": \"public-key\"
   1198 
   1199                      }}"
   1200                 )
   1201                 .as_str()
   1202             )
   1203             .unwrap_err()
   1204             .to_string()
   1205             .into_bytes()[..err.len()],
   1206             err
   1207         );
   1208         // Base case is valid.
   1209         assert!(
   1210             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1211                 serde_json::json!({
   1212                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1213                     "clientDataJSON": b64_cdata,
   1214                     "authenticatorData": b64_adata,
   1215                     "signature": b64_sig,
   1216                     "userHandle": b64_user,
   1217                     "authenticatorAttachment": "cross-platform",
   1218                     "clientExtensionResults": {},
   1219                     "type": "public-key"
   1220                 })
   1221                 .to_string()
   1222                 .as_str()
   1223             )
   1224             .map_or(false, |auth| auth.0.response.client_data_json
   1225                 == c_data_json.as_bytes()
   1226                 && auth.0.response.authenticator_data_and_c_data_hash[..37] == auth_data
   1227                 && auth.0.response.authenticator_data_and_c_data_hash[37..]
   1228                     == *Sha256::digest(c_data_json.as_bytes()).as_slice()
   1229                 && matches!(
   1230                     auth.0.authenticator_attachment,
   1231                     AuthenticatorAttachment::CrossPlatform
   1232                 ))
   1233         );
   1234         // missing `id`.
   1235         err = Error::missing_field("id").to_string().into_bytes();
   1236         assert_eq!(
   1237             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1238                 serde_json::json!({
   1239                     "clientDataJSON": b64_cdata,
   1240                     "authenticatorData": b64_adata,
   1241                     "signature": b64_sig,
   1242                     "userHandle": b64_user,
   1243                     "authenticatorAttachment": "cross-platform",
   1244                     "clientExtensionResults": {},
   1245                     "type": "public-key"
   1246                 })
   1247                 .to_string()
   1248                 .as_str()
   1249             )
   1250             .unwrap_err()
   1251             .to_string()
   1252             .into_bytes()[..err.len()],
   1253             err
   1254         );
   1255         // `null` `id`.
   1256         err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId")
   1257             .to_string()
   1258             .into_bytes();
   1259         assert_eq!(
   1260             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1261                 serde_json::json!({
   1262                     "id": null,
   1263                     "clientDataJSON": b64_cdata,
   1264                     "authenticatorData": b64_adata,
   1265                     "signature": b64_sig,
   1266                     "userHandle": b64_user,
   1267                     "clientExtensionResults": {},
   1268                     "type": "public-key"
   1269                 })
   1270                 .to_string()
   1271                 .as_str()
   1272             )
   1273             .unwrap_err()
   1274             .to_string()
   1275             .into_bytes()[..err.len()],
   1276             err
   1277         );
   1278         // Missing `authenticatorData`.
   1279         err = Error::missing_field("authenticatorData")
   1280             .to_string()
   1281             .into_bytes();
   1282         assert_eq!(
   1283             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1284                 serde_json::json!({
   1285                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1286                     "clientDataJSON": b64_cdata,
   1287                     "signature": b64_sig,
   1288                     "userHandle": b64_user,
   1289                     "clientExtensionResults": {},
   1290                     "type": "public-key"
   1291                 })
   1292                 .to_string()
   1293                 .as_str()
   1294             )
   1295             .unwrap_err()
   1296             .to_string()
   1297             .into_bytes()[..err.len()],
   1298             err
   1299         );
   1300         // `null` `authenticatorData`.
   1301         err = Error::invalid_type(Unexpected::Other("null"), &"AuthenticatorData")
   1302             .to_string()
   1303             .into_bytes();
   1304         assert_eq!(
   1305             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1306                 serde_json::json!({
   1307                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1308                     "clientDataJSON": b64_cdata,
   1309                     "authenticatorData": null,
   1310                     "signature": b64_sig,
   1311                     "userHandle": b64_user,
   1312                     "clientExtensionResults": {},
   1313                     "type": "public-key"
   1314                 })
   1315                 .to_string()
   1316                 .as_str()
   1317             )
   1318             .unwrap_err()
   1319             .to_string()
   1320             .into_bytes()[..err.len()],
   1321             err
   1322         );
   1323         // Missing `signature`.
   1324         err = Error::missing_field("signature").to_string().into_bytes();
   1325         assert_eq!(
   1326             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1327                 serde_json::json!({
   1328                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1329                     "clientDataJSON": b64_cdata,
   1330                     "authenticatorData": b64_adata,
   1331                     "userHandle": b64_user,
   1332                     "clientExtensionResults": {},
   1333                     "type": "public-key"
   1334                 })
   1335                 .to_string()
   1336                 .as_str()
   1337             )
   1338             .unwrap_err()
   1339             .to_string()
   1340             .into_bytes()[..err.len()],
   1341             err
   1342         );
   1343         // `null` `signature`.
   1344         err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data")
   1345             .to_string()
   1346             .into_bytes();
   1347         assert_eq!(
   1348             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1349                 serde_json::json!({
   1350                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1351                     "clientDataJSON": b64_cdata,
   1352                     "authenticatorData": b64_adata,
   1353                     "signature": null,
   1354                     "userHandle": b64_user,
   1355                     "clientExtensionResults": {},
   1356                     "type": "public-key"
   1357                 })
   1358                 .to_string()
   1359                 .as_str()
   1360             )
   1361             .unwrap_err()
   1362             .to_string()
   1363             .into_bytes()[..err.len()],
   1364             err
   1365         );
   1366         // Missing `userHandle`.
   1367         assert!(
   1368             serde_json::from_str::<
   1369                 CustomAuthentication<Option<UserHandle<[u8; USER_HANDLE_MIN_LEN]>>>,
   1370             >(
   1371                 serde_json::json!({
   1372                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1373                     "clientDataJSON": b64_cdata,
   1374                     "authenticatorData": b64_adata,
   1375                     "signature": b64_sig,
   1376                     "clientExtensionResults": {},
   1377                     "type": "public-key"
   1378                 })
   1379                 .to_string()
   1380                 .as_str()
   1381             )
   1382             .is_ok()
   1383         );
   1384         // `null` `userHandle`.
   1385         assert!(
   1386             serde_json::from_str::<
   1387                 CustomAuthentication<Option<UserHandle<[u8; USER_HANDLE_MIN_LEN]>>>,
   1388             >(
   1389                 serde_json::json!({
   1390                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1391                     "clientDataJSON": b64_cdata,
   1392                     "authenticatorData": b64_adata,
   1393                     "signature": b64_sig,
   1394                     "userHandle": null,
   1395                     "clientExtensionResults": {},
   1396                     "type": "public-key"
   1397                 })
   1398                 .to_string()
   1399                 .as_str()
   1400             )
   1401             .is_ok()
   1402         );
   1403         // `null` `authenticatorAttachment`.
   1404         assert!(
   1405             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1406                 serde_json::json!({
   1407                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1408                     "clientDataJSON": b64_cdata,
   1409                     "authenticatorData": b64_adata,
   1410                     "signature": b64_sig,
   1411                     "userHandle": b64_user,
   1412                     "authenticatorAttachment": null,
   1413                     "clientExtensionResults": {},
   1414                     "type": "public-key"
   1415                 })
   1416                 .to_string()
   1417                 .as_str()
   1418             )
   1419             .map_or(false, |auth| matches!(
   1420                 auth.0.authenticator_attachment,
   1421                 AuthenticatorAttachment::None
   1422             ))
   1423         );
   1424         // Unknown `authenticatorAttachment`.
   1425         err = Error::invalid_value(
   1426             Unexpected::Str("Platform"),
   1427             &"'platform' or 'cross-platform'",
   1428         )
   1429         .to_string()
   1430         .into_bytes();
   1431         assert_eq!(
   1432             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1433                 serde_json::json!({
   1434                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1435                     "clientDataJSON": b64_cdata,
   1436                     "authenticatorData": b64_adata,
   1437                     "signature": b64_sig,
   1438                     "userHandle": b64_user,
   1439                     "authenticatorAttachment": "Platform",
   1440                     "clientExtensionResults": {},
   1441                     "type": "public-key"
   1442                 })
   1443                 .to_string()
   1444                 .as_str()
   1445             )
   1446             .unwrap_err()
   1447             .to_string()
   1448             .into_bytes()[..err.len()],
   1449             err
   1450         );
   1451         // Missing `clientDataJSON`.
   1452         err = Error::missing_field("clientDataJSON")
   1453             .to_string()
   1454             .into_bytes();
   1455         assert_eq!(
   1456             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1457                 serde_json::json!({
   1458                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1459                     "authenticatorData": b64_adata,
   1460                     "signature": b64_sig,
   1461                     "userHandle": b64_user,
   1462                     "clientExtensionResults": {},
   1463                     "type": "public-key"
   1464                 })
   1465                 .to_string()
   1466                 .as_str()
   1467             )
   1468             .unwrap_err()
   1469             .to_string()
   1470             .into_bytes()[..err.len()],
   1471             err
   1472         );
   1473         // `null` `clientDataJSON`.
   1474         err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data")
   1475             .to_string()
   1476             .into_bytes();
   1477         assert_eq!(
   1478             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1479                 serde_json::json!({
   1480                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1481                     "clientDataJSON": null,
   1482                     "authenticatorData": b64_adata,
   1483                     "signature": b64_sig,
   1484                     "userHandle": b64_user,
   1485                     "clientExtensionResults": {},
   1486                     "type": "public-key"
   1487                 })
   1488                 .to_string()
   1489                 .as_str()
   1490             )
   1491             .unwrap_err()
   1492             .to_string()
   1493             .into_bytes()[..err.len()],
   1494             err
   1495         );
   1496         // Empty.
   1497         err = Error::missing_field("authenticatorData")
   1498             .to_string()
   1499             .into_bytes();
   1500         assert_eq!(
   1501             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1502                 serde_json::json!({}).to_string().as_str()
   1503             )
   1504             .unwrap_err()
   1505             .to_string()
   1506             .into_bytes()[..err.len()],
   1507             err
   1508         );
   1509         // Missing `clientExtensionResults`.
   1510         err = Error::missing_field("clientExtensionResults")
   1511             .to_string()
   1512             .into_bytes();
   1513         assert_eq!(
   1514             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1515                 serde_json::json!({
   1516                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1517                     "clientDataJSON": b64_cdata,
   1518                     "authenticatorData": b64_adata,
   1519                     "signature": b64_sig,
   1520                     "userHandle": b64_user,
   1521                     "type": "public-key"
   1522                 })
   1523                 .to_string()
   1524                 .as_str()
   1525             )
   1526             .unwrap_err()
   1527             .to_string()
   1528             .into_bytes()[..err.len()],
   1529             err
   1530         );
   1531         // `null` `clientExtensionResults`.
   1532         err = Error::invalid_type(Unexpected::Other("null"), &"ClientExtensionsOutputs")
   1533             .to_string()
   1534             .into_bytes();
   1535         assert_eq!(
   1536             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1537                 serde_json::json!({
   1538                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1539                     "clientDataJSON": b64_cdata,
   1540                     "authenticatorData": b64_adata,
   1541                     "signature": b64_sig,
   1542                     "userHandle": b64_user,
   1543                     "clientExtensionResults": null,
   1544                     "type": "public-key"
   1545                 })
   1546                 .to_string()
   1547                 .as_str()
   1548             )
   1549             .unwrap_err()
   1550             .to_string()
   1551             .into_bytes()[..err.len()],
   1552             err
   1553         );
   1554         assert!(
   1555             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1556                 serde_json::json!({
   1557                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1558                     "clientDataJSON": b64_cdata,
   1559                     "authenticatorData": b64_adata,
   1560                     "signature": b64_sig,
   1561                     "userHandle": b64_user,
   1562                     "clientExtensionResults": {},
   1563                 })
   1564                 .to_string()
   1565                 .as_str()
   1566             )
   1567             .is_ok()
   1568         );
   1569         // `null` `type`.
   1570         err = Error::invalid_type(Unexpected::Other("null"), &"public-key")
   1571             .to_string()
   1572             .into_bytes();
   1573         assert_eq!(
   1574             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1575                 serde_json::json!({
   1576                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1577                     "clientDataJSON": b64_cdata,
   1578                     "authenticatorData": b64_adata,
   1579                     "signature": b64_sig,
   1580                     "userHandle": b64_user,
   1581                     "clientExtensionResults": {},
   1582                     "type": null
   1583                 })
   1584                 .to_string()
   1585                 .as_str()
   1586             )
   1587             .unwrap_err()
   1588             .to_string()
   1589             .into_bytes()[..err.len()],
   1590             err
   1591         );
   1592         // Not exactly `public-type` `type`.
   1593         err = Error::invalid_value(Unexpected::Str("Public-key"), &"public-key")
   1594             .to_string()
   1595             .into_bytes();
   1596         assert_eq!(
   1597             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1598                 serde_json::json!({
   1599                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1600                     "clientDataJSON": b64_cdata,
   1601                     "authenticatorData": b64_adata,
   1602                     "signature": b64_sig,
   1603                     "userHandle": b64_user,
   1604                     "clientExtensionResults": {},
   1605                     "type": "Public-key"
   1606                 })
   1607                 .to_string()
   1608                 .as_str()
   1609             )
   1610             .unwrap_err()
   1611             .to_string()
   1612             .into_bytes()[..err.len()],
   1613             err
   1614         );
   1615         // `null`.
   1616         err = Error::invalid_type(Unexpected::Other("null"), &"CustomAuthentication")
   1617             .to_string()
   1618             .into_bytes();
   1619         assert_eq!(
   1620             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1621                 serde_json::json!(null).to_string().as_str()
   1622             )
   1623             .unwrap_err()
   1624             .to_string()
   1625             .into_bytes()[..err.len()],
   1626             err
   1627         );
   1628         // Unknown field.
   1629         err = Error::unknown_field(
   1630             "foo",
   1631             [
   1632                 "authenticatorAttachment",
   1633                 "authenticatorData",
   1634                 "clientDataJSON",
   1635                 "clientExtensionResults",
   1636                 "id",
   1637                 "signature",
   1638                 "type",
   1639                 "userHandle",
   1640             ]
   1641             .as_slice(),
   1642         )
   1643         .to_string()
   1644         .into_bytes();
   1645         assert_eq!(
   1646             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1647                 serde_json::json!({
   1648                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1649                     "clientDataJSON": b64_cdata,
   1650                     "authenticatorData": b64_adata,
   1651                     "signature": b64_sig,
   1652                     "userHandle": b64_user,
   1653                     "foo": true,
   1654                     "clientExtensionResults": {},
   1655                     "type": "public-key"
   1656                 })
   1657                 .to_string()
   1658                 .as_str()
   1659             )
   1660             .unwrap_err()
   1661             .to_string()
   1662             .into_bytes()[..err.len()],
   1663             err
   1664         );
   1665         // Duplicate field.
   1666         err = Error::duplicate_field("userHandle")
   1667             .to_string()
   1668             .into_bytes();
   1669         assert_eq!(
   1670             serde_json::from_str::<PasskeyCustomAuthentication<[u8; USER_HANDLE_MIN_LEN]>>(
   1671                 format!(
   1672                     "{{
   1673                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1674                        \"clientDataJSON\": \"{b64_cdata}\",
   1675                        \"authenticatorData\": \"{b64_adata}\",
   1676                        \"signature\": \"{b64_sig}\",
   1677                        \"userHandle\": \"{b64_user}\",
   1678                        \"userHandle\": \"{b64_user}\"
   1679                        \"clientExtensionResults\": {{}},
   1680                        \"type\": \"public-key\"
   1681 
   1682                      }}"
   1683                 )
   1684                 .as_str()
   1685             )
   1686             .unwrap_err()
   1687             .to_string()
   1688             .into_bytes()[..err.len()],
   1689             err
   1690         );
   1691     }
   1692     #[test]
   1693     fn client_extensions() {
   1694         let c_data_json = serde_json::json!({}).to_string();
   1695         let auth_data = [
   1696             // `rpIdHash`.
   1697             0,
   1698             0,
   1699             0,
   1700             0,
   1701             0,
   1702             0,
   1703             0,
   1704             0,
   1705             0,
   1706             0,
   1707             0,
   1708             0,
   1709             0,
   1710             0,
   1711             0,
   1712             0,
   1713             0,
   1714             0,
   1715             0,
   1716             0,
   1717             0,
   1718             0,
   1719             0,
   1720             0,
   1721             0,
   1722             0,
   1723             0,
   1724             0,
   1725             0,
   1726             0,
   1727             0,
   1728             0,
   1729             // `flags`.
   1730             0b0000_0101,
   1731             // `signCount`.
   1732             0,
   1733             0,
   1734             0,
   1735             0,
   1736         ];
   1737         let b64_cdata = BASE64URL_NOPAD.encode(c_data_json.as_bytes());
   1738         let b64_adata = BASE64URL_NOPAD.encode(auth_data.as_slice());
   1739         let b64_sig = BASE64URL_NOPAD.encode([].as_slice());
   1740         let b64_user = BASE64URL_NOPAD.encode(b"\x00".as_slice());
   1741         // Base case is valid.
   1742         assert!(
   1743             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1744                 serde_json::json!({
   1745                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1746                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1747                     "response": {
   1748                         "clientDataJSON": b64_cdata,
   1749                         "authenticatorData": b64_adata,
   1750                         "signature": b64_sig,
   1751                         "userHandle": b64_user,
   1752                     },
   1753                     "authenticatorAttachment": "cross-platform",
   1754                     "clientExtensionResults": {},
   1755                     "type": "public-key"
   1756                 })
   1757                 .to_string()
   1758                 .as_str()
   1759             )
   1760             .map_or(false, |auth| auth.0.response.client_data_json
   1761                 == c_data_json.as_bytes()
   1762                 && auth.0.response.authenticator_data_and_c_data_hash[..37] == auth_data
   1763                 && auth.0.response.authenticator_data_and_c_data_hash[37..]
   1764                     == *Sha256::digest(c_data_json.as_bytes()).as_slice()
   1765                 && matches!(
   1766                     auth.0.authenticator_attachment,
   1767                     AuthenticatorAttachment::CrossPlatform
   1768                 ))
   1769         );
   1770         // `null` `prf`.
   1771         assert!(
   1772             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1773                 serde_json::json!({
   1774                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1775                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1776                     "response": {
   1777                         "clientDataJSON": b64_cdata,
   1778                         "authenticatorData": b64_adata,
   1779                         "signature": b64_sig,
   1780                         "userHandle": b64_user,
   1781                     },
   1782                     "clientExtensionResults": {
   1783                         "prf": null
   1784                     },
   1785                     "type": "public-key"
   1786                 })
   1787                 .to_string()
   1788                 .as_str()
   1789             )
   1790             .is_ok()
   1791         );
   1792         // Unknown `clientExtensionResults`.
   1793         assert!(
   1794             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1795                 serde_json::json!({
   1796                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1797                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1798                     "response": {
   1799                         "clientDataJSON": b64_cdata,
   1800                         "authenticatorData": b64_adata,
   1801                         "signature": b64_sig,
   1802                         "userHandle": b64_user,
   1803                     },
   1804                     "clientExtensionResults": {
   1805                         "Prf": null
   1806                     },
   1807                     "type": "public-key"
   1808                 })
   1809                 .to_string()
   1810                 .as_str()
   1811             )
   1812             .is_ok()
   1813         );
   1814         // Duplicate field.
   1815         let mut err = Error::duplicate_field("prf").to_string().into_bytes();
   1816         assert_eq!(
   1817             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1818                 format!(
   1819                     "{{
   1820                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1821                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1822                        \"response\": {{
   1823                            \"clientDataJSON\": \"{b64_cdata}\",
   1824                            \"authenticatorData\": \"{b64_adata}\",
   1825                            \"signature\": \"{b64_sig}\",
   1826                            \"userHandle\": \"{b64_user}\"
   1827                        }},
   1828                        \"clientExtensionResults\": {{
   1829                            \"prf\": null,
   1830                            \"prf\": null
   1831                        }},
   1832                        \"type\": \"public-key\"
   1833                      }}"
   1834                 )
   1835                 .as_str()
   1836             )
   1837             .unwrap_err()
   1838             .to_string()
   1839             .into_bytes()[..err.len()],
   1840             err
   1841         );
   1842         // `null` `results`.
   1843         assert!(
   1844             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1845                 serde_json::json!({
   1846                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1847                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1848                     "response": {
   1849                         "clientDataJSON": b64_cdata,
   1850                         "authenticatorData": b64_adata,
   1851                         "signature": b64_sig,
   1852                         "userHandle": b64_user,
   1853                     },
   1854                     "clientExtensionResults": {
   1855                         "prf": {
   1856                             "results": null,
   1857                         }
   1858                     },
   1859                     "type": "public-key"
   1860                 })
   1861                 .to_string()
   1862                 .as_str()
   1863             )
   1864             .is_ok()
   1865         );
   1866         // Duplicate field in `prf`.
   1867         err = Error::duplicate_field("results").to_string().into_bytes();
   1868         assert_eq!(
   1869             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1870                 format!(
   1871                     "{{
   1872                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1873                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1874                        \"response\": {{
   1875                            \"clientDataJSON\": \"{b64_cdata}\",
   1876                            \"authenticatorData\": \"{b64_adata}\",
   1877                            \"signature\": \"{b64_sig}\",
   1878                            \"userHandle\": \"{b64_user}\"
   1879                        }},
   1880                        \"clientExtensionResults\": {{
   1881                            \"prf\": {{
   1882                                \"results\": null,
   1883                                \"results\": null
   1884                            }}
   1885                        }},
   1886                        \"type\": \"public-key\"
   1887                      }}"
   1888                 )
   1889                 .as_str()
   1890             )
   1891             .unwrap_err()
   1892             .to_string()
   1893             .into_bytes()[..err.len()],
   1894             err
   1895         );
   1896         // Missing `first`.
   1897         assert!(
   1898             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1899                 serde_json::json!({
   1900                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1901                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1902                     "response": {
   1903                         "clientDataJSON": b64_cdata,
   1904                         "authenticatorData": b64_adata,
   1905                         "signature": b64_sig,
   1906                         "userHandle": b64_user,
   1907                     },
   1908                     "clientExtensionResults": {
   1909                         "prf": {
   1910                             "results": {},
   1911                         }
   1912                     },
   1913                     "type": "public-key"
   1914                 })
   1915                 .to_string()
   1916                 .as_str()
   1917             )
   1918             .is_ok()
   1919         );
   1920         // `null` `first`.
   1921         assert!(
   1922             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1923                 serde_json::json!({
   1924                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1925                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1926                     "response": {
   1927                         "clientDataJSON": b64_cdata,
   1928                         "authenticatorData": b64_adata,
   1929                         "signature": b64_sig,
   1930                         "userHandle": b64_user,
   1931                     },
   1932                     "clientExtensionResults": {
   1933                         "prf": {
   1934                             "results": {
   1935                                 "first": null
   1936                             },
   1937                         }
   1938                     },
   1939                     "type": "public-key"
   1940                 })
   1941                 .to_string()
   1942                 .as_str()
   1943             )
   1944             .is_ok()
   1945         );
   1946         // `null` `second`.
   1947         assert!(
   1948             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1949                 serde_json::json!({
   1950                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1951                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1952                     "response": {
   1953                         "clientDataJSON": b64_cdata,
   1954                         "authenticatorData": b64_adata,
   1955                         "signature": b64_sig,
   1956                         "userHandle": b64_user,
   1957                     },
   1958                     "clientExtensionResults": {
   1959                         "prf": {
   1960                             "results": {
   1961                                 "first": null,
   1962                                 "second": null
   1963                             },
   1964                         }
   1965                     },
   1966                     "type": "public-key"
   1967                 })
   1968                 .to_string()
   1969                 .as_str()
   1970             )
   1971             .is_ok()
   1972         );
   1973         // Non-`null` `first`.
   1974         err = Error::invalid_type(Unexpected::Option, &"null")
   1975             .to_string()
   1976             .into_bytes();
   1977         assert_eq!(
   1978             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   1979                 serde_json::json!({
   1980                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1981                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1982                     "response": {
   1983                         "clientDataJSON": b64_cdata,
   1984                         "authenticatorData": b64_adata,
   1985                         "signature": b64_sig,
   1986                         "userHandle": b64_user,
   1987                     },
   1988                     "clientExtensionResults": {
   1989                         "prf": {
   1990                             "results": {
   1991                                 "first": ""
   1992                             },
   1993                         }
   1994                     },
   1995                     "type": "public-key"
   1996                 })
   1997                 .to_string()
   1998                 .as_str()
   1999             )
   2000             .unwrap_err()
   2001             .to_string()
   2002             .into_bytes()[..err.len()],
   2003             err
   2004         );
   2005         // Non-`null` `second`.
   2006         err = Error::invalid_type(Unexpected::Option, &"null")
   2007             .to_string()
   2008             .into_bytes();
   2009         assert_eq!(
   2010             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   2011                 serde_json::json!({
   2012                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2013                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2014                     "response": {
   2015                         "clientDataJSON": b64_cdata,
   2016                         "authenticatorData": b64_adata,
   2017                         "signature": b64_sig,
   2018                         "userHandle": b64_user,
   2019                     },
   2020                     "clientExtensionResults": {
   2021                         "prf": {
   2022                             "results": {
   2023                                 "first": null,
   2024                                 "second": ""
   2025                             },
   2026                         }
   2027                     },
   2028                     "type": "public-key"
   2029                 })
   2030                 .to_string()
   2031                 .as_str()
   2032             )
   2033             .unwrap_err()
   2034             .to_string()
   2035             .into_bytes()[..err.len()],
   2036             err
   2037         );
   2038         // `enabled` is still not allowed.
   2039         err = Error::unknown_field("enabled", ["results"].as_slice())
   2040             .to_string()
   2041             .into_bytes();
   2042         assert_eq!(
   2043             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   2044                 serde_json::json!({
   2045                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2046                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2047                     "response": {
   2048                         "clientDataJSON": b64_cdata,
   2049                         "authenticatorData": b64_adata,
   2050                         "signature": b64_sig,
   2051                         "userHandle": b64_user,
   2052                     },
   2053                     "clientExtensionResults": {
   2054                         "prf": {
   2055                             "enabled": true,
   2056                             "results": null
   2057                         }
   2058                     },
   2059                     "type": "public-key"
   2060                 })
   2061                 .to_string()
   2062                 .as_str()
   2063             )
   2064             .unwrap_err()
   2065             .to_string()
   2066             .into_bytes()[..err.len()],
   2067             err
   2068         );
   2069         // Unknown `prf` field.
   2070         assert!(
   2071             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   2072                 serde_json::json!({
   2073                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2074                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2075                     "response": {
   2076                         "clientDataJSON": b64_cdata,
   2077                         "authenticatorData": b64_adata,
   2078                         "signature": b64_sig,
   2079                         "userHandle": b64_user,
   2080                     },
   2081                     "clientExtensionResults": {
   2082                         "prf": {
   2083                             "foo": true,
   2084                             "results": null
   2085                         }
   2086                     },
   2087                     "type": "public-key"
   2088                 })
   2089                 .to_string()
   2090                 .as_str()
   2091             )
   2092             .is_ok()
   2093         );
   2094         // Unknown `results` field.
   2095         assert!(
   2096             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   2097                 serde_json::json!({
   2098                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2099                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2100                     "response": {
   2101                         "clientDataJSON": b64_cdata,
   2102                         "authenticatorData": b64_adata,
   2103                         "signature": b64_sig,
   2104                         "userHandle": b64_user,
   2105                     },
   2106                     "clientExtensionResults": {
   2107                         "prf": {
   2108                             "results": {
   2109                                 "first": null,
   2110                                 "Second": null
   2111                             }
   2112                         }
   2113                     },
   2114                     "type": "public-key"
   2115                 })
   2116                 .to_string()
   2117                 .as_str()
   2118             )
   2119             .is_ok()
   2120         );
   2121         // Duplicate field in `results`.
   2122         err = Error::duplicate_field("first").to_string().into_bytes();
   2123         assert_eq!(
   2124             serde_json::from_str::<PasskeyAuthenticationRelaxed<[u8; USER_HANDLE_MIN_LEN]>>(
   2125                 format!(
   2126                     "{{
   2127                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   2128                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   2129                        \"response\": {{
   2130                            \"clientDataJSON\": \"{b64_cdata}\",
   2131                            \"authenticatorData\": \"{b64_adata}\",
   2132                            \"signature\": \"{b64_sig}\",
   2133                            \"userHandle\": \"{b64_user}\"
   2134                        }},
   2135                        \"clientExtensionResults\": {{
   2136                            \"prf\": {{
   2137                                \"results\": {{
   2138                                    \"first\": null,
   2139                                    \"first\": null
   2140                                }}
   2141                            }}
   2142                        }},
   2143                        \"type\": \"public-key\"
   2144                      }}"
   2145                 )
   2146                 .as_str()
   2147             )
   2148             .unwrap_err()
   2149             .to_string()
   2150             .into_bytes()[..err.len()],
   2151             err
   2152         );
   2153     }
   2154 }