webauthn_rp

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

ser_relaxed.rs (169826B)


      1 #[cfg(doc)]
      2 use super::super::{super::request::register::CoseAlgorithmIdentifier, Challenge, CredentialId};
      3 use super::{
      4     super::{
      5         register::ser::{
      6             AUTH_ATTEST_FIELDS, AttObj, AuthenticatorAttestationVisitor,
      7             ClientExtensionsOutputsVisitor, EXT_FIELDS,
      8         },
      9         ser::{
     10             AuthenticationExtensionsPrfOutputsHelper, Base64DecodedVal, ClientExtensions,
     11             PublicKeyCredential, Type,
     12         },
     13         ser_relaxed::AuthenticationExtensionsPrfValuesRelaxed,
     14     },
     15     AttestationObject, AuthenticationExtensionsPrfOutputs, AuthenticatorAttachment,
     16     AuthenticatorAttestation, ClientExtensionsOutputs, CredentialPropertiesOutput, Registration,
     17     ser::{AuthAttest, CredentialPropertiesOutputVisitor, PROPS_FIELDS},
     18 };
     19 use core::{
     20     fmt::{self, Formatter},
     21     marker::PhantomData,
     22 };
     23 use serde::de::{Deserialize, Deserializer, Error, MapAccess, Unexpected, Visitor};
     24 /// `newtype` around `CredentialPropertiesOutput` with a "relaxed" [`Self::deserialize`] implementation.
     25 #[derive(Clone, Copy, Debug)]
     26 pub struct CredentialPropertiesOutputRelaxed(pub CredentialPropertiesOutput);
     27 impl From<CredentialPropertiesOutputRelaxed> for CredentialPropertiesOutput {
     28     #[inline]
     29     fn from(value: CredentialPropertiesOutputRelaxed) -> Self {
     30         value.0
     31     }
     32 }
     33 impl<'de> Deserialize<'de> for CredentialPropertiesOutputRelaxed {
     34     /// Same as [`CredentialPropertiesOutput::deserialize`] except unknown keys are ignored.
     35     ///
     36     /// Note that duplicate keys are still forbidden.
     37     #[inline]
     38     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     39     where
     40         D: Deserializer<'de>,
     41     {
     42         deserializer
     43             .deserialize_struct(
     44                 "CredentialPropertiesOutputRelaxed",
     45                 PROPS_FIELDS,
     46                 CredentialPropertiesOutputVisitor::<true>,
     47             )
     48             .map(Self)
     49     }
     50 }
     51 /// `newtype` around `AuthenticationExtensionsPrfOutputs` with a "relaxed" [`Self::deserialize`] implementation.
     52 #[derive(Clone, Copy, Debug)]
     53 pub struct AuthenticationExtensionsPrfOutputsRelaxed(AuthenticationExtensionsPrfOutputs);
     54 impl From<AuthenticationExtensionsPrfOutputsRelaxed> for AuthenticationExtensionsPrfOutputs {
     55     #[inline]
     56     fn from(value: AuthenticationExtensionsPrfOutputsRelaxed) -> Self {
     57         value.0
     58     }
     59 }
     60 impl<'de> Deserialize<'de> for AuthenticationExtensionsPrfOutputsRelaxed {
     61     /// Same as [`AuthenticationExtensionsPrfOutputs::deserialize`] except unknown keys are ignored.
     62     ///
     63     /// Note that duplicate keys are still forbidden;
     64     /// [`enabled`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-enabled) must still exist
     65     /// (and not be `null`); and
     66     /// [`results`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-results) must not exist,
     67     /// be `null`, or be an
     68     /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues)
     69     /// such that unknown keys are ignored, duplicate keys are forbidden,
     70     /// [`first`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-first) is not required but
     71     /// if it exists it must be `null`, and
     72     /// [`second`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-second) can exist but
     73     /// must be `null` if so.
     74     #[inline]
     75     #[expect(clippy::unreachable, reason = "we want to crash when there is a bug")]
     76     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     77     where
     78         D: Deserializer<'de>,
     79     {
     80         AuthenticationExtensionsPrfOutputsHelper::<
     81             true,
     82             true,
     83             AuthenticationExtensionsPrfValuesRelaxed,
     84         >::deserialize(deserializer)
     85         .map(|v| {
     86             Self(AuthenticationExtensionsPrfOutputs {
     87                 enabled: v.0.unwrap_or_else(|| {
     88                     unreachable!(
     89                         "there is a bug in AuthenticationExtensionsPrfOutputsHelper::deserialize"
     90                     )
     91                 }),
     92             })
     93         })
     94     }
     95 }
     96 /// `newtype` around `ClientExtensionsOutputs` with a "relaxed" [`Self::deserialize`] implementation.
     97 #[derive(Clone, Copy, Debug)]
     98 pub struct ClientExtensionsOutputsRelaxed(pub ClientExtensionsOutputs);
     99 impl ClientExtensions for ClientExtensionsOutputsRelaxed {
    100     fn empty() -> Self {
    101         Self(ClientExtensionsOutputs::empty())
    102     }
    103 }
    104 impl<'de> Deserialize<'de> for ClientExtensionsOutputsRelaxed {
    105     /// Same as [`ClientExtensionsOutputs::deserialize`] except unknown keys are ignored,
    106     /// [`credProps`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsclientoutputs-credprops) is
    107     /// `null` or deserialized via [`CredentialPropertiesOutputRelaxed::deserialize`], and
    108     /// [`prf`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsclientoutputs-prf) is
    109     /// `null` or deserialized via [`AuthenticationExtensionsPrfOutputsRelaxed::deserialize`].
    110     ///
    111     /// Note that duplicate keys are still forbidden.
    112     #[inline]
    113     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    114     where
    115         D: Deserializer<'de>,
    116     {
    117         deserializer
    118             .deserialize_struct(
    119                 "ClientExtensionsOutputsRelaxed",
    120                 EXT_FIELDS,
    121                 ClientExtensionsOutputsVisitor::<
    122                     true,
    123                     CredentialPropertiesOutputRelaxed,
    124                     AuthenticationExtensionsPrfOutputsRelaxed,
    125                 >(PhantomData),
    126             )
    127             .map(Self)
    128     }
    129 }
    130 /// `newtype` around `AuthAttest` with a "relaxed" [`Self::deserialize`] implementation.
    131 struct AuthAttestRelaxed(pub AuthAttest);
    132 impl<'de> Deserialize<'de> for AuthAttestRelaxed {
    133     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    134     where
    135         D: Deserializer<'de>,
    136     {
    137         deserializer
    138             .deserialize_struct(
    139                 "AuthenticatorAttestation",
    140                 AUTH_ATTEST_FIELDS,
    141                 AuthenticatorAttestationVisitor::<true>,
    142             )
    143             .map(Self)
    144     }
    145 }
    146 /// `newtype` around `AuthenticatorAttestation` with a "relaxed" [`Self::deserialize`] implementation.
    147 #[derive(Debug)]
    148 pub struct AuthenticatorAttestationRelaxed(pub AuthenticatorAttestation);
    149 impl<'de> Deserialize<'de> for AuthenticatorAttestationRelaxed {
    150     /// Same as [`AuthenticatorAttestation::deserialize`] except unknown keys are ignored and only
    151     /// [`clientDataJSON`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-clientdatajson)
    152     /// and
    153     /// [`attestationObject`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-attestationobject)
    154     /// are required (and must not be `null`). For the other fields, they are allowed to not exist or be `null`.
    155     ///
    156     /// Note that duplicate keys are still forbidden, and data matching still applies when applicable.
    157     #[inline]
    158     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    159     where
    160         D: Deserializer<'de>,
    161     {
    162         AuthAttestRelaxed::deserialize(deserializer).map(|v| Self(v.0.attest))
    163     }
    164 }
    165 /// `newtype` around `Registration` with a "relaxed" [`Self::deserialize`] implementation.
    166 #[derive(Debug)]
    167 pub struct RegistrationRelaxed(pub Registration);
    168 impl<'de> Deserialize<'de> for RegistrationRelaxed {
    169     /// Same as [`Registration::deserialize`] except unknown keys are ignored,
    170     /// [`response`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-response) is deserialized
    171     /// via [`AuthenticatorAttestationRelaxed::deserialize`],
    172     /// [`clientExtensionResults`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-clientextensionresults)
    173     /// is `null` or deserialized via [`ClientExtensionsOutputsRelaxed::deserialize`], and only `response` is required.
    174     /// `id`, `rawId`, and `type` are allowed to not exist. For the other fields, they are allowed to not exist or
    175     /// be `null`.
    176     ///
    177     /// Note that duplicate keys are still forbidden, and data matching still applies when applicable.
    178     #[expect(clippy::indexing_slicing, reason = "comment justifies its correctness")]
    179     #[inline]
    180     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    181     where
    182         D: Deserializer<'de>,
    183     {
    184         PublicKeyCredential::<true, true, AuthAttestRelaxed, ClientExtensionsOutputsRelaxed>::deserialize(deserializer).and_then(|cred| {
    185             cred.id.map_or_else(|| Ok(()), |id| {
    186                 cred.response.0.cred_info.map_or_else(
    187                     || AttestationObject::try_from(cred.response.0.attest.attestation_object()).map_err(Error::custom).and_then(|att_obj| {
    188                         if id == att_obj.auth_data.attested_credential_data.credential_id {
    189                             Ok(())
    190                         } else {
    191                             Err(Error::invalid_value(Unexpected::Bytes(id.as_ref()), &format!("id, rawId, and the credential id in the attested credential data to all match: {:?}", att_obj.auth_data.attested_credential_data.credential_id.0).as_str()))
    192                         }
    193                     }),
    194                     // `start` and `last` were calculated based on `cred.response.attest.attestation_object()`
    195                     // and represent the starting and ending index of the `CredentialId`; therefore this is correct
    196                     // let alone won't `panic`.
    197                     |(start, last)| if id.0 == cred.response.0.attest.attestation_object()[start..last] {
    198                         Ok(())
    199                     } else {
    200                         Err(Error::invalid_value(Unexpected::Bytes(id.as_ref()), &format!("id, rawId, and the credential id in the attested credential data to all match: {:?}", &cred.response.0.attest.attestation_object()[start..last]).as_str()))
    201                     }
    202                 )
    203             }).map(|()| {
    204                 Self(Registration { response: cred.response.0.attest, authenticator_attachment: cred.authenticator_attachment, client_extension_results: cred.client_extension_results.0 })
    205             })
    206         })
    207     }
    208 }
    209 /// `newtype` around `Registration` with a custom [`Self::deserialize`] implementation.
    210 #[derive(Debug)]
    211 pub struct CustomRegistration(pub Registration);
    212 impl<'de> Deserialize<'de> for CustomRegistration {
    213     /// Despite the spec having a
    214     /// [pre-defined format](https://www.w3.org/TR/webauthn-3/#dictdef-registrationresponsejson) that clients
    215     /// can follow, the downside is the superfluous data it contains.
    216     ///
    217     /// There simply is no reason to send the [`CredentialId`] _four_ times. This redundant data puts RPs in
    218     /// a position where they either ignore the data or parse the data to ensure no contradictions exist
    219     /// (e.g., [FIDO conformance requires one to verify `id` and `rawId` exist and match](https://github.com/w3c/webauthn/issues/2119#issuecomment-2287875401)).
    220     ///
    221     /// While [`Registration::deserialize`] _strictly_ adheres to the JSON definition (e.g., it requires `publicKey`
    222     /// to exist and match with what is in both `authenticatorData` and `attestationObject` when the underlying
    223     /// algorithm is not [`CoseAlgorithmIdentifier::Es384`]), this implementation
    224     /// strictly disallows superfluous data. Specifically the following JSON is required to be sent where duplicate
    225     /// and unknown keys are disallowed:
    226     ///
    227     /// ```json
    228     /// {
    229     ///   "attestationObject": <base64url string>,
    230     ///   "authenticatorAttachment": null | "platform" | "cross-platform",
    231     ///   "clientDataJSON": <base64url string>,
    232     ///   "clientExtensionResults": <see ClientExtensionsOutputs::deserialize>,
    233     ///   "transports": <see AuthTransports::deserialize>,
    234     ///   "type": "public-key"
    235     /// }
    236     /// ```
    237     ///
    238     /// All of the above keys are required with the exceptions of `"authenticatorAttachment"` and `"type"`.
    239     ///
    240     /// # Examples
    241     ///
    242     /// ```
    243     /// # use webauthn_rp::response::register::ser_relaxed::CustomRegistration;
    244     /// assert!(
    245     ///     // The below payload is technically valid, but `RegistrationServerState::verify` will fail
    246     ///     // since the attestationObject is not valid. This is true for `Registration::deserialize`
    247     ///     // as well since attestationObject parsing is always deferred.
    248     ///     serde_json::from_str::<CustomRegistration>(
    249     ///         r#"{
    250     ///             "transports": ["usb"],
    251     ///             "attestationObject": "AA",
    252     ///             "authenticatorAttachment": "cross-platform",
    253     ///             "clientExtensionResults": {},
    254     ///             "clientDataJSON": "AA",
    255     ///             "type": "public-key"
    256     ///         }"#
    257     ///     ).is_ok());
    258     /// ```
    259     #[expect(
    260         clippy::too_many_lines,
    261         reason = "want to hide; thus don't want to put in an outer scope"
    262     )]
    263     #[inline]
    264     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    265     where
    266         D: Deserializer<'de>,
    267     {
    268         /// `Visitor` for `CustomRegistration`.
    269         struct CustomRegistrationVisitor;
    270         impl<'d> Visitor<'d> for CustomRegistrationVisitor {
    271             type Value = CustomRegistration;
    272             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    273                 formatter.write_str("CustomRegistration")
    274             }
    275             #[expect(
    276                 clippy::too_many_lines,
    277                 reason = "want to hide; thus don't want to put in an outer scope"
    278             )]
    279             fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
    280             where
    281                 A: MapAccess<'d>,
    282             {
    283                 /// Fields in the JSON.
    284                 enum Field {
    285                     /// `attestationObject` key.
    286                     AttestationObject,
    287                     /// `authenticatorAttachment` key.
    288                     AuthenticatorAttachment,
    289                     /// `clientDataJSON` key.
    290                     ClientDataJson,
    291                     /// `clientExtensionResults` key.
    292                     ClientExtensionResults,
    293                     /// `transports` key.
    294                     Transports,
    295                     /// `type` key.
    296                     Type,
    297                 }
    298                 impl<'e> Deserialize<'e> for Field {
    299                     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    300                     where
    301                         D: Deserializer<'e>,
    302                     {
    303                         /// `Visitor` for `Field`.
    304                         struct FieldVisitor;
    305                         impl Visitor<'_> for FieldVisitor {
    306                             type Value = Field;
    307                             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    308                                 write!(
    309                                     formatter,
    310                                     "'{ATTESTATION_OBJECT}', '{AUTHENTICATOR_ATTACHMENT}', '{CLIENT_DATA_JSON}', '{CLIENT_EXTENSION_RESULTS}', '{TRANSPORTS}', or '{TYPE}'"
    311                                 )
    312                             }
    313                             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    314                             where
    315                                 E: Error,
    316                             {
    317                                 match v {
    318                                     ATTESTATION_OBJECT => Ok(Field::AttestationObject),
    319                                     AUTHENTICATOR_ATTACHMENT => Ok(Field::AuthenticatorAttachment),
    320                                     CLIENT_DATA_JSON => Ok(Field::ClientDataJson),
    321                                     CLIENT_EXTENSION_RESULTS => Ok(Field::ClientExtensionResults),
    322                                     TRANSPORTS => Ok(Field::Transports),
    323                                     TYPE => Ok(Field::Type),
    324                                     _ => Err(E::unknown_field(v, FIELDS)),
    325                                 }
    326                             }
    327                         }
    328                         deserializer.deserialize_identifier(FieldVisitor)
    329                     }
    330                 }
    331                 let mut attestation_object = None;
    332                 let mut authenticator_attachment = None;
    333                 let mut client_data_json = None;
    334                 let mut ext = None;
    335                 let mut transports = None;
    336                 let mut typ = false;
    337                 while let Some(key) = map.next_key()? {
    338                     match key {
    339                         Field::AttestationObject => {
    340                             if attestation_object.is_some() {
    341                                 return Err(Error::duplicate_field(ATTESTATION_OBJECT));
    342                             }
    343                             attestation_object =
    344                                 map.next_value::<AttObj>().map(|val| Some(val.0))?;
    345                         }
    346                         Field::AuthenticatorAttachment => {
    347                             if authenticator_attachment.is_some() {
    348                                 return Err(Error::duplicate_field(AUTHENTICATOR_ATTACHMENT));
    349                             }
    350                             authenticator_attachment = map.next_value::<Option<_>>().map(Some)?;
    351                         }
    352                         Field::ClientDataJson => {
    353                             if client_data_json.is_some() {
    354                                 return Err(Error::duplicate_field(CLIENT_DATA_JSON));
    355                             }
    356                             client_data_json = map
    357                                 .next_value::<Base64DecodedVal>()
    358                                 .map(|val| Some(val.0))?;
    359                         }
    360                         Field::ClientExtensionResults => {
    361                             if ext.is_some() {
    362                                 return Err(Error::duplicate_field(CLIENT_EXTENSION_RESULTS));
    363                             }
    364                             ext = map.next_value().map(Some)?;
    365                         }
    366                         Field::Transports => {
    367                             if transports.is_some() {
    368                                 return Err(Error::duplicate_field(TRANSPORTS));
    369                             }
    370                             transports = map.next_value().map(Some)?;
    371                         }
    372                         Field::Type => {
    373                             if typ {
    374                                 return Err(Error::duplicate_field(TYPE));
    375                             }
    376                             typ = map.next_value::<Type>().map(|_| true)?;
    377                         }
    378                     }
    379                 }
    380                 attestation_object
    381                     .ok_or_else(|| Error::missing_field(ATTESTATION_OBJECT))
    382                     .and_then(|att_obj| {
    383                         client_data_json
    384                             .ok_or_else(|| Error::missing_field(CLIENT_DATA_JSON))
    385                             .and_then(|c_data| {
    386                                 ext.ok_or_else(|| Error::missing_field(CLIENT_EXTENSION_RESULTS))
    387                                     .and_then(|client_extension_results| {
    388                                         transports
    389                                             .ok_or_else(|| Error::missing_field(TRANSPORTS))
    390                                             .map(|trans| {
    391                                                 CustomRegistration(Registration {
    392                                                     response: AuthenticatorAttestation::new(
    393                                                         c_data, att_obj, trans,
    394                                                     ),
    395                                                     authenticator_attachment:
    396                                                         authenticator_attachment.map_or(
    397                                                             AuthenticatorAttachment::None,
    398                                                             |auth_attach| {
    399                                                                 auth_attach.unwrap_or(
    400                                                                     AuthenticatorAttachment::None,
    401                                                                 )
    402                                                             },
    403                                                         ),
    404                                                     client_extension_results,
    405                                                 })
    406                                             })
    407                                     })
    408                             })
    409                     })
    410             }
    411         }
    412         /// `attestationObject` key.
    413         const ATTESTATION_OBJECT: &str = "attestationObject";
    414         /// `authenticatorAttachment` key.
    415         const AUTHENTICATOR_ATTACHMENT: &str = "authenticatorAttachment";
    416         /// `clientDataJSON` key.
    417         const CLIENT_DATA_JSON: &str = "clientDataJSON";
    418         /// `clientExtensionResults` key.
    419         const CLIENT_EXTENSION_RESULTS: &str = "clientExtensionResults";
    420         /// `transports` key.
    421         const TRANSPORTS: &str = "transports";
    422         /// `type` key.
    423         const TYPE: &str = "type";
    424         /// Fields.
    425         const FIELDS: &[&str; 6] = &[
    426             ATTESTATION_OBJECT,
    427             AUTHENTICATOR_ATTACHMENT,
    428             CLIENT_DATA_JSON,
    429             CLIENT_EXTENSION_RESULTS,
    430             TRANSPORTS,
    431             TYPE,
    432         ];
    433         deserializer.deserialize_struct("CustomRegistration", FIELDS, CustomRegistrationVisitor)
    434     }
    435 }
    436 #[cfg(test)]
    437 mod tests {
    438     use super::{
    439         super::{
    440             super::super::request::register::CoseAlgorithmIdentifier, ALG, AuthenticatorAttachment,
    441             EC2, EDDSA, ES256, ES384, KTY, OKP, RSA, cbor,
    442         },
    443         CustomRegistration, RegistrationRelaxed,
    444     };
    445     use ed25519_dalek::{VerifyingKey, pkcs8::EncodePublicKey};
    446     use p256::{
    447         EncodedPoint as P256Pt, PublicKey as P256PubKey, SecretKey as P256Key,
    448         elliptic_curve::sec1::{FromEncodedPoint as _, ToEncodedPoint as _},
    449     };
    450     use p384::{EncodedPoint as P384Pt, PublicKey as P384PubKey, SecretKey as P384Key};
    451     use rsa::{
    452         BigUint, RsaPrivateKey,
    453         sha2::{Digest as _, Sha256},
    454         traits::PublicKeyParts,
    455     };
    456     use serde::de::{Error as _, Unexpected};
    457     use serde_json::Error;
    458     #[test]
    459     fn eddsa_registration_deserialize_data_mismatch() {
    460         let c_data_json = serde_json::json!({}).to_string();
    461         let att_obj = [
    462             cbor::MAP_3,
    463             cbor::TEXT_3,
    464             b'f',
    465             b'm',
    466             b't',
    467             cbor::TEXT_4,
    468             b'n',
    469             b'o',
    470             b'n',
    471             b'e',
    472             cbor::TEXT_7,
    473             b'a',
    474             b't',
    475             b't',
    476             b'S',
    477             b't',
    478             b'm',
    479             b't',
    480             cbor::MAP_0,
    481             cbor::TEXT_8,
    482             b'a',
    483             b'u',
    484             b't',
    485             b'h',
    486             b'D',
    487             b'a',
    488             b't',
    489             b'a',
    490             cbor::BYTES_INFO_24,
    491             113,
    492             // `rpIdHash`.
    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             0,
    516             0,
    517             0,
    518             0,
    519             0,
    520             0,
    521             0,
    522             0,
    523             0,
    524             0,
    525             // `flags`.
    526             0b0100_0101,
    527             // `signCount`.
    528             0,
    529             0,
    530             0,
    531             0,
    532             // `aaguid`.
    533             0,
    534             0,
    535             0,
    536             0,
    537             0,
    538             0,
    539             0,
    540             0,
    541             0,
    542             0,
    543             0,
    544             0,
    545             0,
    546             0,
    547             0,
    548             0,
    549             // `credentialIdLength`.
    550             0,
    551             16,
    552             // `credentialId`.
    553             0,
    554             0,
    555             0,
    556             0,
    557             0,
    558             0,
    559             0,
    560             0,
    561             0,
    562             0,
    563             0,
    564             0,
    565             0,
    566             0,
    567             0,
    568             0,
    569             // Ed25519 COSE key.
    570             cbor::MAP_4,
    571             KTY,
    572             OKP,
    573             ALG,
    574             EDDSA,
    575             // `crv`.
    576             cbor::NEG_ONE,
    577             // `Ed25519`.
    578             cbor::SIX,
    579             // `x`.
    580             cbor::NEG_TWO,
    581             cbor::BYTES_INFO_24,
    582             32,
    583             // Compressed y-coordinate.
    584             1,
    585             1,
    586             1,
    587             1,
    588             1,
    589             1,
    590             1,
    591             1,
    592             1,
    593             1,
    594             1,
    595             1,
    596             1,
    597             1,
    598             1,
    599             1,
    600             1,
    601             1,
    602             1,
    603             1,
    604             1,
    605             1,
    606             1,
    607             1,
    608             1,
    609             1,
    610             1,
    611             1,
    612             1,
    613             1,
    614             1,
    615             1,
    616         ];
    617         let pub_key = VerifyingKey::from_bytes(&[1; 32])
    618             .unwrap()
    619             .to_public_key_der()
    620             .unwrap();
    621         let b64_cdata = base64url_nopad::encode(c_data_json.as_bytes());
    622         let b64_adata = base64url_nopad::encode(&att_obj[att_obj.len() - 113..]);
    623         let b64_key = base64url_nopad::encode(pub_key.as_bytes());
    624         let b64_aobj = base64url_nopad::encode(att_obj.as_slice());
    625         // Base case is valid.
    626         assert!(
    627             serde_json::from_str::<RegistrationRelaxed>(
    628                 serde_json::json!({
    629                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    630                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    631                     "response": {
    632                         "clientDataJSON": b64_cdata,
    633                         "authenticatorData": b64_adata,
    634                         "transports": ["ble", "usb", "hybrid", "internal", "nfc", "smart-card"],
    635                         "publicKey": b64_key,
    636                         "publicKeyAlgorithm": -8,
    637                         "attestationObject": b64_aobj,
    638                     },
    639                     "authenticatorAttachment": "cross-platform",
    640                     "clientExtensionResults": {},
    641                     "type": "public-key"
    642                 })
    643                 .to_string()
    644                 .as_str()
    645             )
    646             .map_or(false, |reg| reg.0.response.client_data_json
    647                 == c_data_json.as_bytes()
    648                 && reg.0.response.attestation_object_and_c_data_hash[..att_obj.len()]
    649                     == att_obj
    650                 && reg.0.response.attestation_object_and_c_data_hash[att_obj.len()..]
    651                     == *Sha256::digest(c_data_json.as_bytes()).as_slice()
    652                 && reg.0.response.transports.count() == 6
    653                 && matches!(
    654                     reg.0.authenticator_attachment,
    655                     AuthenticatorAttachment::CrossPlatform
    656                 )
    657                 && reg.0.client_extension_results.cred_props.is_none()
    658                 && reg.0.client_extension_results.prf.is_none())
    659         );
    660         // `id` and `rawId` mismatch.
    661         let mut err = Error::invalid_value(
    662             Unexpected::Bytes(
    663                 base64url_nopad::decode("ABABABABABABABABABABAA".as_bytes())
    664                     .unwrap()
    665                     .as_slice(),
    666             ),
    667             &format!("id and rawId to match: CredentialId({:?})", [0; 16]).as_str(),
    668         )
    669         .to_string()
    670         .into_bytes();
    671         assert_eq!(
    672             serde_json::from_str::<RegistrationRelaxed>(
    673                 serde_json::json!({
    674                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    675                     "rawId": "ABABABABABABABABABABAA",
    676                     "response": {
    677                         "clientDataJSON": b64_cdata,
    678                         "authenticatorData": b64_adata,
    679                         "transports": [],
    680                         "publicKey": b64_key,
    681                         "publicKeyAlgorithm": -8,
    682                         "attestationObject": b64_aobj,
    683                     },
    684                     "clientExtensionResults": {},
    685                     "type": "public-key"
    686                 })
    687                 .to_string()
    688                 .as_str()
    689             )
    690             .unwrap_err()
    691             .to_string()
    692             .into_bytes()[..err.len()],
    693             err
    694         );
    695         // missing `id`.
    696         assert!(
    697             serde_json::from_str::<RegistrationRelaxed>(
    698                 serde_json::json!({
    699                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    700                     "response": {
    701                         "clientDataJSON": b64_cdata,
    702                         "authenticatorData": b64_adata,
    703                         "transports": [],
    704                         "publicKey": b64_key,
    705                         "publicKeyAlgorithm": -8,
    706                         "attestationObject": b64_aobj,
    707                     },
    708                     "clientExtensionResults": {},
    709                     "type": "public-key"
    710                 })
    711                 .to_string()
    712                 .as_str()
    713             )
    714             .is_ok()
    715         );
    716         // `null` `id`.
    717         err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId")
    718             .to_string()
    719             .into_bytes();
    720         assert_eq!(
    721             serde_json::from_str::<RegistrationRelaxed>(
    722                 serde_json::json!({
    723                     "id": null,
    724                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    725                     "response": {
    726                         "clientDataJSON": null,
    727                         "authenticatorData": b64_adata,
    728                         "transports": [],
    729                         "publicKey": b64_key,
    730                         "publicKeyAlgorithm": -8,
    731                         "attestationObject": b64_aobj,
    732                     },
    733                     "clientExtensionResults": {},
    734                     "type": "public-key"
    735                 })
    736                 .to_string()
    737                 .as_str()
    738             )
    739             .unwrap_err()
    740             .to_string()
    741             .into_bytes()[..err.len()],
    742             err
    743         );
    744         // Missing `rawId`.
    745         assert!(
    746             serde_json::from_str::<RegistrationRelaxed>(
    747                 serde_json::json!({
    748                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    749                     "response": {
    750                         "clientDataJSON": b64_cdata,
    751                         "authenticatorData": b64_adata,
    752                         "transports": [],
    753                         "publicKey": b64_key,
    754                         "publicKeyAlgorithm": -8,
    755                         "attestationObject": b64_aobj,
    756                     },
    757                     "clientExtensionResults": {},
    758                     "type": "public-key"
    759                 })
    760                 .to_string()
    761                 .as_str()
    762             )
    763             .is_ok()
    764         );
    765         // `null` `rawId`.
    766         err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId")
    767             .to_string()
    768             .into_bytes();
    769         assert_eq!(
    770             serde_json::from_str::<RegistrationRelaxed>(
    771                 serde_json::json!({
    772                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    773                     "rawId": null,
    774                     "response": {
    775                         "clientDataJSON": b64_cdata,
    776                         "authenticatorData": b64_adata,
    777                         "transports": [],
    778                         "publicKey": b64_key,
    779                         "publicKeyAlgorithm": -8,
    780                         "attestationObject": b64_aobj,
    781                     },
    782                     "clientExtensionResults": {},
    783                     "type": "public-key"
    784                 })
    785                 .to_string()
    786                 .as_str()
    787             )
    788             .unwrap_err()
    789             .to_string()
    790             .into_bytes()[..err.len()],
    791             err
    792         );
    793         // `id` and the credential id in authenticator data mismatch.
    794         err = Error::invalid_value(
    795             Unexpected::Bytes(
    796                 base64url_nopad
    797                     ::decode("ABABABABABABABABABABAA".as_bytes())
    798                     .unwrap()
    799                     .as_slice(),
    800             ),
    801             &format!("id, rawId, and the credential id in the attested credential data to all match: {:?}", [0; 16]).as_str(),
    802         )
    803         .to_string().into_bytes();
    804         assert_eq!(
    805             serde_json::from_str::<RegistrationRelaxed>(
    806                 serde_json::json!({
    807                     "id": "ABABABABABABABABABABAA",
    808                     "rawId": "ABABABABABABABABABABAA",
    809                     "response": {
    810                         "clientDataJSON": b64_cdata,
    811                         "authenticatorData": b64_adata,
    812                         "transports": [],
    813                         "publicKey": b64_key,
    814                         "publicKeyAlgorithm": -8,
    815                         "attestationObject": b64_aobj,
    816                     },
    817                     "clientExtensionResults": {},
    818                     "type": "public-key"
    819                 })
    820                 .to_string()
    821                 .as_str()
    822             )
    823             .unwrap_err()
    824             .to_string()
    825             .into_bytes()[..err.len()],
    826             err
    827         );
    828         // `authenticatorData` mismatches `authData` in attestation object.
    829         let mut bad_auth = [0; 113];
    830         bad_auth.copy_from_slice(&att_obj[att_obj.len() - 113..]);
    831         bad_auth[113 - 32..].copy_from_slice([0; 32].as_slice());
    832         err = Error::invalid_value(
    833             Unexpected::Bytes(bad_auth.as_slice()),
    834             &format!("authenticator data to match the authenticator data portion of attestation object: {:?}", &att_obj[att_obj.len() - bad_auth.len()..]).as_str(),
    835         )
    836         .to_string().into_bytes();
    837         assert_eq!(
    838             serde_json::from_str::<RegistrationRelaxed>(
    839                 serde_json::json!({
    840                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    841                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    842                     "response": {
    843                         "clientDataJSON": b64_cdata,
    844                         "authenticatorData": base64url_nopad::encode(bad_auth.as_slice()),
    845                         "transports": [],
    846                         "publicKey": b64_key,
    847                         "publicKeyAlgorithm": -8,
    848                         "attestationObject": b64_aobj,
    849                     },
    850                     "clientExtensionResults": {},
    851                     "type": "public-key"
    852                 })
    853                 .to_string()
    854                 .as_str()
    855             )
    856             .unwrap_err()
    857             .to_string()
    858             .into_bytes()[..err.len()],
    859             err
    860         );
    861         // Missing `authenticatorData`.
    862         assert!(
    863             serde_json::from_str::<RegistrationRelaxed>(
    864                 serde_json::json!({
    865                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    866                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    867                     "response": {
    868                         "clientDataJSON": b64_cdata,
    869                         "transports": [],
    870                         "publicKey": b64_key,
    871                         "publicKeyAlgorithm": -8,
    872                         "attestationObject": b64_aobj,
    873                     },
    874                     "clientExtensionResults": {},
    875                     "type": "public-key"
    876                 })
    877                 .to_string()
    878                 .as_str()
    879             )
    880             .is_ok()
    881         );
    882         // `null `authenticatorData`.
    883         assert!(
    884             serde_json::from_str::<RegistrationRelaxed>(
    885                 serde_json::json!({
    886                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    887                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    888                     "response": {
    889                         "clientDataJSON": b64_cdata,
    890                         "transports": [],
    891                         "authenticatorData": null,
    892                         "publicKey": b64_key,
    893                         "publicKeyAlgorithm": -8,
    894                         "attestationObject": b64_aobj,
    895                     },
    896                     "clientExtensionResults": {},
    897                     "type": "public-key"
    898                 })
    899                 .to_string()
    900                 .as_str()
    901             )
    902             .is_ok()
    903         );
    904         // `publicKeyAlgorithm` mismatch.
    905         err = Error::invalid_value(
    906             Unexpected::Other(&format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()),
    907             &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Eddsa).as_str()
    908         )
    909         .to_string().into_bytes();
    910         assert_eq!(
    911             serde_json::from_str::<RegistrationRelaxed>(
    912                 serde_json::json!({
    913                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    914                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    915                     "response": {
    916                         "clientDataJSON": b64_cdata,
    917                         "authenticatorData": b64_adata,
    918                         "transports": [],
    919                         "publicKey": b64_key,
    920                         "publicKeyAlgorithm": -7,
    921                         "attestationObject": b64_aobj,
    922                     },
    923                     "clientExtensionResults": {},
    924                     "type": "public-key"
    925                 })
    926                 .to_string()
    927                 .as_str()
    928             )
    929             .unwrap_err()
    930             .to_string()
    931             .into_bytes()[..err.len()],
    932             err
    933         );
    934         // Missing `publicKeyAlgorithm`.
    935         assert!(
    936             serde_json::from_str::<RegistrationRelaxed>(
    937                 serde_json::json!({
    938                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    939                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    940                     "response": {
    941                         "clientDataJSON": b64_cdata,
    942                         "authenticatorData": b64_adata,
    943                         "transports": [],
    944                         "publicKey": b64_key,
    945                         "attestationObject": b64_aobj,
    946                     },
    947                     "clientExtensionResults": {},
    948                     "type": "public-key"
    949                 })
    950                 .to_string()
    951                 .as_str()
    952             )
    953             .is_ok()
    954         );
    955         // `null` `publicKeyAlgorithm`.
    956         assert!(
    957             serde_json::from_str::<RegistrationRelaxed>(
    958                 serde_json::json!({
    959                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
    960                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    961                     "response": {
    962                         "clientDataJSON": b64_cdata,
    963                         "authenticatorData": b64_adata,
    964                         "transports": [],
    965                         "publicKey": b64_key,
    966                         "publicKeyAlgorithm": null,
    967                         "attestationObject": b64_aobj,
    968                     },
    969                     "clientExtensionResults": {},
    970                     "type": "public-key"
    971                 })
    972                 .to_string()
    973                 .as_str()
    974             )
    975             .is_ok()
    976         );
    977         // `publicKey` mismatch.
    978         err = Error::invalid_value(
    979             Unexpected::Bytes([0; 32].as_slice()),
    980             &format!(
    981                 "DER-encoded public key to match the public key within the attestation object: Ed25519(Ed25519PubKey({:?}))",
    982                 &att_obj[att_obj.len() - 32..],
    983             )
    984             .as_str(),
    985         )
    986         .to_string().into_bytes();
    987         assert_eq!(serde_json::from_str::<RegistrationRelaxed>(
    988             serde_json::json!({
    989                 "id": "AAAAAAAAAAAAAAAAAAAAAA",
    990                 "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
    991                 "response": {
    992                     "clientDataJSON": b64_cdata,
    993                     "authenticatorData": b64_adata,
    994                     "transports": [],
    995                     "publicKey": base64url_nopad::encode(VerifyingKey::from_bytes(&[0; 32]).unwrap().to_public_key_der().unwrap().as_bytes()),
    996                     "publicKeyAlgorithm": -8,
    997                     "attestationObject": b64_aobj,
    998                 },
    999                 "clientExtensionResults": {},
   1000                 "type": "public-key"
   1001             })
   1002             .to_string()
   1003             .as_str()
   1004             )
   1005             .unwrap_err().to_string().into_bytes()[..err.len()],
   1006             err
   1007         );
   1008         // Missing `publicKey`.
   1009         assert!(
   1010             serde_json::from_str::<RegistrationRelaxed>(
   1011                 serde_json::json!({
   1012                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1013                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1014                     "response": {
   1015                         "clientDataJSON": b64_cdata,
   1016                         "authenticatorData": b64_adata,
   1017                         "transports": [],
   1018                         "publicKeyAlgorithm": -8,
   1019                         "attestationObject": b64_aobj,
   1020                     },
   1021                     "clientExtensionResults": {},
   1022                     "type": "public-key"
   1023                 })
   1024                 .to_string()
   1025                 .as_str()
   1026             )
   1027             .is_ok()
   1028         );
   1029         // `null` `publicKey`.
   1030         assert!(
   1031             serde_json::from_str::<RegistrationRelaxed>(
   1032                 serde_json::json!({
   1033                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1034                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1035                     "response": {
   1036                         "clientDataJSON": b64_cdata,
   1037                         "authenticatorData": b64_adata,
   1038                         "transports": [],
   1039                         "publicKey": null,
   1040                         "publicKeyAlgorithm": -8,
   1041                         "attestationObject": b64_aobj,
   1042                     },
   1043                     "clientExtensionResults": {},
   1044                     "type": "public-key"
   1045                 })
   1046                 .to_string()
   1047                 .as_str()
   1048             )
   1049             .is_ok()
   1050         );
   1051         // Missing `transports`.
   1052         assert!(
   1053             serde_json::from_str::<RegistrationRelaxed>(
   1054                 serde_json::json!({
   1055                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1056                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1057                     "response": {
   1058                         "clientDataJSON": b64_cdata,
   1059                         "authenticatorData": b64_adata,
   1060                         "publicKey": b64_key,
   1061                         "publicKeyAlgorithm": -8,
   1062                         "attestationObject": b64_aobj,
   1063                     },
   1064                     "clientExtensionResults": {},
   1065                     "type": "public-key"
   1066                 })
   1067                 .to_string()
   1068                 .as_str()
   1069             )
   1070             .is_ok()
   1071         );
   1072         // Duplicate `transports` are allowed.
   1073         assert!(
   1074             serde_json::from_str::<RegistrationRelaxed>(
   1075                 serde_json::json!({
   1076                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1077                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1078                     "response": {
   1079                         "clientDataJSON": b64_cdata,
   1080                         "authenticatorData": b64_adata,
   1081                         "transports": ["usb", "usb"],
   1082                         "publicKey": b64_key,
   1083                         "publicKeyAlgorithm": -8,
   1084                         "attestationObject": b64_aobj,
   1085                     },
   1086                     "clientExtensionResults": {},
   1087                     "type": "public-key"
   1088                 })
   1089                 .to_string()
   1090                 .as_str()
   1091             )
   1092             .map_or(false, |reg| reg.0.response.transports.count() == 1)
   1093         );
   1094         // `null` `transports`.
   1095         assert!(
   1096             serde_json::from_str::<RegistrationRelaxed>(
   1097                 serde_json::json!({
   1098                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1099                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1100                     "response": {
   1101                         "clientDataJSON": b64_cdata,
   1102                         "authenticatorData": b64_adata,
   1103                         "transports": null,
   1104                         "publicKey": b64_key,
   1105                         "publicKeyAlgorithm": -8,
   1106                         "attestationObject": b64_aobj,
   1107                     },
   1108                     "clientExtensionResults": {},
   1109                     "type": "public-key"
   1110                 })
   1111                 .to_string()
   1112                 .as_str()
   1113             )
   1114             .is_ok()
   1115         );
   1116         // Unknown `transports`.
   1117         err = Error::invalid_value(
   1118             Unexpected::Str("Usb"),
   1119             &"'ble', 'cable', 'hybrid', 'internal', 'nfc', 'smart-card', or 'usb'",
   1120         )
   1121         .to_string()
   1122         .into_bytes();
   1123         assert_eq!(
   1124             serde_json::from_str::<RegistrationRelaxed>(
   1125                 serde_json::json!({
   1126                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1127                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1128                     "response": {
   1129                         "clientDataJSON": b64_cdata,
   1130                         "authenticatorData": b64_adata,
   1131                         "transports": ["Usb"],
   1132                         "publicKey": b64_key,
   1133                         "publicKeyAlgorithm": -8,
   1134                         "attestationObject": b64_aobj,
   1135                     },
   1136                     "clientExtensionResults": {},
   1137                     "type": "public-key"
   1138                 })
   1139                 .to_string()
   1140                 .as_str()
   1141             )
   1142             .unwrap_err()
   1143             .to_string()
   1144             .into_bytes()[..err.len()],
   1145             err
   1146         );
   1147         // `null` `authenticatorAttachment`.
   1148         assert!(
   1149             serde_json::from_str::<RegistrationRelaxed>(
   1150                 serde_json::json!({
   1151                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1152                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1153                     "response": {
   1154                         "clientDataJSON": b64_cdata,
   1155                         "authenticatorData": b64_adata,
   1156                         "transports": [],
   1157                         "publicKey": b64_key,
   1158                         "publicKeyAlgorithm": -8,
   1159                         "attestationObject": b64_aobj,
   1160                     },
   1161                     "authenticatorAttachment": null,
   1162                     "clientExtensionResults": {},
   1163                     "type": "public-key"
   1164                 })
   1165                 .to_string()
   1166                 .as_str()
   1167             )
   1168             .map_or(false, |reg| matches!(
   1169                 reg.0.authenticator_attachment,
   1170                 AuthenticatorAttachment::None
   1171             ))
   1172         );
   1173         // Unknown `authenticatorAttachment`.
   1174         err = Error::invalid_value(
   1175             Unexpected::Str("Platform"),
   1176             &"'platform' or 'cross-platform'",
   1177         )
   1178         .to_string()
   1179         .into_bytes();
   1180         assert_eq!(
   1181             serde_json::from_str::<RegistrationRelaxed>(
   1182                 serde_json::json!({
   1183                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1184                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1185                     "response": {
   1186                         "clientDataJSON": b64_cdata,
   1187                         "authenticatorData": b64_adata,
   1188                         "transports": [],
   1189                         "publicKey": b64_key,
   1190                         "publicKeyAlgorithm": -8,
   1191                         "attestationObject": b64_aobj,
   1192                     },
   1193                     "authenticatorAttachment": "Platform",
   1194                     "clientExtensionResults": {},
   1195                     "type": "public-key"
   1196                 })
   1197                 .to_string()
   1198                 .as_str()
   1199             )
   1200             .unwrap_err()
   1201             .to_string()
   1202             .into_bytes()[..err.len()],
   1203             err
   1204         );
   1205         // Missing `clientDataJSON`.
   1206         err = Error::missing_field("clientDataJSON")
   1207             .to_string()
   1208             .into_bytes();
   1209         assert_eq!(
   1210             serde_json::from_str::<RegistrationRelaxed>(
   1211                 serde_json::json!({
   1212                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1213                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1214                     "response": {
   1215                         "authenticatorData": b64_adata,
   1216                         "transports": [],
   1217                         "publicKey": b64_key,
   1218                         "publicKeyAlgorithm": -8,
   1219                         "attestationObject": b64_aobj,
   1220                     },
   1221                     "clientExtensionResults": {},
   1222                     "type": "public-key"
   1223                 })
   1224                 .to_string()
   1225                 .as_str()
   1226             )
   1227             .unwrap_err()
   1228             .to_string()
   1229             .into_bytes()[..err.len()],
   1230             err
   1231         );
   1232         // `null` `clientDataJSON`.
   1233         err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data")
   1234             .to_string()
   1235             .into_bytes();
   1236         assert_eq!(
   1237             serde_json::from_str::<RegistrationRelaxed>(
   1238                 serde_json::json!({
   1239                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1240                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1241                     "response": {
   1242                         "clientDataJSON": null,
   1243                         "authenticatorData": b64_adata,
   1244                         "transports": [],
   1245                         "publicKey": b64_key,
   1246                         "publicKeyAlgorithm": -8,
   1247                         "attestationObject": b64_aobj,
   1248                     },
   1249                     "clientExtensionResults": {},
   1250                     "type": "public-key"
   1251                 })
   1252                 .to_string()
   1253                 .as_str()
   1254             )
   1255             .unwrap_err()
   1256             .to_string()
   1257             .into_bytes()[..err.len()],
   1258             err
   1259         );
   1260         // Missing `attestationObject`.
   1261         err = Error::missing_field("attestationObject")
   1262             .to_string()
   1263             .into_bytes();
   1264         assert_eq!(
   1265             serde_json::from_str::<RegistrationRelaxed>(
   1266                 serde_json::json!({
   1267                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1268                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1269                     "response": {
   1270                         "clientDataJSON": b64_cdata,
   1271                         "authenticatorData": b64_adata,
   1272                         "transports": [],
   1273                         "publicKey": b64_key,
   1274                         "publicKeyAlgorithm": -8,
   1275                     },
   1276                     "clientExtensionResults": {},
   1277                     "type": "public-key"
   1278                 })
   1279                 .to_string()
   1280                 .as_str()
   1281             )
   1282             .unwrap_err()
   1283             .to_string()
   1284             .into_bytes()[..err.len()],
   1285             err
   1286         );
   1287         // `null` `attestationObject`.
   1288         err = Error::invalid_type(
   1289             Unexpected::Other("null"),
   1290             &"base64url-encoded attestation object",
   1291         )
   1292         .to_string()
   1293         .into_bytes();
   1294         assert_eq!(
   1295             serde_json::from_str::<RegistrationRelaxed>(
   1296                 serde_json::json!({
   1297                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1298                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1299                     "response": {
   1300                         "clientDataJSON": b64_cdata,
   1301                         "authenticatorData": b64_adata,
   1302                         "transports": [],
   1303                         "publicKey": b64_key,
   1304                         "publicKeyAlgorithm": -8,
   1305                         "attestationObject": null,
   1306                     },
   1307                     "clientExtensionResults": {},
   1308                     "type": "public-key"
   1309                 })
   1310                 .to_string()
   1311                 .as_str()
   1312             )
   1313             .unwrap_err()
   1314             .to_string()
   1315             .into_bytes()[..err.len()],
   1316             err
   1317         );
   1318         // Missing `response`.
   1319         err = Error::missing_field("response").to_string().into_bytes();
   1320         assert_eq!(
   1321             serde_json::from_str::<RegistrationRelaxed>(
   1322                 serde_json::json!({
   1323                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1324                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1325                     "clientExtensionResults": {},
   1326                     "type": "public-key"
   1327                 })
   1328                 .to_string()
   1329                 .as_str()
   1330             )
   1331             .unwrap_err()
   1332             .to_string()
   1333             .into_bytes()[..err.len()],
   1334             err
   1335         );
   1336         // `null` `response`.
   1337         err = Error::invalid_type(Unexpected::Other("null"), &"AuthenticatorAttestation")
   1338             .to_string()
   1339             .into_bytes();
   1340         assert_eq!(
   1341             serde_json::from_str::<RegistrationRelaxed>(
   1342                 serde_json::json!({
   1343                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1344                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1345                     "response": null,
   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         // Empty `response`.
   1358         err = Error::missing_field("clientDataJSON")
   1359             .to_string()
   1360             .into_bytes();
   1361         assert_eq!(
   1362             serde_json::from_str::<RegistrationRelaxed>(
   1363                 serde_json::json!({
   1364                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1365                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1366                     "response": {},
   1367                     "clientExtensionResults": {},
   1368                     "type": "public-key"
   1369                 })
   1370                 .to_string()
   1371                 .as_str()
   1372             )
   1373             .unwrap_err()
   1374             .to_string()
   1375             .into_bytes()[..err.len()],
   1376             err
   1377         );
   1378         // Missing `clientExtensionResults`.
   1379         assert!(
   1380             serde_json::from_str::<RegistrationRelaxed>(
   1381                 serde_json::json!({
   1382                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1383                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1384                     "response": {
   1385                         "clientDataJSON": b64_cdata,
   1386                         "authenticatorData": b64_adata,
   1387                         "transports": [],
   1388                         "publicKey": b64_key,
   1389                         "publicKeyAlgorithm": -8,
   1390                         "attestationObject": b64_aobj,
   1391                     },
   1392                     "type": "public-key"
   1393                 })
   1394                 .to_string()
   1395                 .as_str()
   1396             )
   1397             .is_ok()
   1398         );
   1399         // `null` `clientExtensionResults`.
   1400         assert!(
   1401             serde_json::from_str::<RegistrationRelaxed>(
   1402                 serde_json::json!({
   1403                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1404                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1405                     "response": {
   1406                         "clientDataJSON": b64_cdata,
   1407                         "authenticatorData": b64_adata,
   1408                         "transports": [],
   1409                         "publicKey": b64_key,
   1410                         "publicKeyAlgorithm": -8,
   1411                         "attestationObject": b64_aobj,
   1412                     },
   1413                     "clientExtensionResults": null,
   1414                     "type": "public-key"
   1415                 })
   1416                 .to_string()
   1417                 .as_str()
   1418             )
   1419             .is_ok()
   1420         );
   1421         // Missing `type`.
   1422         assert!(
   1423             serde_json::from_str::<RegistrationRelaxed>(
   1424                 serde_json::json!({
   1425                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1426                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1427                     "response": {
   1428                         "clientDataJSON": b64_cdata,
   1429                         "authenticatorData": b64_adata,
   1430                         "transports": [],
   1431                         "publicKey": b64_key,
   1432                         "publicKeyAlgorithm": -8,
   1433                         "attestationObject": b64_aobj,
   1434                     },
   1435                     "clientExtensionResults": {},
   1436                 })
   1437                 .to_string()
   1438                 .as_str()
   1439             )
   1440             .is_ok()
   1441         );
   1442         // `null` `type`.
   1443         err = Error::invalid_type(Unexpected::Other("null"), &"public-key")
   1444             .to_string()
   1445             .into_bytes();
   1446         assert_eq!(
   1447             serde_json::from_str::<RegistrationRelaxed>(
   1448                 serde_json::json!({
   1449                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1450                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1451                     "response": {
   1452                         "clientDataJSON": b64_cdata,
   1453                         "authenticatorData": b64_adata,
   1454                         "transports": [],
   1455                         "publicKey": b64_key,
   1456                         "publicKeyAlgorithm": -8,
   1457                         "attestationObject": b64_aobj,
   1458                     },
   1459                     "clientExtensionResults": {},
   1460                     "type": null
   1461                 })
   1462                 .to_string()
   1463                 .as_str()
   1464             )
   1465             .unwrap_err()
   1466             .to_string()
   1467             .into_bytes()[..err.len()],
   1468             err
   1469         );
   1470         // Not exactly `public-type` `type`.
   1471         err = Error::invalid_value(Unexpected::Str("Public-key"), &"public-key")
   1472             .to_string()
   1473             .into_bytes();
   1474         assert_eq!(
   1475             serde_json::from_str::<RegistrationRelaxed>(
   1476                 serde_json::json!({
   1477                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1478                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1479                     "response": {
   1480                         "clientDataJSON": b64_cdata,
   1481                         "authenticatorData": b64_adata,
   1482                         "transports": [],
   1483                         "publicKey": b64_key,
   1484                         "publicKeyAlgorithm": -8,
   1485                         "attestationObject": b64_aobj,
   1486                     },
   1487                     "clientExtensionResults": {},
   1488                     "type": "Public-key"
   1489                 })
   1490                 .to_string()
   1491                 .as_str()
   1492             )
   1493             .unwrap_err()
   1494             .to_string()
   1495             .into_bytes()[..err.len()],
   1496             err
   1497         );
   1498         // `null`.
   1499         err = Error::invalid_type(Unexpected::Other("null"), &"PublicKeyCredential")
   1500             .to_string()
   1501             .into_bytes();
   1502         assert_eq!(
   1503             serde_json::from_str::<RegistrationRelaxed>(
   1504                 serde_json::json!(null).to_string().as_str()
   1505             )
   1506             .unwrap_err()
   1507             .to_string()
   1508             .into_bytes()[..err.len()],
   1509             err
   1510         );
   1511         // Empty.
   1512         err = Error::missing_field("response").to_string().into_bytes();
   1513         assert_eq!(
   1514             serde_json::from_str::<RegistrationRelaxed>(serde_json::json!({}).to_string().as_str())
   1515                 .unwrap_err()
   1516                 .to_string()
   1517                 .into_bytes()[..err.len()],
   1518             err
   1519         );
   1520         // Unknown field in `response`.
   1521         assert!(
   1522             serde_json::from_str::<RegistrationRelaxed>(
   1523                 serde_json::json!({
   1524                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1525                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1526                     "response": {
   1527                         "clientDataJSON": b64_cdata,
   1528                         "authenticatorData": b64_adata,
   1529                         "transports": [],
   1530                         "publicKey": b64_key,
   1531                         "publicKeyAlgorithm": -8,
   1532                         "attestationObject": b64_aobj,
   1533                         "foo": true,
   1534                     },
   1535                     "clientExtensionResults": {},
   1536                     "type": "public-key"
   1537                 })
   1538                 .to_string()
   1539                 .as_str()
   1540             )
   1541             .is_ok()
   1542         );
   1543         // Duplicate field in `response`.
   1544         err = Error::duplicate_field("transports")
   1545             .to_string()
   1546             .into_bytes();
   1547         assert_eq!(
   1548             serde_json::from_str::<RegistrationRelaxed>(
   1549                 format!(
   1550                     "{{
   1551                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1552                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1553                        \"response\": {{
   1554                            \"clientDataJSON\": \"{b64_cdata}\",
   1555                            \"authenticatorData\": \"{b64_adata}\",
   1556                            \"transports\": [],
   1557                            \"publicKey\": \"{b64_key}\",
   1558                            \"publicKeyAlgorithm\": -8,
   1559                            \"attestationObject\": \"{b64_aobj}\",
   1560                            \"transports\": []
   1561                        }},
   1562                        \"clientExtensionResults\": {{}},
   1563                        \"type\": \"public-key\"
   1564 
   1565                      }}"
   1566                 )
   1567                 .as_str()
   1568             )
   1569             .unwrap_err()
   1570             .to_string()
   1571             .into_bytes()[..err.len()],
   1572             err
   1573         );
   1574         // Unknown field in `PublicKeyCredential`.
   1575         assert!(
   1576             serde_json::from_str::<RegistrationRelaxed>(
   1577                 serde_json::json!({
   1578                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1579                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1580                     "response": {
   1581                         "clientDataJSON": b64_cdata,
   1582                         "authenticatorData": b64_adata,
   1583                         "transports": [],
   1584                         "publicKey": b64_key,
   1585                         "publicKeyAlgorithm": -8,
   1586                         "attestationObject": b64_aobj
   1587                     },
   1588                     "clientExtensionResults": {},
   1589                     "type": "public-key",
   1590                     "foo": true,
   1591                 })
   1592                 .to_string()
   1593                 .as_str()
   1594             )
   1595             .is_ok()
   1596         );
   1597         // Duplicate field in `PublicKeyCredential`.
   1598         err = Error::duplicate_field("id").to_string().into_bytes();
   1599         assert_eq!(
   1600             serde_json::from_str::<RegistrationRelaxed>(
   1601                 format!(
   1602                     "{{
   1603                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1604                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1605                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   1606                        \"response\": {{
   1607                            \"clientDataJSON\": \"{b64_cdata}\",
   1608                            \"authenticatorData\": \"{b64_adata}\",
   1609                            \"transports\": [],
   1610                            \"publicKey\": \"{b64_key}\",
   1611                            \"publicKeyAlgorithm\": -8,
   1612                            \"attestationObject\": \"{b64_aobj}\"
   1613                        }},
   1614                        \"clientExtensionResults\": {{}},
   1615                        \"type\": \"public-key\"
   1616 
   1617                      }}"
   1618                 )
   1619                 .as_str()
   1620             )
   1621             .unwrap_err()
   1622             .to_string()
   1623             .into_bytes()[..err.len()],
   1624             err
   1625         );
   1626         // Base case is correct.
   1627         assert!(
   1628             serde_json::from_str::<CustomRegistration>(
   1629                 serde_json::json!({
   1630                     "attestationObject": b64_aobj,
   1631                     "authenticatorAttachment": "cross-platform",
   1632                     "clientDataJSON": b64_cdata,
   1633                     "clientExtensionResults": {},
   1634                     "transports": ["ble", "usb", "hybrid", "internal", "nfc", "smart-card"],
   1635                     "type": "public-key"
   1636                 })
   1637                 .to_string()
   1638                 .as_str()
   1639             )
   1640             .map_or(false, |reg| reg.0.response.client_data_json
   1641                 == c_data_json.as_bytes()
   1642                 && reg.0.response.attestation_object_and_c_data_hash[..att_obj.len()]
   1643                     == att_obj
   1644                 && reg.0.response.attestation_object_and_c_data_hash[att_obj.len()..]
   1645                     == *Sha256::digest(c_data_json.as_bytes()).as_slice()
   1646                 && reg.0.response.transports.count() == 6
   1647                 && matches!(
   1648                     reg.0.authenticator_attachment,
   1649                     AuthenticatorAttachment::CrossPlatform
   1650                 )
   1651                 && reg.0.client_extension_results.cred_props.is_none()
   1652                 && reg.0.client_extension_results.prf.is_none())
   1653         );
   1654         // Missing `transports`.
   1655         err = Error::missing_field("transports").to_string().into_bytes();
   1656         assert_eq!(
   1657             serde_json::from_str::<CustomRegistration>(
   1658                 serde_json::json!({
   1659                     "attestationObject": b64_aobj,
   1660                     "authenticatorAttachment": "cross-platform",
   1661                     "clientDataJSON": b64_cdata,
   1662                     "clientExtensionResults": {},
   1663                     "type": "public-key"
   1664                 })
   1665                 .to_string()
   1666                 .as_str()
   1667             )
   1668             .unwrap_err()
   1669             .to_string()
   1670             .into_bytes()[..err.len()],
   1671             err
   1672         );
   1673         // Duplicate `transports` are allowed.
   1674         assert!(
   1675             serde_json::from_str::<CustomRegistration>(
   1676                 serde_json::json!({
   1677                     "attestationObject": b64_aobj,
   1678                     "authenticatorAttachment": "cross-platform",
   1679                     "clientDataJSON": b64_cdata,
   1680                     "clientExtensionResults": {},
   1681                     "transports": ["usb", "usb"],
   1682                     "type": "public-key"
   1683                 })
   1684                 .to_string()
   1685                 .as_str()
   1686             )
   1687             .map_or(false, |reg| reg.0.response.transports.count() == 1)
   1688         );
   1689         // `null` `transports`.
   1690         err = Error::invalid_type(Unexpected::Other("null"), &"AuthTransports")
   1691             .to_string()
   1692             .into_bytes();
   1693         assert_eq!(
   1694             serde_json::from_str::<CustomRegistration>(
   1695                 serde_json::json!({
   1696                     "clientDataJSON": b64_cdata,
   1697                     "transports": null,
   1698                     "attestationObject": b64_aobj,
   1699                     "clientExtensionResults": {},
   1700                     "type": "public-key"
   1701                 })
   1702                 .to_string()
   1703                 .as_str()
   1704             )
   1705             .unwrap_err()
   1706             .to_string()
   1707             .into_bytes()[..err.len()],
   1708             err
   1709         );
   1710         // Unknown `transports`.
   1711         err = Error::invalid_value(
   1712             Unexpected::Str("Usb"),
   1713             &"'ble', 'cable', 'hybrid', 'internal', 'nfc', 'smart-card', or 'usb'",
   1714         )
   1715         .to_string()
   1716         .into_bytes();
   1717         assert_eq!(
   1718             serde_json::from_str::<CustomRegistration>(
   1719                 serde_json::json!({
   1720                     "attestationObject": b64_aobj,
   1721                     "authenticatorAttachment": "cross-platform",
   1722                     "clientDataJSON": b64_cdata,
   1723                     "clientExtensionResults": {},
   1724                     "transports": ["Usb"],
   1725                     "type": "public-key"
   1726                 })
   1727                 .to_string()
   1728                 .as_str()
   1729             )
   1730             .unwrap_err()
   1731             .to_string()
   1732             .into_bytes()[..err.len()],
   1733             err
   1734         );
   1735         // `null` `authenticatorAttachment`.
   1736         assert!(
   1737             serde_json::from_str::<CustomRegistration>(
   1738                 serde_json::json!({
   1739                     "attestationObject": b64_aobj,
   1740                     "authenticatorAttachment": null,
   1741                     "clientDataJSON": b64_cdata,
   1742                     "clientExtensionResults": {},
   1743                     "transports": [],
   1744                     "type": "public-key"
   1745                 })
   1746                 .to_string()
   1747                 .as_str()
   1748             )
   1749             .map_or(false, |reg| matches!(
   1750                 reg.0.authenticator_attachment,
   1751                 AuthenticatorAttachment::None
   1752             ))
   1753         );
   1754         // Unknown `authenticatorAttachment`.
   1755         err = Error::invalid_value(
   1756             Unexpected::Str("Platform"),
   1757             &"'platform' or 'cross-platform'",
   1758         )
   1759         .to_string()
   1760         .into_bytes();
   1761         assert_eq!(
   1762             serde_json::from_str::<CustomRegistration>(
   1763                 serde_json::json!({
   1764                     "attestationObject": b64_aobj,
   1765                     "authenticatorAttachment": "Platform",
   1766                     "clientDataJSON": b64_cdata,
   1767                     "clientExtensionResults": {},
   1768                     "transports": [],
   1769                     "type": "public-key"
   1770                 })
   1771                 .to_string()
   1772                 .as_str()
   1773             )
   1774             .unwrap_err()
   1775             .to_string()
   1776             .into_bytes()[..err.len()],
   1777             err
   1778         );
   1779         // Missing `clientDataJSON`.
   1780         err = Error::missing_field("clientDataJSON")
   1781             .to_string()
   1782             .into_bytes();
   1783         assert_eq!(
   1784             serde_json::from_str::<CustomRegistration>(
   1785                 serde_json::json!({
   1786                     "transports": [],
   1787                     "attestationObject": b64_aobj,
   1788                     "clientExtensionResults": {},
   1789                     "type": "public-key"
   1790                 })
   1791                 .to_string()
   1792                 .as_str()
   1793             )
   1794             .unwrap_err()
   1795             .to_string()
   1796             .into_bytes()[..err.len()],
   1797             err
   1798         );
   1799         // `null` `clientDataJSON`.
   1800         err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data")
   1801             .to_string()
   1802             .into_bytes();
   1803         assert_eq!(
   1804             serde_json::from_str::<CustomRegistration>(
   1805                 serde_json::json!({
   1806                     "clientDataJSON": null,
   1807                     "transports": [],
   1808                     "attestationObject": b64_aobj,
   1809                 })
   1810                 .to_string()
   1811                 .as_str()
   1812             )
   1813             .unwrap_err()
   1814             .to_string()
   1815             .into_bytes()[..err.len()],
   1816             err
   1817         );
   1818         // Missing `attestationObject`.
   1819         err = Error::missing_field("attestationObject")
   1820             .to_string()
   1821             .into_bytes();
   1822         assert_eq!(
   1823             serde_json::from_str::<CustomRegistration>(
   1824                 serde_json::json!({
   1825                     "clientDataJSON": b64_cdata,
   1826                     "transports": [],
   1827                     "clientExtensionResults": {},
   1828                     "type": "public-key"
   1829                 })
   1830                 .to_string()
   1831                 .as_str()
   1832             )
   1833             .unwrap_err()
   1834             .to_string()
   1835             .into_bytes()[..err.len()],
   1836             err
   1837         );
   1838         // `null` `attestationObject`.
   1839         err = Error::invalid_type(
   1840             Unexpected::Other("null"),
   1841             &"base64url-encoded attestation object",
   1842         )
   1843         .to_string()
   1844         .into_bytes();
   1845         assert_eq!(
   1846             serde_json::from_str::<CustomRegistration>(
   1847                 serde_json::json!({
   1848                     "clientDataJSON": b64_cdata,
   1849                     "transports": [],
   1850                     "attestationObject": null,
   1851                     "clientExtensionResults": {},
   1852                     "type": "public-key"
   1853                 })
   1854                 .to_string()
   1855                 .as_str()
   1856             )
   1857             .unwrap_err()
   1858             .to_string()
   1859             .into_bytes()[..err.len()],
   1860             err
   1861         );
   1862         // Missing `clientExtensionResults`.
   1863         err = Error::missing_field("clientExtensionResults")
   1864             .to_string()
   1865             .into_bytes();
   1866         assert_eq!(
   1867             serde_json::from_str::<CustomRegistration>(
   1868                 serde_json::json!({
   1869                     "clientDataJSON": b64_cdata,
   1870                     "transports": [],
   1871                     "attestationObject": b64_aobj,
   1872                     "type": "public-key"
   1873                 })
   1874                 .to_string()
   1875                 .as_str()
   1876             )
   1877             .unwrap_err()
   1878             .to_string()
   1879             .into_bytes()[..err.len()],
   1880             err
   1881         );
   1882         // `null` `clientExtensionResults`.
   1883         err = Error::invalid_type(Unexpected::Other("null"), &"ClientExtensionsOutputs")
   1884             .to_string()
   1885             .into_bytes();
   1886         assert_eq!(
   1887             serde_json::from_str::<CustomRegistration>(
   1888                 serde_json::json!({
   1889                     "clientDataJSON": b64_cdata,
   1890                     "transports": [],
   1891                     "attestationObject": b64_aobj,
   1892                     "clientExtensionResults": null,
   1893                     "type": "public-key"
   1894                 })
   1895                 .to_string()
   1896                 .as_str()
   1897             )
   1898             .unwrap_err()
   1899             .to_string()
   1900             .into_bytes()[..err.len()],
   1901             err
   1902         );
   1903         // Missing `type`.
   1904         assert!(
   1905             serde_json::from_str::<CustomRegistration>(
   1906                 serde_json::json!({
   1907                     "attestationObject": b64_aobj,
   1908                     "clientDataJSON": b64_cdata,
   1909                     "clientExtensionResults": {},
   1910                     "transports": []
   1911                 })
   1912                 .to_string()
   1913                 .as_str()
   1914             )
   1915             .map_or(false, |_| true)
   1916         );
   1917         // `null` `type`.
   1918         err = Error::invalid_type(Unexpected::Other("null"), &"public-key")
   1919             .to_string()
   1920             .into_bytes();
   1921         assert_eq!(
   1922             serde_json::from_str::<CustomRegistration>(
   1923                 serde_json::json!({
   1924                     "attestationObject": b64_aobj,
   1925                     "clientDataJSON": b64_cdata,
   1926                     "clientExtensionResults": {},
   1927                     "transports": [],
   1928                     "type": null
   1929                 })
   1930                 .to_string()
   1931                 .as_str()
   1932             )
   1933             .unwrap_err()
   1934             .to_string()
   1935             .into_bytes()[..err.len()],
   1936             err
   1937         );
   1938         // Not exactly `public-type` `type`.
   1939         err = Error::invalid_value(Unexpected::Str("Public-key"), &"public-key")
   1940             .to_string()
   1941             .into_bytes();
   1942         assert_eq!(
   1943             serde_json::from_str::<CustomRegistration>(
   1944                 serde_json::json!({
   1945                     "clientDataJSON": b64_cdata,
   1946                     "transports": [],
   1947                     "attestationObject": b64_aobj,
   1948                     "clientExtensionResults": {},
   1949                     "type": "Public-key"
   1950                 })
   1951                 .to_string()
   1952                 .as_str()
   1953             )
   1954             .unwrap_err()
   1955             .to_string()
   1956             .into_bytes()[..err.len()],
   1957             err
   1958         );
   1959         // `null`.
   1960         err = Error::invalid_type(Unexpected::Other("null"), &"CustomRegistration")
   1961             .to_string()
   1962             .into_bytes();
   1963         assert_eq!(
   1964             serde_json::from_str::<CustomRegistration>(
   1965                 serde_json::json!(null).to_string().as_str()
   1966             )
   1967             .unwrap_err()
   1968             .to_string()
   1969             .into_bytes()[..err.len()],
   1970             err
   1971         );
   1972         // Empty.
   1973         err = Error::missing_field("attestationObject")
   1974             .to_string()
   1975             .into_bytes();
   1976         assert_eq!(
   1977             serde_json::from_str::<CustomRegistration>(serde_json::json!({}).to_string().as_str())
   1978                 .unwrap_err()
   1979                 .to_string()
   1980                 .into_bytes()[..err.len()],
   1981             err
   1982         );
   1983         // Unknown field.
   1984         err = Error::unknown_field(
   1985             "foo",
   1986             [
   1987                 "attestationObject",
   1988                 "authenticatorAttachment",
   1989                 "clientDataJSON",
   1990                 "clientExtensionResults",
   1991                 "transports",
   1992                 "type",
   1993             ]
   1994             .as_slice(),
   1995         )
   1996         .to_string()
   1997         .into_bytes();
   1998         assert_eq!(
   1999             serde_json::from_str::<CustomRegistration>(
   2000                 serde_json::json!({
   2001                     "clientDataJSON": b64_cdata,
   2002                     "transports": [],
   2003                     "attestationObject": b64_aobj,
   2004                     "foo": true,
   2005                     "clientExtensionResults": {},
   2006                     "type": "public-key"
   2007                 })
   2008                 .to_string()
   2009                 .as_str()
   2010             )
   2011             .unwrap_err()
   2012             .to_string()
   2013             .into_bytes()[..err.len()],
   2014             err
   2015         );
   2016         // Duplicate field.
   2017         err = Error::duplicate_field("transports")
   2018             .to_string()
   2019             .into_bytes();
   2020         assert_eq!(
   2021             serde_json::from_str::<CustomRegistration>(
   2022                 format!(
   2023                     "{{
   2024                        \"clientDataJSON\": \"{b64_cdata}\",
   2025                        \"transports\": [],
   2026                        \"attestationObject\": \"{b64_aobj}\",
   2027                        \"transports\": []
   2028                        \"clientExtensionResults\": {{}},
   2029                        \"type\": \"public-key\"
   2030                      }}"
   2031                 )
   2032                 .as_str()
   2033             )
   2034             .unwrap_err()
   2035             .to_string()
   2036             .into_bytes()[..err.len()],
   2037             err
   2038         );
   2039     }
   2040     #[test]
   2041     fn client_extensions() {
   2042         let c_data_json = serde_json::json!({}).to_string();
   2043         let att_obj = [
   2044             cbor::MAP_3,
   2045             cbor::TEXT_3,
   2046             b'f',
   2047             b'm',
   2048             b't',
   2049             cbor::TEXT_4,
   2050             b'n',
   2051             b'o',
   2052             b'n',
   2053             b'e',
   2054             cbor::TEXT_7,
   2055             b'a',
   2056             b't',
   2057             b't',
   2058             b'S',
   2059             b't',
   2060             b'm',
   2061             b't',
   2062             cbor::MAP_0,
   2063             cbor::TEXT_8,
   2064             b'a',
   2065             b'u',
   2066             b't',
   2067             b'h',
   2068             b'D',
   2069             b'a',
   2070             b't',
   2071             b'a',
   2072             cbor::BYTES_INFO_24,
   2073             113,
   2074             // `rpIdHash`.
   2075             0,
   2076             0,
   2077             0,
   2078             0,
   2079             0,
   2080             0,
   2081             0,
   2082             0,
   2083             0,
   2084             0,
   2085             0,
   2086             0,
   2087             0,
   2088             0,
   2089             0,
   2090             0,
   2091             0,
   2092             0,
   2093             0,
   2094             0,
   2095             0,
   2096             0,
   2097             0,
   2098             0,
   2099             0,
   2100             0,
   2101             0,
   2102             0,
   2103             0,
   2104             0,
   2105             0,
   2106             0,
   2107             // `flags`.
   2108             0b0100_0101,
   2109             // `signCount`.
   2110             0,
   2111             0,
   2112             0,
   2113             0,
   2114             // `aaguid`.
   2115             0,
   2116             0,
   2117             0,
   2118             0,
   2119             0,
   2120             0,
   2121             0,
   2122             0,
   2123             0,
   2124             0,
   2125             0,
   2126             0,
   2127             0,
   2128             0,
   2129             0,
   2130             0,
   2131             // `credentialIdLength`.
   2132             0,
   2133             16,
   2134             // `credentialId`.
   2135             0,
   2136             0,
   2137             0,
   2138             0,
   2139             0,
   2140             0,
   2141             0,
   2142             0,
   2143             0,
   2144             0,
   2145             0,
   2146             0,
   2147             0,
   2148             0,
   2149             0,
   2150             0,
   2151             // Ed25519 COSE key.
   2152             cbor::MAP_4,
   2153             KTY,
   2154             OKP,
   2155             ALG,
   2156             EDDSA,
   2157             // `crv`.
   2158             cbor::NEG_ONE,
   2159             // `Ed25519`.
   2160             cbor::SIX,
   2161             // `x`.
   2162             cbor::NEG_TWO,
   2163             cbor::BYTES_INFO_24,
   2164             32,
   2165             // Compressed y-coordinate.
   2166             1,
   2167             1,
   2168             1,
   2169             1,
   2170             1,
   2171             1,
   2172             1,
   2173             1,
   2174             1,
   2175             1,
   2176             1,
   2177             1,
   2178             1,
   2179             1,
   2180             1,
   2181             1,
   2182             1,
   2183             1,
   2184             1,
   2185             1,
   2186             1,
   2187             1,
   2188             1,
   2189             1,
   2190             1,
   2191             1,
   2192             1,
   2193             1,
   2194             1,
   2195             1,
   2196             1,
   2197             1,
   2198         ];
   2199         let pub_key = VerifyingKey::from_bytes(&[1; 32])
   2200             .unwrap()
   2201             .to_public_key_der()
   2202             .unwrap();
   2203         let b64_cdata = base64url_nopad::encode(c_data_json.as_bytes());
   2204         let b64_adata = base64url_nopad::encode(&att_obj[att_obj.len() - 113..]);
   2205         let b64_key = base64url_nopad::encode(pub_key.as_bytes());
   2206         let b64_aobj = base64url_nopad::encode(att_obj.as_slice());
   2207         // Base case is valid.
   2208         assert!(
   2209             serde_json::from_str::<RegistrationRelaxed>(
   2210                 serde_json::json!({
   2211                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2212                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2213                     "response": {
   2214                         "clientDataJSON": b64_cdata,
   2215                         "authenticatorData": b64_adata,
   2216                         "transports": [],
   2217                         "publicKey": b64_key,
   2218                         "publicKeyAlgorithm": -8,
   2219                         "attestationObject": b64_aobj,
   2220                     },
   2221                     "clientExtensionResults": {},
   2222                     "type": "public-key"
   2223                 })
   2224                 .to_string()
   2225                 .as_str()
   2226             )
   2227             .map_or(false, |reg| reg.0.response.client_data_json
   2228                 == c_data_json.as_bytes()
   2229                 && reg.0.response.attestation_object_and_c_data_hash[..att_obj.len()]
   2230                     == att_obj
   2231                 && reg.0.response.attestation_object_and_c_data_hash[att_obj.len()..]
   2232                     == *Sha256::digest(c_data_json.as_bytes()).as_slice()
   2233                 && reg.0.response.transports.is_empty()
   2234                 && matches!(
   2235                     reg.0.authenticator_attachment,
   2236                     AuthenticatorAttachment::None
   2237                 )
   2238                 && reg.0.client_extension_results.cred_props.is_none()
   2239                 && reg.0.client_extension_results.prf.is_none())
   2240         );
   2241         // `null` `credProps`.
   2242         assert!(
   2243             serde_json::from_str::<RegistrationRelaxed>(
   2244                 serde_json::json!({
   2245                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2246                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2247                     "response": {
   2248                         "clientDataJSON": b64_cdata,
   2249                         "authenticatorData": b64_adata,
   2250                         "transports": [],
   2251                         "publicKey": b64_key,
   2252                         "publicKeyAlgorithm": -8,
   2253                         "attestationObject": b64_aobj,
   2254                     },
   2255                     "clientExtensionResults": {
   2256                         "credProps": null
   2257                     },
   2258                     "type": "public-key"
   2259                 })
   2260                 .to_string()
   2261                 .as_str()
   2262             )
   2263             .map_or(false, |reg| reg
   2264                 .0
   2265                 .client_extension_results
   2266                 .cred_props
   2267                 .is_none()
   2268                 && reg.0.client_extension_results.prf.is_none())
   2269         );
   2270         // `null` `prf`.
   2271         assert!(
   2272             serde_json::from_str::<RegistrationRelaxed>(
   2273                 serde_json::json!({
   2274                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2275                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2276                     "response": {
   2277                         "clientDataJSON": b64_cdata,
   2278                         "authenticatorData": b64_adata,
   2279                         "transports": [],
   2280                         "publicKey": b64_key,
   2281                         "publicKeyAlgorithm": -8,
   2282                         "attestationObject": b64_aobj,
   2283                     },
   2284                     "clientExtensionResults": {
   2285                         "prf": null
   2286                     },
   2287                     "type": "public-key"
   2288                 })
   2289                 .to_string()
   2290                 .as_str()
   2291             )
   2292             .map_or(false, |reg| reg
   2293                 .0
   2294                 .client_extension_results
   2295                 .cred_props
   2296                 .is_none()
   2297                 && reg.0.client_extension_results.prf.is_none())
   2298         );
   2299         // Unknown `clientExtensionResults`.
   2300         assert!(
   2301             serde_json::from_str::<RegistrationRelaxed>(
   2302                 serde_json::json!({
   2303                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2304                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2305                     "response": {
   2306                         "clientDataJSON": b64_cdata,
   2307                         "authenticatorData": b64_adata,
   2308                         "transports": [],
   2309                         "publicKey": b64_key,
   2310                         "publicKeyAlgorithm": -8,
   2311                         "attestationObject": b64_aobj,
   2312                     },
   2313                     "clientExtensionResults": {
   2314                         "CredProps": {
   2315                             "rk": true
   2316                         }
   2317                     },
   2318                     "type": "public-key"
   2319                 })
   2320                 .to_string()
   2321                 .as_str()
   2322             )
   2323             .is_ok()
   2324         );
   2325         // Duplicate field.
   2326         let mut err = Error::duplicate_field("credProps").to_string().into_bytes();
   2327         assert_eq!(
   2328             serde_json::from_str::<RegistrationRelaxed>(
   2329                 format!(
   2330                     "{{
   2331                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   2332                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   2333                        \"response\": {{
   2334                            \"clientDataJSON\": \"{b64_cdata}\",
   2335                            \"authenticatorData\": \"{b64_adata}\",
   2336                            \"transports\": [],
   2337                            \"publicKey\": \"{b64_key}\",
   2338                            \"publicKeyAlgorithm\": -8,
   2339                            \"attestationObject\": \"{b64_aobj}\"
   2340                        }},
   2341                        \"clientExtensionResults\": {{
   2342                            \"credProps\": null,
   2343                            \"credProps\": null
   2344                        }},
   2345                        \"type\": \"public-key\"
   2346                      }}"
   2347                 )
   2348                 .as_str()
   2349             )
   2350             .unwrap_err()
   2351             .to_string()
   2352             .into_bytes()[..err.len()],
   2353             err
   2354         );
   2355         // `null` `rk`.
   2356         assert!(
   2357             serde_json::from_str::<RegistrationRelaxed>(
   2358                 serde_json::json!({
   2359                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2360                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2361                     "response": {
   2362                         "clientDataJSON": b64_cdata,
   2363                         "authenticatorData": b64_adata,
   2364                         "transports": [],
   2365                         "publicKey": b64_key,
   2366                         "publicKeyAlgorithm": -8,
   2367                         "attestationObject": b64_aobj,
   2368                     },
   2369                     "clientExtensionResults": {
   2370                         "credProps": {
   2371                             "rk": null
   2372                         }
   2373                     },
   2374                     "type": "public-key"
   2375                 })
   2376                 .to_string()
   2377                 .as_str()
   2378             )
   2379             .map_or(false, |reg| reg
   2380                 .0
   2381                 .client_extension_results
   2382                 .cred_props
   2383                 .map_or(false, |props| props.rk.is_none())
   2384                 && reg.0.client_extension_results.prf.is_none())
   2385         );
   2386         // Missing `rk`.
   2387         assert!(
   2388             serde_json::from_str::<RegistrationRelaxed>(
   2389                 serde_json::json!({
   2390                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2391                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2392                     "response": {
   2393                         "clientDataJSON": b64_cdata,
   2394                         "authenticatorData": b64_adata,
   2395                         "transports": [],
   2396                         "publicKey": b64_key,
   2397                         "publicKeyAlgorithm": -8,
   2398                         "attestationObject": b64_aobj,
   2399                     },
   2400                     "clientExtensionResults": {
   2401                         "credProps": {}
   2402                     },
   2403                     "type": "public-key"
   2404                 })
   2405                 .to_string()
   2406                 .as_str()
   2407             )
   2408             .map_or(false, |reg| reg
   2409                 .0
   2410                 .client_extension_results
   2411                 .cred_props
   2412                 .map_or(false, |props| props.rk.is_none())
   2413                 && reg.0.client_extension_results.prf.is_none())
   2414         );
   2415         // `true` rk`.
   2416         assert!(
   2417             serde_json::from_str::<RegistrationRelaxed>(
   2418                 serde_json::json!({
   2419                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2420                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2421                     "response": {
   2422                         "clientDataJSON": b64_cdata,
   2423                         "authenticatorData": b64_adata,
   2424                         "transports": [],
   2425                         "publicKey": b64_key,
   2426                         "publicKeyAlgorithm": -8,
   2427                         "attestationObject": b64_aobj,
   2428                     },
   2429                     "clientExtensionResults": {
   2430                         "credProps": {
   2431                             "rk": true
   2432                         }
   2433                     },
   2434                     "type": "public-key"
   2435                 })
   2436                 .to_string()
   2437                 .as_str()
   2438             )
   2439             .map_or(false, |reg| reg
   2440                 .0
   2441                 .client_extension_results
   2442                 .cred_props
   2443                 .map_or(false, |props| props.rk.unwrap_or_default())
   2444                 && reg.0.client_extension_results.prf.is_none())
   2445         );
   2446         // `false` rk`.
   2447         assert!(
   2448             serde_json::from_str::<RegistrationRelaxed>(
   2449                 serde_json::json!({
   2450                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2451                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2452                     "response": {
   2453                         "clientDataJSON": b64_cdata,
   2454                         "authenticatorData": b64_adata,
   2455                         "transports": [],
   2456                         "publicKey": b64_key,
   2457                         "publicKeyAlgorithm": -8,
   2458                         "attestationObject": b64_aobj,
   2459                     },
   2460                     "clientExtensionResults": {
   2461                         "credProps": {
   2462                             "rk": false
   2463                         }
   2464                     },
   2465                     "type": "public-key"
   2466                 })
   2467                 .to_string()
   2468                 .as_str()
   2469             )
   2470             .map_or(false, |reg| reg
   2471                 .0
   2472                 .client_extension_results
   2473                 .cred_props
   2474                 .map_or(false, |props| props.rk.map_or(false, |rk| !rk))
   2475                 && reg.0.client_extension_results.prf.is_none())
   2476         );
   2477         // Invalid `rk`.
   2478         err = Error::invalid_type(Unexpected::Unsigned(3), &"a boolean")
   2479             .to_string()
   2480             .into_bytes();
   2481         assert_eq!(
   2482             serde_json::from_str::<RegistrationRelaxed>(
   2483                 serde_json::json!({
   2484                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2485                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2486                     "response": {
   2487                         "clientDataJSON": b64_cdata,
   2488                         "authenticatorData": b64_adata,
   2489                         "transports": [],
   2490                         "publicKey": b64_key,
   2491                         "publicKeyAlgorithm": -8,
   2492                         "attestationObject": b64_aobj,
   2493                     },
   2494                     "clientExtensionResults": {
   2495                         "credProps": {
   2496                             "rk": 3
   2497                         }
   2498                     },
   2499                     "type": "public-key"
   2500                 })
   2501                 .to_string()
   2502                 .as_str()
   2503             )
   2504             .unwrap_err()
   2505             .to_string()
   2506             .into_bytes()[..err.len()],
   2507             err
   2508         );
   2509         // Unknown `credProps` field.
   2510         assert!(
   2511             serde_json::from_str::<RegistrationRelaxed>(
   2512                 serde_json::json!({
   2513                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2514                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2515                     "response": {
   2516                         "clientDataJSON": b64_cdata,
   2517                         "authenticatorData": b64_adata,
   2518                         "transports": [],
   2519                         "publicKey": b64_key,
   2520                         "publicKeyAlgorithm": -8,
   2521                         "attestationObject": b64_aobj,
   2522                     },
   2523                     "clientExtensionResults": {
   2524                         "credProps": {
   2525                             "Rk": true,
   2526                         }
   2527                     },
   2528                     "type": "public-key"
   2529                 })
   2530                 .to_string()
   2531                 .as_str()
   2532             )
   2533             .is_ok()
   2534         );
   2535         // Duplicate field in `credProps`.
   2536         err = Error::duplicate_field("rk").to_string().into_bytes();
   2537         assert_eq!(
   2538             serde_json::from_str::<RegistrationRelaxed>(
   2539                 format!(
   2540                     "{{
   2541                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   2542                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   2543                        \"response\": {{
   2544                            \"clientDataJSON\": \"{b64_cdata}\",
   2545                            \"authenticatorData\": \"{b64_adata}\",
   2546                            \"transports\": [],
   2547                            \"publicKey\": \"{b64_key}\",
   2548                            \"publicKeyAlgorithm\": -8,
   2549                            \"attestationObject\": \"{b64_aobj}\"
   2550                        }},
   2551                        \"clientExtensionResults\": {{
   2552                            \"credProps\": {{
   2553                                \"rk\": true,
   2554                                \"rk\": true
   2555                            }}
   2556                        }},
   2557                        \"type\": \"public-key\"
   2558                      }}"
   2559                 )
   2560                 .as_str()
   2561             )
   2562             .unwrap_err()
   2563             .to_string()
   2564             .into_bytes()[..err.len()],
   2565             err
   2566         );
   2567         // `null` `enabled`.
   2568         err = Error::invalid_type(Unexpected::Other("null"), &"a boolean")
   2569             .to_string()
   2570             .into_bytes();
   2571         assert_eq!(
   2572             serde_json::from_str::<RegistrationRelaxed>(
   2573                 serde_json::json!({
   2574                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2575                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2576                     "response": {
   2577                         "clientDataJSON": b64_cdata,
   2578                         "authenticatorData": b64_adata,
   2579                         "transports": [],
   2580                         "publicKey": b64_key,
   2581                         "publicKeyAlgorithm": -8,
   2582                         "attestationObject": b64_aobj,
   2583                     },
   2584                     "clientExtensionResults": {
   2585                         "prf": {
   2586                             "enabled": null
   2587                         }
   2588                     },
   2589                     "type": "public-key"
   2590                 })
   2591                 .to_string()
   2592                 .as_str()
   2593             )
   2594             .unwrap_err()
   2595             .to_string()
   2596             .into_bytes()[..err.len()],
   2597             err
   2598         );
   2599         // Missing `enabled`.
   2600         err = Error::missing_field("enabled").to_string().into_bytes();
   2601         assert_eq!(
   2602             serde_json::from_str::<RegistrationRelaxed>(
   2603                 serde_json::json!({
   2604                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2605                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2606                     "response": {
   2607                         "clientDataJSON": b64_cdata,
   2608                         "authenticatorData": b64_adata,
   2609                         "transports": [],
   2610                         "publicKey": b64_key,
   2611                         "publicKeyAlgorithm": -8,
   2612                         "attestationObject": b64_aobj,
   2613                     },
   2614                     "clientExtensionResults": {
   2615                         "prf": {}
   2616                     },
   2617                     "type": "public-key"
   2618                 })
   2619                 .to_string()
   2620                 .as_str()
   2621             )
   2622             .unwrap_err()
   2623             .to_string()
   2624             .into_bytes()[..err.len()],
   2625             err
   2626         );
   2627         // `true` `enabled`.
   2628         assert!(
   2629             serde_json::from_str::<RegistrationRelaxed>(
   2630                 serde_json::json!({
   2631                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2632                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2633                     "response": {
   2634                         "clientDataJSON": b64_cdata,
   2635                         "authenticatorData": b64_adata,
   2636                         "transports": [],
   2637                         "publicKey": b64_key,
   2638                         "publicKeyAlgorithm": -8,
   2639                         "attestationObject": b64_aobj,
   2640                     },
   2641                     "clientExtensionResults": {
   2642                         "prf": {
   2643                             "enabled": true
   2644                         }
   2645                     },
   2646                     "type": "public-key"
   2647                 })
   2648                 .to_string()
   2649                 .as_str()
   2650             )
   2651             .map_or(false, |reg| reg
   2652                 .0
   2653                 .client_extension_results
   2654                 .cred_props
   2655                 .is_none()
   2656                 && reg
   2657                     .0
   2658                     .client_extension_results
   2659                     .prf
   2660                     .map_or(false, |prf| prf.enabled))
   2661         );
   2662         // `false` `enabled`.
   2663         assert!(
   2664             serde_json::from_str::<RegistrationRelaxed>(
   2665                 serde_json::json!({
   2666                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2667                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2668                     "response": {
   2669                         "clientDataJSON": b64_cdata,
   2670                         "authenticatorData": b64_adata,
   2671                         "transports": [],
   2672                         "publicKey": b64_key,
   2673                         "publicKeyAlgorithm": -8,
   2674                         "attestationObject": b64_aobj,
   2675                     },
   2676                     "clientExtensionResults": {
   2677                         "prf": {
   2678                             "enabled": false,
   2679                         }
   2680                     },
   2681                     "type": "public-key"
   2682                 })
   2683                 .to_string()
   2684                 .as_str()
   2685             )
   2686             .map_or(false, |reg| reg
   2687                 .0
   2688                 .client_extension_results
   2689                 .cred_props
   2690                 .is_none()
   2691                 && reg
   2692                     .0
   2693                     .client_extension_results
   2694                     .prf
   2695                     .map_or(false, |prf| !prf.enabled))
   2696         );
   2697         // Invalid `enabled`.
   2698         err = Error::invalid_type(Unexpected::Unsigned(3), &"a boolean")
   2699             .to_string()
   2700             .into_bytes();
   2701         assert_eq!(
   2702             serde_json::from_str::<RegistrationRelaxed>(
   2703                 serde_json::json!({
   2704                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2705                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2706                     "response": {
   2707                         "clientDataJSON": b64_cdata,
   2708                         "authenticatorData": b64_adata,
   2709                         "transports": [],
   2710                         "publicKey": b64_key,
   2711                         "publicKeyAlgorithm": -8,
   2712                         "attestationObject": b64_aobj,
   2713                     },
   2714                     "clientExtensionResults": {
   2715                         "prf": {
   2716                             "enabled": 3
   2717                         }
   2718                     },
   2719                     "type": "public-key"
   2720                 })
   2721                 .to_string()
   2722                 .as_str()
   2723             )
   2724             .unwrap_err()
   2725             .to_string()
   2726             .into_bytes()[..err.len()],
   2727             err
   2728         );
   2729         // `null` `results` with `enabled` `true`.
   2730         assert!(
   2731             serde_json::from_str::<RegistrationRelaxed>(
   2732                 serde_json::json!({
   2733                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2734                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2735                     "response": {
   2736                         "clientDataJSON": b64_cdata,
   2737                         "authenticatorData": b64_adata,
   2738                         "transports": [],
   2739                         "publicKey": b64_key,
   2740                         "publicKeyAlgorithm": -8,
   2741                         "attestationObject": b64_aobj,
   2742                     },
   2743                     "clientExtensionResults": {
   2744                         "prf": {
   2745                             "enabled": true,
   2746                             "results": null,
   2747                         }
   2748                     },
   2749                     "type": "public-key"
   2750                 })
   2751                 .to_string()
   2752                 .as_str()
   2753             )
   2754             .map_or(false, |reg| reg
   2755                 .0
   2756                 .client_extension_results
   2757                 .cred_props
   2758                 .is_none()
   2759                 && reg
   2760                     .0
   2761                     .client_extension_results
   2762                     .prf
   2763                     .map_or(false, |prf| prf.enabled))
   2764         );
   2765         // `null` `results` with `enabled` `false`.
   2766         err = Error::custom(
   2767             "prf must not have 'results', including a null 'results', if 'enabled' is false",
   2768         )
   2769         .to_string()
   2770         .into_bytes();
   2771         assert_eq!(
   2772             serde_json::from_str::<RegistrationRelaxed>(
   2773                 serde_json::json!({
   2774                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2775                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2776                     "response": {
   2777                         "clientDataJSON": b64_cdata,
   2778                         "authenticatorData": b64_adata,
   2779                         "transports": [],
   2780                         "publicKey": b64_key,
   2781                         "publicKeyAlgorithm": -8,
   2782                         "attestationObject": b64_aobj,
   2783                     },
   2784                     "clientExtensionResults": {
   2785                         "prf": {
   2786                             "enabled": false,
   2787                             "results": null
   2788                         }
   2789                     },
   2790                     "type": "public-key"
   2791                 })
   2792                 .to_string()
   2793                 .as_str()
   2794             )
   2795             .unwrap_err()
   2796             .to_string()
   2797             .into_bytes()[..err.len()],
   2798             err
   2799         );
   2800         // Duplicate field in `prf`.
   2801         err = Error::duplicate_field("enabled").to_string().into_bytes();
   2802         assert_eq!(
   2803             serde_json::from_str::<RegistrationRelaxed>(
   2804                 format!(
   2805                     "{{
   2806                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   2807                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   2808                        \"response\": {{
   2809                            \"clientDataJSON\": \"{b64_cdata}\",
   2810                            \"authenticatorData\": \"{b64_adata}\",
   2811                            \"transports\": [],
   2812                            \"publicKey\": \"{b64_key}\",
   2813                            \"publicKeyAlgorithm\": -8,
   2814                            \"attestationObject\": \"{b64_aobj}\"
   2815                        }},
   2816                        \"clientExtensionResults\": {{
   2817                            \"prf\": {{
   2818                                \"enabled\": true,
   2819                                \"enabled\": true
   2820                            }}
   2821                        }},
   2822                        \"type\": \"public-key\"
   2823                      }}"
   2824                 )
   2825                 .as_str()
   2826             )
   2827             .unwrap_err()
   2828             .to_string()
   2829             .into_bytes()[..err.len()],
   2830             err
   2831         );
   2832         // Missing `first`.
   2833         assert!(
   2834             serde_json::from_str::<RegistrationRelaxed>(
   2835                 serde_json::json!({
   2836                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2837                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2838                     "response": {
   2839                         "clientDataJSON": b64_cdata,
   2840                         "authenticatorData": b64_adata,
   2841                         "transports": [],
   2842                         "publicKey": b64_key,
   2843                         "publicKeyAlgorithm": -8,
   2844                         "attestationObject": b64_aobj,
   2845                     },
   2846                     "clientExtensionResults": {
   2847                         "prf": {
   2848                             "enabled": true,
   2849                             "results": {},
   2850                         }
   2851                     },
   2852                     "type": "public-key"
   2853                 })
   2854                 .to_string()
   2855                 .as_str()
   2856             )
   2857             .is_ok()
   2858         );
   2859         // `null` `first`.
   2860         assert!(
   2861             serde_json::from_str::<RegistrationRelaxed>(
   2862                 serde_json::json!({
   2863                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2864                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2865                     "response": {
   2866                         "clientDataJSON": b64_cdata,
   2867                         "authenticatorData": b64_adata,
   2868                         "transports": [],
   2869                         "publicKey": b64_key,
   2870                         "publicKeyAlgorithm": -8,
   2871                         "attestationObject": b64_aobj,
   2872                     },
   2873                     "clientExtensionResults": {
   2874                         "prf": {
   2875                             "enabled": true,
   2876                             "results": {
   2877                                 "first": null
   2878                             },
   2879                         }
   2880                     },
   2881                     "type": "public-key"
   2882                 })
   2883                 .to_string()
   2884                 .as_str()
   2885             )
   2886             .map_or(false, |reg| reg
   2887                 .0
   2888                 .client_extension_results
   2889                 .cred_props
   2890                 .is_none()
   2891                 && reg
   2892                     .0
   2893                     .client_extension_results
   2894                     .prf
   2895                     .map_or(false, |prf| prf.enabled))
   2896         );
   2897         // `null` `second`.
   2898         assert!(
   2899             serde_json::from_str::<RegistrationRelaxed>(
   2900                 serde_json::json!({
   2901                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2902                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2903                     "response": {
   2904                         "clientDataJSON": b64_cdata,
   2905                         "authenticatorData": b64_adata,
   2906                         "transports": [],
   2907                         "publicKey": b64_key,
   2908                         "publicKeyAlgorithm": -8,
   2909                         "attestationObject": b64_aobj,
   2910                     },
   2911                     "clientExtensionResults": {
   2912                         "prf": {
   2913                             "enabled": true,
   2914                             "results": {
   2915                                 "first": null,
   2916                                 "second": null
   2917                             },
   2918                         }
   2919                     },
   2920                     "type": "public-key"
   2921                 })
   2922                 .to_string()
   2923                 .as_str()
   2924             )
   2925             .map_or(false, |reg| reg
   2926                 .0
   2927                 .client_extension_results
   2928                 .cred_props
   2929                 .is_none()
   2930                 && reg
   2931                     .0
   2932                     .client_extension_results
   2933                     .prf
   2934                     .map_or(false, |prf| prf.enabled))
   2935         );
   2936         // Non-`null` `first`.
   2937         err = Error::invalid_type(Unexpected::Option, &"null")
   2938             .to_string()
   2939             .into_bytes();
   2940         assert_eq!(
   2941             serde_json::from_str::<RegistrationRelaxed>(
   2942                 serde_json::json!({
   2943                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2944                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2945                     "response": {
   2946                         "clientDataJSON": b64_cdata,
   2947                         "authenticatorData": b64_adata,
   2948                         "transports": [],
   2949                         "publicKey": b64_key,
   2950                         "publicKeyAlgorithm": -8,
   2951                         "attestationObject": b64_aobj,
   2952                     },
   2953                     "clientExtensionResults": {
   2954                         "prf": {
   2955                             "enabled": true,
   2956                             "results": {
   2957                                 "first": ""
   2958                             },
   2959                         }
   2960                     },
   2961                     "type": "public-key"
   2962                 })
   2963                 .to_string()
   2964                 .as_str()
   2965             )
   2966             .unwrap_err()
   2967             .to_string()
   2968             .into_bytes()[..err.len()],
   2969             err
   2970         );
   2971         // Non-`null` `second`.
   2972         err = Error::invalid_type(Unexpected::Option, &"null")
   2973             .to_string()
   2974             .into_bytes();
   2975         assert_eq!(
   2976             serde_json::from_str::<RegistrationRelaxed>(
   2977                 serde_json::json!({
   2978                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2979                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2980                     "response": {
   2981                         "clientDataJSON": b64_cdata,
   2982                         "authenticatorData": b64_adata,
   2983                         "transports": [],
   2984                         "publicKey": b64_key,
   2985                         "publicKeyAlgorithm": -8,
   2986                         "attestationObject": b64_aobj,
   2987                     },
   2988                     "clientExtensionResults": {
   2989                         "prf": {
   2990                             "enabled": true,
   2991                             "results": {
   2992                                 "first": null,
   2993                                 "second": ""
   2994                             },
   2995                         }
   2996                     },
   2997                     "type": "public-key"
   2998                 })
   2999                 .to_string()
   3000                 .as_str()
   3001             )
   3002             .unwrap_err()
   3003             .to_string()
   3004             .into_bytes()[..err.len()],
   3005             err
   3006         );
   3007         // Unknown `prf` field.
   3008         assert!(
   3009             serde_json::from_str::<RegistrationRelaxed>(
   3010                 serde_json::json!({
   3011                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3012                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3013                     "response": {
   3014                         "clientDataJSON": b64_cdata,
   3015                         "authenticatorData": b64_adata,
   3016                         "transports": [],
   3017                         "publicKey": b64_key,
   3018                         "publicKeyAlgorithm": -8,
   3019                         "attestationObject": b64_aobj,
   3020                     },
   3021                     "clientExtensionResults": {
   3022                         "prf": {
   3023                             "enabled": true,
   3024                             "Results": null
   3025                         }
   3026                     },
   3027                     "type": "public-key"
   3028                 })
   3029                 .to_string()
   3030                 .as_str()
   3031             )
   3032             .is_ok()
   3033         );
   3034         // Unknown `results` field.
   3035         assert!(
   3036             serde_json::from_str::<RegistrationRelaxed>(
   3037                 serde_json::json!({
   3038                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3039                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3040                     "response": {
   3041                         "clientDataJSON": b64_cdata,
   3042                         "authenticatorData": b64_adata,
   3043                         "transports": [],
   3044                         "publicKey": b64_key,
   3045                         "publicKeyAlgorithm": -8,
   3046                         "attestationObject": b64_aobj,
   3047                     },
   3048                     "clientExtensionResults": {
   3049                         "prf": {
   3050                             "enabled": true,
   3051                             "results": {
   3052                                 "first": null,
   3053                                 "Second": null
   3054                             }
   3055                         }
   3056                     },
   3057                     "type": "public-key"
   3058                 })
   3059                 .to_string()
   3060                 .as_str()
   3061             )
   3062             .is_ok()
   3063         );
   3064         // Duplicate field in `results`.
   3065         err = Error::duplicate_field("first").to_string().into_bytes();
   3066         assert_eq!(
   3067             serde_json::from_str::<RegistrationRelaxed>(
   3068                 format!(
   3069                     "{{
   3070                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   3071                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   3072                        \"response\": {{
   3073                            \"clientDataJSON\": \"{b64_cdata}\",
   3074                            \"authenticatorData\": \"{b64_adata}\",
   3075                            \"transports\": [],
   3076                            \"publicKey\": \"{b64_key}\",
   3077                            \"publicKeyAlgorithm\": -8,
   3078                            \"attestationObject\": \"{b64_aobj}\"
   3079                        }},
   3080                        \"clientExtensionResults\": {{
   3081                            \"prf\": {{
   3082                                \"enabled\": true,
   3083                                \"results\": {{
   3084                                    \"first\": null,
   3085                                    \"first\": null
   3086                                }}
   3087                            }}
   3088                        }},
   3089                        \"type\": \"public-key\"
   3090                      }}"
   3091                 )
   3092                 .as_str()
   3093             )
   3094             .unwrap_err()
   3095             .to_string()
   3096             .into_bytes()[..err.len()],
   3097             err
   3098         );
   3099     }
   3100     #[test]
   3101     fn es256_registration_deserialize_data_mismatch() {
   3102         let c_data_json = serde_json::json!({}).to_string();
   3103         let mut att_obj = [
   3104             cbor::MAP_3,
   3105             cbor::TEXT_3,
   3106             b'f',
   3107             b'm',
   3108             b't',
   3109             cbor::TEXT_4,
   3110             b'n',
   3111             b'o',
   3112             b'n',
   3113             b'e',
   3114             cbor::TEXT_7,
   3115             b'a',
   3116             b't',
   3117             b't',
   3118             b'S',
   3119             b't',
   3120             b'm',
   3121             b't',
   3122             cbor::MAP_0,
   3123             cbor::TEXT_8,
   3124             b'a',
   3125             b'u',
   3126             b't',
   3127             b'h',
   3128             b'D',
   3129             b'a',
   3130             b't',
   3131             b'a',
   3132             cbor::BYTES_INFO_24,
   3133             148,
   3134             // `rpIdHash`.
   3135             0,
   3136             0,
   3137             0,
   3138             0,
   3139             0,
   3140             0,
   3141             0,
   3142             0,
   3143             0,
   3144             0,
   3145             0,
   3146             0,
   3147             0,
   3148             0,
   3149             0,
   3150             0,
   3151             0,
   3152             0,
   3153             0,
   3154             0,
   3155             0,
   3156             0,
   3157             0,
   3158             0,
   3159             0,
   3160             0,
   3161             0,
   3162             0,
   3163             0,
   3164             0,
   3165             0,
   3166             0,
   3167             // `flags`.
   3168             0b0100_0101,
   3169             // `signCount`.
   3170             0,
   3171             0,
   3172             0,
   3173             0,
   3174             // `aaguid`.
   3175             0,
   3176             0,
   3177             0,
   3178             0,
   3179             0,
   3180             0,
   3181             0,
   3182             0,
   3183             0,
   3184             0,
   3185             0,
   3186             0,
   3187             0,
   3188             0,
   3189             0,
   3190             0,
   3191             // `credentialIdLength`.
   3192             0,
   3193             16,
   3194             // `credentialId`.
   3195             0,
   3196             0,
   3197             0,
   3198             0,
   3199             0,
   3200             0,
   3201             0,
   3202             0,
   3203             0,
   3204             0,
   3205             0,
   3206             0,
   3207             0,
   3208             0,
   3209             0,
   3210             0,
   3211             // P-256 COSE key.
   3212             cbor::MAP_5,
   3213             KTY,
   3214             EC2,
   3215             ALG,
   3216             ES256,
   3217             // `crv`.
   3218             cbor::NEG_ONE,
   3219             // `P-256`.
   3220             cbor::ONE,
   3221             // `x`.
   3222             cbor::NEG_TWO,
   3223             cbor::BYTES_INFO_24,
   3224             32,
   3225             // x-coordinate. This will be overwritten later.
   3226             0,
   3227             0,
   3228             0,
   3229             0,
   3230             0,
   3231             0,
   3232             0,
   3233             0,
   3234             0,
   3235             0,
   3236             0,
   3237             0,
   3238             0,
   3239             0,
   3240             0,
   3241             0,
   3242             0,
   3243             0,
   3244             0,
   3245             0,
   3246             0,
   3247             0,
   3248             0,
   3249             0,
   3250             0,
   3251             0,
   3252             0,
   3253             0,
   3254             0,
   3255             0,
   3256             0,
   3257             0,
   3258             // `y`.
   3259             cbor::NEG_THREE,
   3260             cbor::BYTES_INFO_24,
   3261             32,
   3262             // y-coordinate. This will be overwritten later.
   3263             0,
   3264             0,
   3265             0,
   3266             0,
   3267             0,
   3268             0,
   3269             0,
   3270             0,
   3271             0,
   3272             0,
   3273             0,
   3274             0,
   3275             0,
   3276             0,
   3277             0,
   3278             0,
   3279             0,
   3280             0,
   3281             0,
   3282             0,
   3283             0,
   3284             0,
   3285             0,
   3286             0,
   3287             0,
   3288             0,
   3289             0,
   3290             0,
   3291             0,
   3292             0,
   3293             0,
   3294             0,
   3295         ];
   3296         let key = P256Key::from_bytes(
   3297             &[
   3298                 137, 133, 36, 206, 163, 47, 255, 5, 76, 144, 163, 141, 40, 109, 108, 240, 246, 115,
   3299                 178, 237, 169, 68, 6, 129, 92, 21, 238, 127, 55, 158, 207, 95,
   3300             ]
   3301             .into(),
   3302         )
   3303         .unwrap()
   3304         .public_key();
   3305         let enc_key = key.to_encoded_point(false);
   3306         let pub_key = key.to_public_key_der().unwrap();
   3307         let att_obj_len = att_obj.len();
   3308         att_obj[att_obj_len - 67..att_obj_len - 35]
   3309             .copy_from_slice(enc_key.x().unwrap().as_slice());
   3310         att_obj[att_obj_len - 32..].copy_from_slice(enc_key.y().unwrap().as_slice());
   3311         let b64_cdata = base64url_nopad::encode(c_data_json.as_bytes());
   3312         let b64_adata = base64url_nopad::encode(&att_obj[att_obj.len() - 148..]);
   3313         let b64_key = base64url_nopad::encode(pub_key.as_bytes());
   3314         let b64_aobj = base64url_nopad::encode(att_obj.as_slice());
   3315         // Base case is valid.
   3316         assert!(
   3317             serde_json::from_str::<RegistrationRelaxed>(
   3318                 serde_json::json!({
   3319                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3320                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3321                     "response": {
   3322                         "clientDataJSON": b64_cdata,
   3323                         "authenticatorData": b64_adata,
   3324                         "transports": [],
   3325                         "publicKey": b64_key,
   3326                         "publicKeyAlgorithm": -7,
   3327                         "attestationObject": b64_aobj,
   3328                     },
   3329                     "clientExtensionResults": {},
   3330                     "type": "public-key"
   3331                 })
   3332                 .to_string()
   3333                 .as_str()
   3334             )
   3335             .map_or(false, |reg| reg.0.response.client_data_json
   3336                 == c_data_json.as_bytes()
   3337                 && reg.0.response.attestation_object_and_c_data_hash[..att_obj.len()]
   3338                     == att_obj
   3339                 && reg.0.response.attestation_object_and_c_data_hash[att_obj.len()..]
   3340                     == *Sha256::digest(c_data_json.as_bytes()).as_slice()
   3341                 && reg.0.response.transports.is_empty()
   3342                 && matches!(
   3343                     reg.0.authenticator_attachment,
   3344                     AuthenticatorAttachment::None
   3345                 )
   3346                 && reg.0.client_extension_results.cred_props.is_none()
   3347                 && reg.0.client_extension_results.prf.is_none())
   3348         );
   3349         // `publicKeyAlgorithm` mismatch.
   3350         let mut err = Error::invalid_value(
   3351             Unexpected::Other(&format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()),
   3352             &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es256).as_str()
   3353         )
   3354         .to_string().into_bytes();
   3355         assert_eq!(
   3356             serde_json::from_str::<RegistrationRelaxed>(
   3357                 serde_json::json!({
   3358                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3359                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3360                     "response": {
   3361                         "clientDataJSON": b64_cdata,
   3362                         "authenticatorData": b64_adata,
   3363                         "transports": [],
   3364                         "publicKey": b64_key,
   3365                         "publicKeyAlgorithm": -8,
   3366                         "attestationObject": b64_aobj,
   3367                     },
   3368                     "clientExtensionResults": {},
   3369                     "type": "public-key"
   3370                 })
   3371                 .to_string()
   3372                 .as_str()
   3373             )
   3374             .unwrap_err()
   3375             .to_string()
   3376             .into_bytes()[..err.len()],
   3377             err
   3378         );
   3379         // Missing `publicKeyAlgorithm`.
   3380         assert!(
   3381             serde_json::from_str::<RegistrationRelaxed>(
   3382                 serde_json::json!({
   3383                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3384                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3385                     "response": {
   3386                         "clientDataJSON": b64_cdata,
   3387                         "authenticatorData": b64_adata,
   3388                         "transports": [],
   3389                         "publicKey": b64_key,
   3390                         "attestationObject": b64_aobj,
   3391                     },
   3392                     "clientExtensionResults": {},
   3393                     "type": "public-key"
   3394                 })
   3395                 .to_string()
   3396                 .as_str()
   3397             )
   3398             .is_ok()
   3399         );
   3400         // `null` `publicKeyAlgorithm`.
   3401         assert!(
   3402             serde_json::from_str::<RegistrationRelaxed>(
   3403                 serde_json::json!({
   3404                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3405                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3406                     "response": {
   3407                         "clientDataJSON": b64_cdata,
   3408                         "authenticatorData": b64_adata,
   3409                         "transports": [],
   3410                         "publicKey": b64_key,
   3411                         "publicKeyAlgorithm": null,
   3412                         "attestationObject": b64_aobj,
   3413                     },
   3414                     "clientExtensionResults": {},
   3415                     "type": "public-key"
   3416                 })
   3417                 .to_string()
   3418                 .as_str()
   3419             )
   3420             .is_ok()
   3421         );
   3422         // `publicKey` mismatch.
   3423         let bad_pub_key = P256PubKey::from_encoded_point(&P256Pt::from_affine_coordinates(
   3424             &[
   3425                 66, 71, 188, 41, 125, 2, 226, 44, 148, 62, 63, 190, 172, 64, 33, 214, 6, 37, 148,
   3426                 23, 240, 235, 203, 84, 112, 219, 232, 197, 54, 182, 17, 235,
   3427             ]
   3428             .into(),
   3429             &[
   3430                 22, 172, 123, 13, 170, 242, 217, 248, 193, 209, 206, 163, 92, 4, 162, 168, 113, 63,
   3431                 2, 117, 16, 223, 239, 196, 109, 179, 10, 130, 43, 213, 205, 92,
   3432             ]
   3433             .into(),
   3434             false,
   3435         ))
   3436         .unwrap();
   3437         err = Error::invalid_value(
   3438             Unexpected::Bytes([0; 32].as_slice()),
   3439             &format!(
   3440                 "DER-encoded public key to match the public key within the attestation object: P256(UncompressedP256PubKey({:?}, {:?}))",
   3441                 &att_obj[att_obj.len() - 67..att_obj.len() - 35],
   3442                 &att_obj[att_obj.len() - 32..],
   3443             )
   3444             .as_str(),
   3445         )
   3446         .to_string().into_bytes();
   3447         assert_eq!(serde_json::from_str::<RegistrationRelaxed>(
   3448             serde_json::json!({
   3449                 "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3450                 "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3451                 "response": {
   3452                     "clientDataJSON": b64_cdata,
   3453                     "authenticatorData": b64_adata,
   3454                     "transports": [],
   3455                     "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()),
   3456                     "publicKeyAlgorithm": -7,
   3457                     "attestationObject": b64_aobj,
   3458                 },
   3459                 "clientExtensionResults": {},
   3460                 "type": "public-key"
   3461             })
   3462             .to_string()
   3463             .as_str()
   3464             )
   3465             .unwrap_err().to_string().into_bytes()[..err.len()],
   3466             err
   3467         );
   3468         // Missing `publicKey`.
   3469         assert!(
   3470             serde_json::from_str::<RegistrationRelaxed>(
   3471                 serde_json::json!({
   3472                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3473                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3474                     "response": {
   3475                         "clientDataJSON": b64_cdata,
   3476                         "authenticatorData": b64_adata,
   3477                         "transports": [],
   3478                         "publicKeyAlgorithm": -7,
   3479                         "attestationObject": b64_aobj,
   3480                     },
   3481                     "clientExtensionResults": {},
   3482                     "type": "public-key"
   3483                 })
   3484                 .to_string()
   3485                 .as_str()
   3486             )
   3487             .is_ok()
   3488         );
   3489         // `null` `publicKey`.
   3490         assert!(
   3491             serde_json::from_str::<RegistrationRelaxed>(
   3492                 serde_json::json!({
   3493                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3494                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3495                     "response": {
   3496                         "clientDataJSON": b64_cdata,
   3497                         "authenticatorData": b64_adata,
   3498                         "transports": [],
   3499                         "publicKey": null,
   3500                         "publicKeyAlgorithm": -7,
   3501                         "attestationObject": b64_aobj,
   3502                     },
   3503                     "clientExtensionResults": {},
   3504                     "type": "public-key"
   3505                 })
   3506                 .to_string()
   3507                 .as_str()
   3508             )
   3509             .is_ok()
   3510         );
   3511         // Base case is valid.
   3512         assert!(
   3513             serde_json::from_str::<CustomRegistration>(
   3514                 serde_json::json!({
   3515                     "clientDataJSON": b64_cdata,
   3516                     "transports": [],
   3517                     "attestationObject": b64_aobj,
   3518                     "clientExtensionResults": {},
   3519                     "type": "public-key"
   3520                 })
   3521                 .to_string()
   3522                 .as_str()
   3523             )
   3524             .map_or(false, |reg| reg.0.response.client_data_json
   3525                 == c_data_json.as_bytes()
   3526                 && reg.0.response.attestation_object_and_c_data_hash[..att_obj.len()]
   3527                     == att_obj
   3528                 && reg.0.response.attestation_object_and_c_data_hash[att_obj.len()..]
   3529                     == *Sha256::digest(c_data_json.as_bytes()).as_slice()
   3530                 && reg.0.response.transports.is_empty()
   3531                 && matches!(
   3532                     reg.0.authenticator_attachment,
   3533                     AuthenticatorAttachment::None
   3534                 )
   3535                 && reg.0.client_extension_results.cred_props.is_none()
   3536                 && reg.0.client_extension_results.prf.is_none())
   3537         );
   3538     }
   3539     #[test]
   3540     fn es384_registration_deserialize_data_mismatch() {
   3541         let c_data_json = serde_json::json!({}).to_string();
   3542         let mut att_obj = [
   3543             cbor::MAP_3,
   3544             cbor::TEXT_3,
   3545             b'f',
   3546             b'm',
   3547             b't',
   3548             cbor::TEXT_4,
   3549             b'n',
   3550             b'o',
   3551             b'n',
   3552             b'e',
   3553             cbor::TEXT_7,
   3554             b'a',
   3555             b't',
   3556             b't',
   3557             b'S',
   3558             b't',
   3559             b'm',
   3560             b't',
   3561             cbor::MAP_0,
   3562             cbor::TEXT_8,
   3563             b'a',
   3564             b'u',
   3565             b't',
   3566             b'h',
   3567             b'D',
   3568             b'a',
   3569             b't',
   3570             b'a',
   3571             cbor::BYTES_INFO_24,
   3572             181,
   3573             // `rpIdHash`.
   3574             0,
   3575             0,
   3576             0,
   3577             0,
   3578             0,
   3579             0,
   3580             0,
   3581             0,
   3582             0,
   3583             0,
   3584             0,
   3585             0,
   3586             0,
   3587             0,
   3588             0,
   3589             0,
   3590             0,
   3591             0,
   3592             0,
   3593             0,
   3594             0,
   3595             0,
   3596             0,
   3597             0,
   3598             0,
   3599             0,
   3600             0,
   3601             0,
   3602             0,
   3603             0,
   3604             0,
   3605             0,
   3606             // `flags`.
   3607             0b0100_0101,
   3608             // `signCount`.
   3609             0,
   3610             0,
   3611             0,
   3612             0,
   3613             // `aaguid`.
   3614             0,
   3615             0,
   3616             0,
   3617             0,
   3618             0,
   3619             0,
   3620             0,
   3621             0,
   3622             0,
   3623             0,
   3624             0,
   3625             0,
   3626             0,
   3627             0,
   3628             0,
   3629             0,
   3630             // `credentialIdLength`.
   3631             0,
   3632             16,
   3633             // `credentialId`.
   3634             0,
   3635             0,
   3636             0,
   3637             0,
   3638             0,
   3639             0,
   3640             0,
   3641             0,
   3642             0,
   3643             0,
   3644             0,
   3645             0,
   3646             0,
   3647             0,
   3648             0,
   3649             0,
   3650             // P-384 COSE key.
   3651             cbor::MAP_5,
   3652             KTY,
   3653             EC2,
   3654             ALG,
   3655             cbor::NEG_INFO_24,
   3656             ES384,
   3657             // `crv`.
   3658             cbor::NEG_ONE,
   3659             // `P-384`.
   3660             cbor::TWO,
   3661             // `x`.
   3662             cbor::NEG_TWO,
   3663             cbor::BYTES_INFO_24,
   3664             48,
   3665             // x-coordinate. This will be overwritten later.
   3666             0,
   3667             0,
   3668             0,
   3669             0,
   3670             0,
   3671             0,
   3672             0,
   3673             0,
   3674             0,
   3675             0,
   3676             0,
   3677             0,
   3678             0,
   3679             0,
   3680             0,
   3681             0,
   3682             0,
   3683             0,
   3684             0,
   3685             0,
   3686             0,
   3687             0,
   3688             0,
   3689             0,
   3690             0,
   3691             0,
   3692             0,
   3693             0,
   3694             0,
   3695             0,
   3696             0,
   3697             0,
   3698             0,
   3699             0,
   3700             0,
   3701             0,
   3702             0,
   3703             0,
   3704             0,
   3705             0,
   3706             0,
   3707             0,
   3708             0,
   3709             0,
   3710             0,
   3711             0,
   3712             0,
   3713             0,
   3714             // `y`.
   3715             cbor::NEG_THREE,
   3716             cbor::BYTES_INFO_24,
   3717             48,
   3718             // y-coordinate. This will be overwritten later.
   3719             0,
   3720             0,
   3721             0,
   3722             0,
   3723             0,
   3724             0,
   3725             0,
   3726             0,
   3727             0,
   3728             0,
   3729             0,
   3730             0,
   3731             0,
   3732             0,
   3733             0,
   3734             0,
   3735             0,
   3736             0,
   3737             0,
   3738             0,
   3739             0,
   3740             0,
   3741             0,
   3742             0,
   3743             0,
   3744             0,
   3745             0,
   3746             0,
   3747             0,
   3748             0,
   3749             0,
   3750             0,
   3751             0,
   3752             0,
   3753             0,
   3754             0,
   3755             0,
   3756             0,
   3757             0,
   3758             0,
   3759             0,
   3760             0,
   3761             0,
   3762             0,
   3763             0,
   3764             0,
   3765             0,
   3766             0,
   3767         ];
   3768         let key = P384Key::from_bytes(
   3769             &[
   3770                 158, 99, 156, 49, 190, 211, 85, 167, 28, 2, 80, 57, 31, 22, 17, 38, 85, 78, 232,
   3771                 42, 45, 199, 154, 243, 136, 251, 84, 34, 5, 120, 208, 91, 61, 248, 64, 144, 87, 1,
   3772                 32, 86, 220, 68, 182, 11, 105, 223, 75, 70,
   3773             ]
   3774             .into(),
   3775         )
   3776         .unwrap()
   3777         .public_key();
   3778         let enc_key = key.to_encoded_point(false);
   3779         let pub_key = key.to_public_key_der().unwrap();
   3780         let att_obj_len = att_obj.len();
   3781         att_obj[att_obj_len - 99..att_obj_len - 51]
   3782             .copy_from_slice(enc_key.x().unwrap().as_slice());
   3783         att_obj[att_obj_len - 48..].copy_from_slice(enc_key.y().unwrap().as_slice());
   3784         let b64_cdata = base64url_nopad::encode(c_data_json.as_bytes());
   3785         let b64_adata = base64url_nopad::encode(&att_obj[att_obj.len() - 181..]);
   3786         let b64_key = base64url_nopad::encode(pub_key.as_bytes());
   3787         let b64_aobj = base64url_nopad::encode(att_obj.as_slice());
   3788         // Base case is valid.
   3789         assert!(
   3790             serde_json::from_str::<RegistrationRelaxed>(
   3791                 serde_json::json!({
   3792                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3793                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3794                     "response": {
   3795                         "clientDataJSON": b64_cdata,
   3796                         "authenticatorData": b64_adata,
   3797                         "transports": [],
   3798                         "publicKey": b64_key,
   3799                         "publicKeyAlgorithm": -35,
   3800                         "attestationObject": b64_aobj,
   3801                     },
   3802                     "clientExtensionResults": {},
   3803                     "type": "public-key"
   3804                 })
   3805                 .to_string()
   3806                 .as_str()
   3807             )
   3808             .map_or(false, |reg| reg.0.response.client_data_json
   3809                 == c_data_json.as_bytes()
   3810                 && reg.0.response.attestation_object_and_c_data_hash[..att_obj.len()]
   3811                     == att_obj
   3812                 && reg.0.response.attestation_object_and_c_data_hash[att_obj.len()..]
   3813                     == *Sha256::digest(c_data_json.as_bytes()).as_slice()
   3814                 && reg.0.response.transports.is_empty()
   3815                 && matches!(
   3816                     reg.0.authenticator_attachment,
   3817                     AuthenticatorAttachment::None
   3818                 )
   3819                 && reg.0.client_extension_results.cred_props.is_none()
   3820                 && reg.0.client_extension_results.prf.is_none())
   3821         );
   3822         // `publicKeyAlgorithm` mismatch.
   3823         let mut err = Error::invalid_value(
   3824             Unexpected::Other(&format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()),
   3825             &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es384).as_str()
   3826         )
   3827         .to_string().into_bytes();
   3828         assert_eq!(
   3829             serde_json::from_str::<RegistrationRelaxed>(
   3830                 serde_json::json!({
   3831                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3832                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3833                     "response": {
   3834                         "clientDataJSON": b64_cdata,
   3835                         "authenticatorData": b64_adata,
   3836                         "transports": [],
   3837                         "publicKey": b64_key,
   3838                         "publicKeyAlgorithm": -7,
   3839                         "attestationObject": b64_aobj,
   3840                     },
   3841                     "clientExtensionResults": {},
   3842                     "type": "public-key"
   3843                 })
   3844                 .to_string()
   3845                 .as_str()
   3846             )
   3847             .unwrap_err()
   3848             .to_string()
   3849             .into_bytes()[..err.len()],
   3850             err
   3851         );
   3852         // Missing `publicKeyAlgorithm`.
   3853         assert!(
   3854             serde_json::from_str::<RegistrationRelaxed>(
   3855                 serde_json::json!({
   3856                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3857                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3858                     "response": {
   3859                         "clientDataJSON": b64_cdata,
   3860                         "authenticatorData": b64_adata,
   3861                         "transports": [],
   3862                         "publicKey": b64_key,
   3863                         "attestationObject": b64_aobj,
   3864                     },
   3865                     "clientExtensionResults": {},
   3866                     "type": "public-key"
   3867                 })
   3868                 .to_string()
   3869                 .as_str()
   3870             )
   3871             .is_ok()
   3872         );
   3873         // `null` `publicKeyAlgorithm`.
   3874         assert!(
   3875             serde_json::from_str::<RegistrationRelaxed>(
   3876                 serde_json::json!({
   3877                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3878                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3879                     "response": {
   3880                         "clientDataJSON": b64_cdata,
   3881                         "authenticatorData": b64_adata,
   3882                         "transports": [],
   3883                         "publicKey": b64_key,
   3884                         "publicKeyAlgorithm": null,
   3885                         "attestationObject": b64_aobj,
   3886                     },
   3887                     "clientExtensionResults": {},
   3888                     "type": "public-key"
   3889                 })
   3890                 .to_string()
   3891                 .as_str()
   3892             )
   3893             .is_ok()
   3894         );
   3895         // `publicKey` mismatch.
   3896         let bad_pub_key = P384PubKey::from_encoded_point(&P384Pt::from_affine_coordinates(
   3897             &[
   3898                 192, 10, 27, 46, 66, 67, 80, 98, 33, 230, 156, 95, 1, 135, 150, 110, 64, 243, 22,
   3899                 118, 5, 255, 107, 44, 234, 111, 217, 105, 125, 114, 39, 7, 126, 2, 191, 111, 48,
   3900                 93, 234, 175, 18, 172, 59, 28, 97, 106, 178, 152,
   3901             ]
   3902             .into(),
   3903             &[
   3904                 57, 36, 196, 12, 109, 129, 253, 115, 88, 154, 6, 43, 195, 85, 169, 5, 230, 51, 28,
   3905                 205, 142, 28, 150, 35, 24, 222, 170, 253, 14, 248, 84, 151, 109, 191, 152, 111,
   3906                 222, 70, 134, 247, 109, 171, 211, 33, 214, 217, 200, 111,
   3907             ]
   3908             .into(),
   3909             false,
   3910         ))
   3911         .unwrap();
   3912         err = Error::invalid_value(
   3913             Unexpected::Bytes([0; 32].as_slice()),
   3914             &format!(
   3915                 "DER-encoded public key to match the public key within the attestation object: P384(UncompressedP384PubKey({:?}, {:?}))",
   3916                 &att_obj[att_obj.len() - 99..att_obj.len() - 51],
   3917                 &att_obj[att_obj.len() - 48..],
   3918             )
   3919             .as_str(),
   3920         )
   3921         .to_string().into_bytes();
   3922         assert_eq!(serde_json::from_str::<RegistrationRelaxed>(
   3923             serde_json::json!({
   3924                 "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3925                 "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3926                 "response": {
   3927                     "clientDataJSON": b64_cdata,
   3928                     "authenticatorData": b64_adata,
   3929                     "transports": [],
   3930                     "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()),
   3931                     "publicKeyAlgorithm": -35,
   3932                     "attestationObject": b64_aobj,
   3933                 },
   3934                 "clientExtensionResults": {},
   3935                 "type": "public-key"
   3936             }).to_string().as_str()
   3937         ).unwrap_err().to_string().into_bytes()[..err.len()], err);
   3938         // Missing `publicKey`.
   3939         assert!(
   3940             serde_json::from_str::<RegistrationRelaxed>(
   3941                 serde_json::json!({
   3942                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3943                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3944                     "response": {
   3945                         "clientDataJSON": b64_cdata,
   3946                         "authenticatorData": b64_adata,
   3947                         "transports": [],
   3948                         "publicKeyAlgorithm": -35,
   3949                         "attestationObject": b64_aobj,
   3950                     },
   3951                     "clientExtensionResults": {},
   3952                     "type": "public-key"
   3953                 })
   3954                 .to_string()
   3955                 .as_str()
   3956             )
   3957             .is_ok()
   3958         );
   3959         // `publicKeyAlgorithm` mismatch when `publicKey` does not exist.
   3960         err = Error::invalid_value(
   3961             Unexpected::Other(&format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()),
   3962             &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es384).as_str()
   3963         )
   3964         .to_string().into_bytes();
   3965         assert_eq!(
   3966             serde_json::from_str::<RegistrationRelaxed>(
   3967                 serde_json::json!({
   3968                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3969                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3970                     "response": {
   3971                         "clientDataJSON": b64_cdata,
   3972                         "authenticatorData": b64_adata,
   3973                         "transports": [],
   3974                         "publicKeyAlgorithm": -8,
   3975                         "attestationObject": b64_aobj,
   3976                     },
   3977                     "clientExtensionResults": {},
   3978                     "type": "public-key"
   3979                 })
   3980                 .to_string()
   3981                 .as_str()
   3982             )
   3983             .unwrap_err()
   3984             .to_string()
   3985             .into_bytes()[..err.len()],
   3986             err
   3987         );
   3988         // `null` `publicKey`.
   3989         assert!(
   3990             serde_json::from_str::<RegistrationRelaxed>(
   3991                 serde_json::json!({
   3992                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3993                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3994                     "response": {
   3995                         "clientDataJSON": b64_cdata,
   3996                         "authenticatorData": b64_adata,
   3997                         "transports": [],
   3998                         "publicKey": null,
   3999                         "publicKeyAlgorithm": -35,
   4000                         "attestationObject": b64_aobj,
   4001                     },
   4002                     "clientExtensionResults": {},
   4003                     "type": "public-key"
   4004                 })
   4005                 .to_string()
   4006                 .as_str()
   4007             )
   4008             .is_ok()
   4009         );
   4010         // `publicKeyAlgorithm` mismatch when `publicKey` is null.
   4011         err = Error::invalid_value(
   4012             Unexpected::Other(&format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()),
   4013             &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es384).as_str()
   4014         )
   4015         .to_string().into_bytes();
   4016         assert_eq!(
   4017             serde_json::from_str::<RegistrationRelaxed>(
   4018                 serde_json::json!({
   4019                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4020                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4021                     "response": {
   4022                         "clientDataJSON": b64_cdata,
   4023                         "authenticatorData": b64_adata,
   4024                         "transports": [],
   4025                         "publicKey": null,
   4026                         "publicKeyAlgorithm": -8,
   4027                         "attestationObject": b64_aobj,
   4028                     },
   4029                     "clientExtensionResults": {},
   4030                     "type": "public-key"
   4031                 })
   4032                 .to_string()
   4033                 .as_str()
   4034             )
   4035             .unwrap_err()
   4036             .to_string()
   4037             .into_bytes()[..err.len()],
   4038             err
   4039         );
   4040         // Base case is valid.
   4041         assert!(
   4042             serde_json::from_str::<CustomRegistration>(
   4043                 serde_json::json!({
   4044                     "clientDataJSON": b64_cdata,
   4045                     "transports": [],
   4046                     "attestationObject": b64_aobj,
   4047                     "clientExtensionResults": {},
   4048                     "type": "public-key"
   4049                 })
   4050                 .to_string()
   4051                 .as_str()
   4052             )
   4053             .map_or(false, |reg| reg.0.response.client_data_json
   4054                 == c_data_json.as_bytes()
   4055                 && reg.0.response.attestation_object_and_c_data_hash[..att_obj.len()]
   4056                     == att_obj
   4057                 && reg.0.response.attestation_object_and_c_data_hash[att_obj.len()..]
   4058                     == *Sha256::digest(c_data_json.as_bytes()).as_slice()
   4059                 && reg.0.response.transports.is_empty()
   4060                 && matches!(
   4061                     reg.0.authenticator_attachment,
   4062                     AuthenticatorAttachment::None
   4063                 )
   4064                 && reg.0.client_extension_results.cred_props.is_none()
   4065                 && reg.0.client_extension_results.prf.is_none())
   4066         );
   4067     }
   4068     #[test]
   4069     fn rs256_registration_deserialize_data_mismatch() {
   4070         let c_data_json = serde_json::json!({}).to_string();
   4071         let mut att_obj = [
   4072             cbor::MAP_3,
   4073             cbor::TEXT_3,
   4074             b'f',
   4075             b'm',
   4076             b't',
   4077             cbor::TEXT_4,
   4078             b'n',
   4079             b'o',
   4080             b'n',
   4081             b'e',
   4082             cbor::TEXT_7,
   4083             b'a',
   4084             b't',
   4085             b't',
   4086             b'S',
   4087             b't',
   4088             b'm',
   4089             b't',
   4090             cbor::MAP_0,
   4091             cbor::TEXT_8,
   4092             b'a',
   4093             b'u',
   4094             b't',
   4095             b'h',
   4096             b'D',
   4097             b'a',
   4098             b't',
   4099             b'a',
   4100             cbor::BYTES_INFO_25,
   4101             1,
   4102             87,
   4103             // `rpIdHash`.
   4104             0,
   4105             0,
   4106             0,
   4107             0,
   4108             0,
   4109             0,
   4110             0,
   4111             0,
   4112             0,
   4113             0,
   4114             0,
   4115             0,
   4116             0,
   4117             0,
   4118             0,
   4119             0,
   4120             0,
   4121             0,
   4122             0,
   4123             0,
   4124             0,
   4125             0,
   4126             0,
   4127             0,
   4128             0,
   4129             0,
   4130             0,
   4131             0,
   4132             0,
   4133             0,
   4134             0,
   4135             0,
   4136             // `flags`.
   4137             0b0100_0101,
   4138             // `signCount`.
   4139             0,
   4140             0,
   4141             0,
   4142             0,
   4143             // `aaguid`.
   4144             0,
   4145             0,
   4146             0,
   4147             0,
   4148             0,
   4149             0,
   4150             0,
   4151             0,
   4152             0,
   4153             0,
   4154             0,
   4155             0,
   4156             0,
   4157             0,
   4158             0,
   4159             0,
   4160             // `credentialIdLength`.
   4161             0,
   4162             16,
   4163             // `credentialId`.
   4164             0,
   4165             0,
   4166             0,
   4167             0,
   4168             0,
   4169             0,
   4170             0,
   4171             0,
   4172             0,
   4173             0,
   4174             0,
   4175             0,
   4176             0,
   4177             0,
   4178             0,
   4179             0,
   4180             // RSA COSE key.
   4181             cbor::MAP_4,
   4182             KTY,
   4183             RSA,
   4184             ALG,
   4185             cbor::NEG_INFO_25,
   4186             // RS256.
   4187             1,
   4188             0,
   4189             // `n`.
   4190             cbor::NEG_ONE,
   4191             cbor::BYTES_INFO_25,
   4192             1,
   4193             0,
   4194             // n. This will be overwritten later.
   4195             0,
   4196             0,
   4197             0,
   4198             0,
   4199             0,
   4200             0,
   4201             0,
   4202             0,
   4203             0,
   4204             0,
   4205             0,
   4206             0,
   4207             0,
   4208             0,
   4209             0,
   4210             0,
   4211             0,
   4212             0,
   4213             0,
   4214             0,
   4215             0,
   4216             0,
   4217             0,
   4218             0,
   4219             0,
   4220             0,
   4221             0,
   4222             0,
   4223             0,
   4224             0,
   4225             0,
   4226             0,
   4227             0,
   4228             0,
   4229             0,
   4230             0,
   4231             0,
   4232             0,
   4233             0,
   4234             0,
   4235             0,
   4236             0,
   4237             0,
   4238             0,
   4239             0,
   4240             0,
   4241             0,
   4242             0,
   4243             0,
   4244             0,
   4245             0,
   4246             0,
   4247             0,
   4248             0,
   4249             0,
   4250             0,
   4251             0,
   4252             0,
   4253             0,
   4254             0,
   4255             0,
   4256             0,
   4257             0,
   4258             0,
   4259             0,
   4260             0,
   4261             0,
   4262             0,
   4263             0,
   4264             0,
   4265             0,
   4266             0,
   4267             0,
   4268             0,
   4269             0,
   4270             0,
   4271             0,
   4272             0,
   4273             0,
   4274             0,
   4275             0,
   4276             0,
   4277             0,
   4278             0,
   4279             0,
   4280             0,
   4281             0,
   4282             0,
   4283             0,
   4284             0,
   4285             0,
   4286             0,
   4287             0,
   4288             0,
   4289             0,
   4290             0,
   4291             0,
   4292             0,
   4293             0,
   4294             0,
   4295             0,
   4296             0,
   4297             0,
   4298             0,
   4299             0,
   4300             0,
   4301             0,
   4302             0,
   4303             0,
   4304             0,
   4305             0,
   4306             0,
   4307             0,
   4308             0,
   4309             0,
   4310             0,
   4311             0,
   4312             0,
   4313             0,
   4314             0,
   4315             0,
   4316             0,
   4317             0,
   4318             0,
   4319             0,
   4320             0,
   4321             0,
   4322             0,
   4323             0,
   4324             0,
   4325             0,
   4326             0,
   4327             0,
   4328             0,
   4329             0,
   4330             0,
   4331             0,
   4332             0,
   4333             0,
   4334             0,
   4335             0,
   4336             0,
   4337             0,
   4338             0,
   4339             0,
   4340             0,
   4341             0,
   4342             0,
   4343             0,
   4344             0,
   4345             0,
   4346             0,
   4347             0,
   4348             0,
   4349             0,
   4350             0,
   4351             0,
   4352             0,
   4353             0,
   4354             0,
   4355             0,
   4356             0,
   4357             0,
   4358             0,
   4359             0,
   4360             0,
   4361             0,
   4362             0,
   4363             0,
   4364             0,
   4365             0,
   4366             0,
   4367             0,
   4368             0,
   4369             0,
   4370             0,
   4371             0,
   4372             0,
   4373             0,
   4374             0,
   4375             0,
   4376             0,
   4377             0,
   4378             0,
   4379             0,
   4380             0,
   4381             0,
   4382             0,
   4383             0,
   4384             0,
   4385             0,
   4386             0,
   4387             0,
   4388             0,
   4389             0,
   4390             0,
   4391             0,
   4392             0,
   4393             0,
   4394             0,
   4395             0,
   4396             0,
   4397             0,
   4398             0,
   4399             0,
   4400             0,
   4401             0,
   4402             0,
   4403             0,
   4404             0,
   4405             0,
   4406             0,
   4407             0,
   4408             0,
   4409             0,
   4410             0,
   4411             0,
   4412             0,
   4413             0,
   4414             0,
   4415             0,
   4416             0,
   4417             0,
   4418             0,
   4419             0,
   4420             0,
   4421             0,
   4422             0,
   4423             0,
   4424             0,
   4425             0,
   4426             0,
   4427             0,
   4428             0,
   4429             0,
   4430             0,
   4431             0,
   4432             0,
   4433             0,
   4434             0,
   4435             0,
   4436             0,
   4437             0,
   4438             0,
   4439             0,
   4440             0,
   4441             0,
   4442             0,
   4443             0,
   4444             0,
   4445             0,
   4446             0,
   4447             0,
   4448             0,
   4449             0,
   4450             0,
   4451             // `e`.
   4452             cbor::NEG_TWO,
   4453             cbor::BYTES | 3,
   4454             // e.
   4455             1,
   4456             0,
   4457             1,
   4458         ];
   4459         let n = [
   4460             111, 183, 124, 133, 38, 167, 70, 148, 44, 50, 30, 60, 121, 14, 38, 37, 96, 114, 107,
   4461             195, 248, 64, 79, 36, 237, 140, 43, 27, 94, 74, 102, 152, 135, 102, 184, 150, 186, 206,
   4462             185, 19, 165, 209, 48, 98, 98, 9, 3, 205, 208, 82, 250, 105, 132, 201, 73, 62, 60, 165,
   4463             100, 128, 153, 9, 41, 118, 66, 95, 236, 214, 73, 135, 197, 68, 184, 10, 27, 116, 204,
   4464             145, 50, 174, 58, 42, 183, 181, 119, 232, 126, 252, 217, 96, 162, 190, 103, 122, 64,
   4465             87, 145, 45, 32, 207, 17, 239, 223, 3, 35, 14, 112, 119, 124, 141, 123, 208, 239, 105,
   4466             81, 217, 151, 162, 190, 17, 88, 182, 176, 158, 81, 200, 42, 166, 133, 48, 23, 236, 55,
   4467             117, 248, 233, 151, 203, 122, 155, 231, 46, 177, 20, 20, 151, 64, 222, 239, 226, 7, 21,
   4468             254, 81, 202, 64, 232, 161, 235, 22, 51, 246, 207, 213, 0, 229, 138, 46, 222, 205, 157,
   4469             108, 139, 253, 230, 80, 50, 2, 122, 212, 163, 100, 180, 114, 12, 113, 52, 56, 99, 188,
   4470             42, 198, 212, 23, 182, 222, 56, 221, 200, 79, 96, 239, 221, 135, 10, 17, 106, 183, 56,
   4471             104, 68, 94, 198, 196, 35, 200, 83, 204, 26, 185, 204, 212, 31, 183, 19, 111, 233, 13,
   4472             72, 93, 53, 65, 111, 59, 242, 122, 160, 244, 162, 126, 38, 235, 156, 47, 88, 39, 132,
   4473             153, 79, 0, 133, 78, 7, 218, 165, 241,
   4474         ];
   4475         let e = 65537u32;
   4476         let d = [
   4477             145, 79, 21, 97, 233, 3, 192, 194, 177, 68, 181, 80, 120, 197, 23, 44, 185, 74, 144, 0,
   4478             132, 149, 139, 11, 16, 224, 4, 112, 236, 94, 238, 97, 121, 124, 213, 145, 24, 253, 168,
   4479             35, 190, 205, 132, 115, 33, 201, 38, 253, 246, 180, 66, 155, 165, 46, 3, 254, 68, 108,
   4480             154, 247, 246, 45, 187, 0, 204, 96, 185, 157, 249, 174, 158, 38, 62, 244, 183, 76, 102,
   4481             6, 219, 92, 212, 138, 59, 147, 163, 219, 111, 39, 105, 21, 236, 196, 38, 255, 114, 247,
   4482             82, 104, 113, 204, 29, 152, 209, 219, 48, 239, 74, 129, 19, 247, 33, 239, 119, 166,
   4483             216, 152, 94, 138, 238, 164, 242, 129, 50, 150, 57, 20, 53, 224, 56, 241, 138, 97, 111,
   4484             215, 107, 212, 195, 146, 108, 143, 0, 229, 181, 171, 73, 152, 105, 146, 25, 243, 242,
   4485             140, 252, 248, 162, 247, 63, 168, 180, 20, 153, 120, 10, 248, 211, 1, 71, 127, 212,
   4486             249, 237, 203, 202, 48, 26, 216, 226, 228, 186, 13, 204, 70, 255, 240, 89, 255, 59, 83,
   4487             31, 253, 55, 43, 158, 90, 248, 83, 32, 159, 105, 57, 134, 34, 96, 18, 255, 245, 153,
   4488             162, 60, 91, 99, 220, 51, 44, 85, 114, 67, 125, 202, 65, 217, 245, 40, 8, 81, 165, 142,
   4489             24, 245, 127, 122, 247, 152, 212, 75, 45, 59, 90, 184, 234, 31, 147, 36, 8, 212, 45,
   4490             50, 23, 3, 25, 253, 87, 227, 79, 119, 161,
   4491         ];
   4492         let p = BigUint::from_bytes_le(
   4493             [
   4494                 215, 166, 5, 21, 11, 179, 41, 77, 198, 92, 165, 48, 77, 162, 42, 41, 206, 141, 60,
   4495                 69, 47, 164, 19, 92, 46, 72, 100, 238, 100, 53, 214, 197, 163, 185, 6, 140, 229,
   4496                 250, 195, 77, 8, 12, 5, 236, 178, 173, 86, 201, 43, 213, 165, 51, 108, 101, 161,
   4497                 99, 76, 240, 14, 234, 76, 197, 137, 53, 198, 168, 135, 205, 212, 198, 120, 29, 16,
   4498                 82, 98, 233, 236, 177, 12, 171, 141, 100, 107, 146, 33, 176, 125, 202, 172, 79,
   4499                 147, 179, 30, 62, 247, 206, 169, 19, 168, 114, 26, 73, 108, 178, 105, 84, 89, 191,
   4500                 168, 253, 228, 214, 54, 16, 212, 199, 111, 72, 3, 41, 247, 227, 165, 244, 32, 188,
   4501                 24, 247,
   4502             ]
   4503             .as_slice(),
   4504         );
   4505         let p_2 = BigUint::from_bytes_le(
   4506             [
   4507                 41, 25, 198, 240, 134, 206, 121, 57, 11, 5, 134, 192, 212, 77, 229, 197, 14, 78,
   4508                 85, 212, 190, 114, 179, 188, 21, 171, 174, 12, 104, 74, 15, 164, 136, 173, 62, 177,
   4509                 141, 213, 93, 102, 147, 83, 59, 124, 146, 59, 175, 213, 55, 27, 25, 248, 154, 29,
   4510                 39, 85, 50, 235, 134, 60, 203, 106, 186, 195, 190, 185, 71, 169, 142, 236, 92, 11,
   4511                 250, 187, 198, 8, 201, 184, 120, 178, 227, 87, 63, 243, 89, 227, 234, 184, 28, 252,
   4512                 112, 211, 193, 69, 23, 92, 5, 72, 93, 53, 69, 159, 73, 160, 105, 244, 249, 94, 214,
   4513                 173, 9, 236, 4, 255, 129, 11, 224, 140, 252, 168, 57, 143, 176, 241, 60, 219, 90,
   4514                 250,
   4515             ]
   4516             .as_slice(),
   4517         );
   4518         let key = RsaPrivateKey::from_components(
   4519             BigUint::from_bytes_le(n.as_slice()),
   4520             e.into(),
   4521             BigUint::from_bytes_le(d.as_slice()),
   4522             vec![p, p_2],
   4523         )
   4524         .unwrap()
   4525         .to_public_key();
   4526         let pub_key = key.to_public_key_der().unwrap();
   4527         let att_obj_len = att_obj.len();
   4528         att_obj[att_obj_len - 261..att_obj_len - 5]
   4529             .copy_from_slice(key.n().to_bytes_be().as_slice());
   4530         let b64_cdata = base64url_nopad::encode(c_data_json.as_bytes());
   4531         let b64_adata = base64url_nopad::encode(&att_obj[att_obj.len() - 343..]);
   4532         let b64_key = base64url_nopad::encode(pub_key.as_bytes());
   4533         let b64_aobj = base64url_nopad::encode(att_obj.as_slice());
   4534         // Base case is valid.
   4535         assert!(
   4536             serde_json::from_str::<RegistrationRelaxed>(
   4537                 serde_json::json!({
   4538                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4539                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4540                     "response": {
   4541                         "clientDataJSON": b64_cdata,
   4542                         "authenticatorData": b64_adata,
   4543                         "transports": [],
   4544                         "publicKey": b64_key,
   4545                         "publicKeyAlgorithm": -257,
   4546                         "attestationObject": b64_aobj,
   4547                     },
   4548                     "clientExtensionResults": {},
   4549                     "type": "public-key"
   4550                 })
   4551                 .to_string()
   4552                 .as_str()
   4553             )
   4554             .map_or(false, |reg| reg.0.response.client_data_json
   4555                 == c_data_json.as_bytes()
   4556                 && reg.0.response.attestation_object_and_c_data_hash[..att_obj.len()]
   4557                     == att_obj
   4558                 && reg.0.response.attestation_object_and_c_data_hash[att_obj.len()..]
   4559                     == *Sha256::digest(c_data_json.as_bytes()).as_slice()
   4560                 && reg.0.response.transports.is_empty()
   4561                 && matches!(
   4562                     reg.0.authenticator_attachment,
   4563                     AuthenticatorAttachment::None
   4564                 )
   4565                 && reg.0.client_extension_results.cred_props.is_none()
   4566                 && reg.0.client_extension_results.prf.is_none())
   4567         );
   4568         // `publicKeyAlgorithm` mismatch.
   4569         let mut err = Error::invalid_value(
   4570             Unexpected::Other(&format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()),
   4571             &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Rs256).as_str()
   4572         )
   4573         .to_string().into_bytes();
   4574         assert_eq!(
   4575             serde_json::from_str::<RegistrationRelaxed>(
   4576                 serde_json::json!({
   4577                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4578                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4579                     "response": {
   4580                         "clientDataJSON": b64_cdata,
   4581                         "authenticatorData": b64_adata,
   4582                         "transports": [],
   4583                         "publicKey": b64_key,
   4584                         "publicKeyAlgorithm": -8,
   4585                         "attestationObject": b64_aobj,
   4586                     },
   4587                     "clientExtensionResults": {},
   4588                     "type": "public-key"
   4589                 })
   4590                 .to_string()
   4591                 .as_str()
   4592             )
   4593             .unwrap_err()
   4594             .to_string()
   4595             .into_bytes()[..err.len()],
   4596             err
   4597         );
   4598         // Missing `publicKeyAlgorithm`.
   4599         assert!(
   4600             serde_json::from_str::<RegistrationRelaxed>(
   4601                 serde_json::json!({
   4602                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4603                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4604                     "response": {
   4605                         "clientDataJSON": b64_cdata,
   4606                         "authenticatorData": b64_adata,
   4607                         "transports": [],
   4608                         "publicKey": b64_key,
   4609                         "attestationObject": b64_aobj,
   4610                     },
   4611                     "clientExtensionResults": {},
   4612                     "type": "public-key"
   4613                 })
   4614                 .to_string()
   4615                 .as_str()
   4616             )
   4617             .is_ok()
   4618         );
   4619         // `null` `publicKeyAlgorithm`.
   4620         assert!(
   4621             serde_json::from_str::<RegistrationRelaxed>(
   4622                 serde_json::json!({
   4623                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4624                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4625                     "response": {
   4626                         "clientDataJSON": b64_cdata,
   4627                         "authenticatorData": b64_adata,
   4628                         "transports": [],
   4629                         "publicKey": b64_key,
   4630                         "publicKeyAlgorithm": null,
   4631                         "attestationObject": b64_aobj,
   4632                     },
   4633                     "clientExtensionResults": {},
   4634                     "type": "public-key"
   4635                 })
   4636                 .to_string()
   4637                 .as_str()
   4638             )
   4639             .is_ok()
   4640         );
   4641         // `publicKey` mismatch.
   4642         let bad_pub_key = RsaPrivateKey::from_components(
   4643             BigUint::from_bytes_le(
   4644                 [
   4645                     175, 161, 161, 75, 52, 244, 72, 168, 29, 119, 33, 120, 3, 222, 231, 152, 222,
   4646                     119, 112, 83, 221, 237, 74, 174, 79, 216, 147, 251, 245, 94, 234, 114, 254, 21,
   4647                     17, 254, 8, 115, 75, 127, 150, 87, 59, 109, 230, 116, 85, 90, 11, 160, 63, 217,
   4648                     9, 38, 187, 250, 226, 183, 38, 164, 182, 218, 22, 19, 58, 189, 83, 219, 11,
   4649                     144, 15, 99, 151, 166, 46, 57, 17, 111, 189, 131, 142, 113, 85, 122, 188, 238,
   4650                     52, 21, 116, 125, 102, 195, 182, 165, 29, 156, 213, 182, 125, 156, 88, 56, 221,
   4651                     2, 98, 43, 210, 115, 32, 4, 105, 88, 181, 158, 207, 236, 162, 250, 253, 240,
   4652                     72, 8, 253, 50, 220, 247, 76, 170, 143, 68, 225, 231, 113, 64, 244, 17, 138,
   4653                     162, 233, 33, 2, 67, 11, 223, 188, 232, 152, 193, 20, 32, 243, 52, 64, 43, 2,
   4654                     243, 8, 77, 150, 232, 109, 148, 95, 127, 55, 71, 162, 34, 54, 83, 135, 52, 172,
   4655                     191, 32, 42, 106, 43, 211, 206, 100, 104, 110, 232, 5, 43, 120, 180, 166, 40,
   4656                     144, 233, 239, 103, 134, 103, 255, 224, 138, 184, 208, 137, 127, 36, 189, 143,
   4657                     248, 201, 2, 218, 51, 232, 96, 30, 83, 124, 109, 241, 23, 179, 247, 151, 238,
   4658                     212, 204, 44, 43, 223, 148, 241, 172, 10, 235, 155, 94, 68, 116, 24, 116, 191,
   4659                     86, 53, 127, 35, 133, 198, 204, 59, 76, 110, 16, 1, 15, 148, 135, 157,
   4660                 ]
   4661                 .as_slice(),
   4662             ),
   4663             65537u32.into(),
   4664             BigUint::from_bytes_le(
   4665                 [
   4666                     129, 93, 123, 251, 104, 29, 84, 203, 116, 100, 75, 237, 111, 160, 12, 100, 172,
   4667                     76, 57, 178, 144, 235, 81, 61, 115, 243, 28, 40, 183, 22, 56, 150, 68, 38, 220,
   4668                     62, 233, 110, 48, 174, 35, 197, 244, 109, 148, 109, 36, 69, 69, 82, 225, 113,
   4669                     175, 6, 239, 27, 193, 101, 50, 239, 122, 102, 7, 46, 98, 79, 195, 116, 155,
   4670                     158, 138, 147, 51, 93, 24, 237, 246, 82, 14, 109, 144, 250, 239, 93, 63, 214,
   4671                     96, 130, 226, 134, 198, 145, 161, 11, 231, 97, 214, 180, 255, 95, 158, 88, 108,
   4672                     254, 243, 177, 133, 184, 92, 95, 148, 88, 55, 124, 245, 244, 84, 86, 4, 121,
   4673                     44, 231, 97, 176, 190, 29, 155, 40, 57, 69, 165, 80, 168, 9, 56, 43, 233, 6,
   4674                     14, 157, 112, 223, 64, 88, 141, 7, 65, 23, 64, 208, 6, 83, 61, 8, 182, 248,
   4675                     126, 84, 179, 163, 80, 238, 90, 133, 4, 14, 71, 177, 175, 27, 29, 151, 211,
   4676                     108, 162, 195, 7, 157, 167, 86, 169, 3, 87, 235, 89, 158, 237, 216, 31, 243,
   4677                     197, 62, 5, 84, 131, 230, 186, 248, 49, 12, 93, 244, 61, 135, 180, 17, 162,
   4678                     241, 13, 115, 241, 138, 219, 98, 155, 166, 191, 63, 12, 37, 1, 165, 178, 84,
   4679                     200, 72, 80, 41, 77, 136, 217, 141, 246, 209, 31, 243, 159, 71, 43, 246, 159,
   4680                     182, 171, 116, 12, 3, 142, 235, 218, 164, 70, 90, 147, 238, 42, 75,
   4681                 ]
   4682                 .as_slice(),
   4683             ),
   4684             vec![
   4685                 BigUint::from_bytes_le(
   4686                     [
   4687                         215, 199, 110, 28, 64, 16, 16, 109, 106, 152, 150, 124, 52, 166, 121, 92,
   4688                         242, 13, 0, 69, 7, 152, 72, 172, 118, 63, 156, 180, 140, 39, 53, 29, 197,
   4689                         224, 177, 48, 41, 221, 102, 65, 17, 185, 55, 62, 219, 152, 227, 7, 78, 219,
   4690                         14, 139, 71, 204, 144, 152, 14, 39, 247, 244, 165, 224, 234, 60, 213, 74,
   4691                         237, 30, 102, 177, 242, 138, 168, 31, 122, 47, 206, 155, 225, 113, 103,
   4692                         175, 152, 244, 27, 233, 112, 223, 248, 38, 215, 178, 20, 244, 8, 121, 26,
   4693                         11, 70, 122, 16, 85, 167, 87, 64, 216, 228, 227, 173, 57, 250, 8, 221, 38,
   4694                         12, 203, 212, 1, 112, 43, 72, 91, 225, 97, 228, 57, 154, 193,
   4695                     ]
   4696                     .as_slice(),
   4697                 ),
   4698                 BigUint::from_bytes_le(
   4699                     [
   4700                         233, 89, 204, 152, 31, 242, 8, 110, 38, 190, 111, 159, 105, 105, 45, 85,
   4701                         15, 244, 30, 250, 174, 226, 219, 111, 107, 191, 196, 135, 17, 123, 186,
   4702                         167, 85, 13, 120, 197, 159, 129, 78, 237, 152, 31, 230, 26, 229, 253, 197,
   4703                         211, 105, 204, 126, 142, 250, 55, 26, 172, 65, 160, 45, 6, 99, 86, 66, 238,
   4704                         107, 6, 98, 171, 93, 224, 201, 160, 31, 204, 82, 120, 228, 158, 238, 6,
   4705                         190, 12, 150, 153, 239, 95, 57, 71, 100, 239, 235, 155, 73, 200, 5, 225,
   4706                         127, 185, 46, 48, 243, 84, 33, 142, 17, 19, 20, 23, 215, 16, 114, 58, 211,
   4707                         14, 73, 148, 168, 252, 159, 252, 125, 57, 101, 211, 188, 12, 77, 208,
   4708                     ]
   4709                     .as_slice(),
   4710                 ),
   4711             ],
   4712         )
   4713         .unwrap()
   4714         .to_public_key();
   4715         err = Error::invalid_value(
   4716             Unexpected::Bytes([0; 32].as_slice()),
   4717             &format!(
   4718                 "DER-encoded public key to match the public key within the attestation object: Rsa(RsaPubKey({:?}, 65537))",
   4719                 &att_obj[att_obj.len() - 261..att_obj.len() - 5],
   4720             )
   4721             .as_str(),
   4722         )
   4723         .to_string().into_bytes();
   4724         assert_eq!(serde_json::from_str::<RegistrationRelaxed>(
   4725             serde_json::json!({
   4726                 "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4727                 "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4728                 "response": {
   4729                     "clientDataJSON": b64_cdata,
   4730                     "authenticatorData": b64_adata,
   4731                     "transports": [],
   4732                     "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()),
   4733                     "publicKeyAlgorithm": -257,
   4734                     "attestationObject": b64_aobj,
   4735                 },
   4736                 "clientExtensionResults": {},
   4737                 "type": "public-key"
   4738             })
   4739             .to_string()
   4740             .as_str()
   4741             )
   4742            .unwrap_err().to_string().into_bytes()[..err.len()],
   4743            err
   4744         );
   4745         // Missing `publicKey`.
   4746         assert!(
   4747             serde_json::from_str::<RegistrationRelaxed>(
   4748                 serde_json::json!({
   4749                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4750                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4751                     "response": {
   4752                         "clientDataJSON": b64_cdata,
   4753                         "authenticatorData": b64_adata,
   4754                         "transports": [],
   4755                         "publicKeyAlgorithm": -257,
   4756                         "attestationObject": b64_aobj,
   4757                     },
   4758                     "clientExtensionResults": {},
   4759                     "type": "public-key"
   4760                 })
   4761                 .to_string()
   4762                 .as_str()
   4763             )
   4764             .is_ok()
   4765         );
   4766         // `null` `publicKey`.
   4767         assert!(
   4768             serde_json::from_str::<RegistrationRelaxed>(
   4769                 serde_json::json!({
   4770                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4771                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4772                     "response": {
   4773                         "clientDataJSON": b64_cdata,
   4774                         "authenticatorData": b64_adata,
   4775                         "transports": [],
   4776                         "publicKey": null,
   4777                         "publicKeyAlgorithm": -257,
   4778                         "attestationObject": b64_aobj,
   4779                     },
   4780                     "clientExtensionResults": {},
   4781                     "type": "public-key"
   4782                 })
   4783                 .to_string()
   4784                 .as_str()
   4785             )
   4786             .is_ok()
   4787         );
   4788         // Base case is valid.
   4789         assert!(
   4790             serde_json::from_str::<CustomRegistration>(
   4791                 serde_json::json!({
   4792                     "clientDataJSON": b64_cdata,
   4793                     "transports": [],
   4794                     "attestationObject": b64_aobj,
   4795                     "clientExtensionResults": {},
   4796                     "type": "public-key"
   4797                 })
   4798                 .to_string()
   4799                 .as_str()
   4800             )
   4801             .map_or(false, |reg| reg.0.response.client_data_json
   4802                 == c_data_json.as_bytes()
   4803                 && reg.0.response.attestation_object_and_c_data_hash[..att_obj.len()]
   4804                     == att_obj
   4805                 && reg.0.response.attestation_object_and_c_data_hash[att_obj.len()..]
   4806                     == *Sha256::digest(c_data_json.as_bytes()).as_slice()
   4807                 && reg.0.response.transports.is_empty()
   4808                 && matches!(
   4809                     reg.0.authenticator_attachment,
   4810                     AuthenticatorAttachment::None
   4811                 )
   4812                 && reg.0.client_extension_results.cred_props.is_none()
   4813                 && reg.0.client_extension_results.prf.is_none())
   4814         );
   4815     }
   4816 }