webauthn_rp

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

ser_relaxed.rs (84898B)


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