webauthn_rp

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

ser_relaxed.rs (84769B)


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