webauthn_rp

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

ser_relaxed.rs (84669B)


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