webauthn_rp

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

ser.rs (228137B)


      1 use super::{
      2     super::{
      3         super::request::register::CoseAlgorithmIdentifier,
      4         ser::{
      5             AuthenticationExtensionsPrfOutputsHelper, AuthenticationExtensionsPrfValues,
      6             Base64DecodedVal, ClientExtensions, PublicKeyCredential,
      7         },
      8     },
      9     AttestationObject, AttestedCredentialData, AuthTransports, AuthenticationExtensionsPrfOutputs,
     10     AuthenticatorAttestation, ClientExtensionsOutputs, CredentialPropertiesOutput, FromCbor as _,
     11     Registration, UncompressedPubKey,
     12 };
     13 #[cfg(doc)]
     14 use super::{AuthenticatorAttachment, CredentialId};
     15 use core::{
     16     fmt::{self, Formatter},
     17     marker::PhantomData,
     18     str,
     19 };
     20 use rsa::sha2::{Sha256, digest::OutputSizeUser as _};
     21 use serde::de::{Deserialize, Deserializer, Error, IgnoredAny, MapAccess, Unexpected, Visitor};
     22 /// Functionality for deserializing DER-encoded `SubjectPublicKeyInfo` _without_ making copies of data or
     23 /// verifying the key is valid. This exists purely to ensure that the public key we receive in JSON is the same as
     24 /// the public key in the attestation object.
     25 mod spki {
     26     use super::super::{
     27         Ed25519PubKey, RsaPubKey, RsaPubKeyErr, UncompressedP256PubKey, UncompressedP384PubKey,
     28         UncompressedPubKey,
     29     };
     30     use core::fmt::{self, Display, Formatter};
     31     use p256::{
     32         NistP256,
     33         elliptic_curve::{Curve, generic_array::typenum::type_operators::ToInt as _},
     34     };
     35     use p384::NistP384;
     36     /// Value assigned to the integer type under the universal tag class per
     37     /// [ITU-T X.680](https://www.itu.int/rec/T-REC-X.680-202102-I/en).
     38     const INTEGER: u8 = 2;
     39     /// Value assigned to the bitstring type under the universal tag class per
     40     /// [ITU-T X.680](https://www.itu.int/rec/T-REC-X.680-202102-I/en).
     41     const BITSTRING: u8 = 3;
     42     /// Value assigned to the null type under the universal tag class per
     43     /// [ITU-T X.680](https://www.itu.int/rec/T-REC-X.680-202102-I/en).
     44     const NULL: u8 = 5;
     45     /// Value assigned to the object identifier type under the universal tag class per
     46     /// [ITU-T X.680](https://www.itu.int/rec/T-REC-X.680-202102-I/en).
     47     const OID: u8 = 6;
     48     /// Value assigned to the sequence type under the universal tag class per
     49     /// [ITU-T X.680](https://www.itu.int/rec/T-REC-X.680-202102-I/en).
     50     const SEQUENCE: u8 = 16;
     51     /// Value assigned to a constructed [`SEQUENCE`] per
     52     /// [ITU-T X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en).
     53     ///
     54     /// All sequences are constructed once encoded, so this will likely always be used instead of
     55     /// `SEQUENCE`.
     56     const CONSTRUCTED_SEQUENCE: u8 = SEQUENCE | 0b0010_0000;
     57     /// Length of the header before the compressed y-coordinate in a DER-encoded ASN.1 `SubjectPublicKeyInfo`
     58     /// for an Ed25519 public key.
     59     const ED25519_HEADER_LEN: usize = 12;
     60     /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for Ed25519 public key.
     61     const ED25519_LEN: usize = ED25519_HEADER_LEN + ed25519_dalek::PUBLIC_KEY_LENGTH;
     62     /// `ED25519_LEN` as a `u8`.
     63     // `44 as u8` is clearly OK.
     64     #[expect(
     65         clippy::as_conversions,
     66         clippy::cast_possible_truncation,
     67         reason = "comments above justify their correctness"
     68     )]
     69     const ED25519_LEN_U8: u8 = ED25519_LEN as u8;
     70     /// Length of the header before the uncompressed SEC- 1 pubic key in a DER-encoded ASN.1 `SubjectPublicKeyInfo`
     71     /// for an uncompressed ECDSA public key based on secp256r1/P-256.
     72     const P256_HEADER_LEN: usize = 27;
     73     /// Number of bytes the x-coordinate takes in an uncompressed P-256 public key.
     74     const P256_X_LEN: usize = <NistP256 as Curve>::FieldBytesSize::INT;
     75     /// Number of bytes the y-coordinate takes in an uncompressed P-256 public key.
     76     const P256_Y_LEN: usize = <NistP256 as Curve>::FieldBytesSize::INT;
     77     /// Number of bytes the x and y coordinates take in an uncompressed P-256 public key when concatenated together.
     78     const P256_COORD_LEN: usize = P256_X_LEN + P256_Y_LEN;
     79     /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for an uncompressed SEC-1 ECDSA public key
     80     /// based on secp256r1/P-256.
     81     const P256_LEN: usize = P256_HEADER_LEN + P256_COORD_LEN;
     82     /// `P256_LEN` as a `u8`.
     83     // `91 as u8` is clearly OK.
     84     #[expect(
     85         clippy::as_conversions,
     86         clippy::cast_possible_truncation,
     87         reason = "comments above justify their correctness"
     88     )]
     89     const P256_LEN_U8: u8 = P256_LEN as u8;
     90     /// Length of the header before the uncompressed SEC- 1 pubic key in a DER-encoded ASN.1 `SubjectPublicKeyInfo`
     91     /// for an uncompressed ECDSA public key based on secp384r1/P-384.
     92     const P384_HEADER_LEN: usize = 24;
     93     /// Number of bytes the x-coordinate takes in an uncompressed P-384 public key.
     94     const P384_X_LEN: usize = <NistP384 as Curve>::FieldBytesSize::INT;
     95     /// Number of bytes the y-coordinate takes in an uncompressed P-384 public key.
     96     const P384_Y_LEN: usize = <NistP384 as Curve>::FieldBytesSize::INT;
     97     /// Number of bytes the x and y coordinates take in an uncompressed P-384 public key when concatenated together.
     98     const P384_COORD_LEN: usize = P384_X_LEN + P384_Y_LEN;
     99     /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for an uncompressed SEC-1 ECDSA public key
    100     /// based on secp384r1/P-384.
    101     const P384_LEN: usize = P384_HEADER_LEN + P384_COORD_LEN;
    102     /// `P384_LEN` as a `u8`.
    103     // `120 as u8` is clearly OK.
    104     #[expect(
    105         clippy::as_conversions,
    106         clippy::cast_possible_truncation,
    107         reason = "comments above justify their correctness"
    108     )]
    109     const P384_LEN_U8: u8 = P384_LEN as u8;
    110     /// Error returned from [`SubjectPublicKeyInfo::from_der`].
    111     pub(super) enum SubjectPublicKeyInfoErr {
    112         /// The DER-encoded `SubjectPublicKeyInfo` had an invalid length.
    113         Len,
    114         /// The length of the DER-encoded Ed25519 key was invalid.
    115         Ed25519Len,
    116         /// The header of the DER-encoded Ed25519 key was invalid.
    117         Ed25519Header,
    118         /// The length of the DER-encoded P-256 key was invalid.
    119         P256Len,
    120         /// The header of the DER-encoded P-256 key was invalid.
    121         P256Header,
    122         /// The length of the DER-encoded P-384 key was invalid.
    123         P384Len,
    124         /// The header of the DER-encoded P-384 key was invalid.
    125         P384Header,
    126         /// The length of the DER-encoded RSA key was invalid.
    127         RsaLen,
    128         /// The DER-encoding of the RSA key was invalid.
    129         RsaEncoding,
    130         /// The exponent of the DER-encoded RSA key was too large.
    131         RsaExponentTooLarge,
    132         /// The DER-encoded RSA key was not a valid [`RsaPubKey`].
    133         RsaPubKey(RsaPubKeyErr),
    134     }
    135     impl Display for SubjectPublicKeyInfoErr {
    136         fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    137             match *self {
    138                 Self::Len => {
    139                     f.write_str("the DER-encoded SubjectPublicKeyInfo had an invalid length")
    140                 }
    141                 Self::Ed25519Len => {
    142                     f.write_str("length of the DER-encoded Ed25519 key was invalid")
    143                 }
    144                 Self::Ed25519Header => {
    145                     f.write_str("header of the DER-encoded Ed25519 key was invalid")
    146                 }
    147                 Self::P256Len => f.write_str("length of the DER-encoded P-256 key was invalid"),
    148                 Self::P256Header => f.write_str("header of the DER-encoded P-256 key was invalid"),
    149                 Self::P384Len => f.write_str("length of the DER-encoded P-384 key was invalid"),
    150                 Self::P384Header => f.write_str("header of the DER-encoded P-384 key was invalid"),
    151                 Self::RsaLen => f.write_str("length of the DER-encoded RSA key was invalid"),
    152                 Self::RsaEncoding => {
    153                     f.write_str("the DER-encoding of the RSA public key was invalid")
    154                 }
    155                 Self::RsaExponentTooLarge => {
    156                     f.write_str("the DER-encoded RSA public key had an exponent that was too large")
    157                 }
    158                 Self::RsaPubKey(err) => {
    159                     write!(f, "the DER-encoded RSA public was not valid: {err}")
    160                 }
    161             }
    162         }
    163     }
    164     /// Types that can be deserialized from the DER-encoded ASN.1 `SubjectPublicKeyInfo` as defined in
    165     /// [RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280#appendix-A.1) and other applicable RFCs
    166     /// and documents (e.g., [ITU-T X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en)).
    167     pub(super) trait SubjectPublicKeyInfo<'a>: Sized {
    168         /// Transforms the DER-encoded ASN.1 `SubjectPublicKeyInfo` into `Self`.
    169         ///
    170         /// # Errors
    171         ///
    172         /// Errors iff `der` does not conform.
    173         #[expect(single_use_lifetimes, reason = "false positive")]
    174         fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr>;
    175     }
    176     impl<'a> SubjectPublicKeyInfo<'a> for Ed25519PubKey<&'a [u8]> {
    177         #[expect(single_use_lifetimes, reason = "false positive")]
    178         fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> {
    179             /// ```asn
    180             /// SubjectPublicKeyInfo ::= SEQUENCE {
    181             ///     algorithm           AlgorithmIdentifier,
    182             ///     subjectPublicKey    BIT STRING
    183             /// }
    184             ///
    185             /// AlgorithmIdentifier ::= SEQUENCE {
    186             ///     algorithm     OBJECT IDENTIFIER,
    187             ///     parameters    ANY DEFINED BY algorithm OPTIONAL
    188             /// }
    189             /// ```
    190             ///
    191             /// [RFC 8410](https://www.rfc-editor.org/rfc/rfc8410#section-3) requires parameters to not exist
    192             /// in `AlgorithmIdentifier`.
    193             ///
    194             /// RFC 8410 defines the OID as 1.3.101.112 which is encoded as 43.101.112
    195             /// per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en).
    196             ///
    197             /// RFC 8410 defines the bitstring as a reinterpretation of the byte string.
    198             const HEADER: [u8; ED25519_HEADER_LEN] = [
    199                 CONSTRUCTED_SEQUENCE,
    200                 // `ED25519_LEN_U8` is the length of the entire payload; thus we subtract
    201                 // the "consumed" length.
    202                 ED25519_LEN_U8 - 2,
    203                 CONSTRUCTED_SEQUENCE,
    204                 // AlgorithmIdentifier only contains the OID; thus it's length is 5.
    205                 3 + 2,
    206                 OID,
    207                 3,
    208                 43,
    209                 101,
    210                 112,
    211                 BITSTRING,
    212                 // `ED25519_LEN_U8` is the length of the entire payload; thus we subtract
    213                 // the "consumed" length.
    214                 ED25519_LEN_U8 - 11,
    215                 // The number of unused bits.
    216                 0,
    217             ];
    218             der.split_at_checked(HEADER.len())
    219                 .ok_or(SubjectPublicKeyInfoErr::Ed25519Len)
    220                 .and_then(|(header, rem)| {
    221                     if header == HEADER {
    222                         if rem.len() == ed25519_dalek::PUBLIC_KEY_LENGTH {
    223                             Ok(Self(rem))
    224                         } else {
    225                             Err(SubjectPublicKeyInfoErr::Ed25519Len)
    226                         }
    227                     } else {
    228                         Err(SubjectPublicKeyInfoErr::Ed25519Header)
    229                     }
    230                 })
    231         }
    232     }
    233     impl<'a> SubjectPublicKeyInfo<'a> for UncompressedP256PubKey<'a> {
    234         #[expect(single_use_lifetimes, reason = "false positive")]
    235         fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> {
    236             // ```asn
    237             // SubjectPublicKeyInfo ::= SEQUENCE {
    238             //     algorithm           AlgorithmIdentifier,
    239             //     subjectPublicKey    BIT STRING
    240             // }
    241             //
    242             // AlgorithmIdentifier ::= SEQUENCE {
    243             //     algorithm     OBJECT IDENTIFIER,
    244             //     parameters    ANY DEFINED BY algorithm OPTIONAL
    245             // }
    246             //
    247             // ECParameters ::= CHOICE {
    248             //     namedCurve         OBJECT IDENTIFIER
    249             //     -- implicitCurve   NULL
    250             //     -- specifiedCurve  SpecifiedECDomain
    251             // }
    252             // ```
    253             // [RFC 5480](https://www.rfc-editor.org/rfc/rfc5480#section-2.1.1) requires parameters to exist and
    254             // be of the `ECParameters` form with the requirement that `namedCurve` is chosen.
    255             //
    256             // RFC 5480 defines the OID for id-ecPublicKey as 1.2.840.10045.2.1 and the OID for the namedCurve
    257             // secp256r1 as 1.2.840.10045.3.1.7. The former OID is encoded as 42.134.72.206.61.2.1 and the latter
    258             // is encoded as 42.134.72.206.61.3.1.7 per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en).
    259             //
    260             // [RFC 5480](https://www.rfc-editor.org/rfc/rfc5480#section-5) only requires support for the
    261             // uncompressed form and only states that the compressed form MAY be supported. In practice this means
    262             // DER-encoded payloads almost always are of the uncompressed form for compatibility reasons. This in
    263             // conjunction with the fact the COSE key is required to be in the uncompressed form means we only support
    264             // DER-encoded payloads containing uncompressed keys.
    265             //
    266             // [SEC 1](https://secg.org/sec1-v2.pdf) defines the point as an octet string, and the conversion
    267             // to a bitstring simply requires reinterpreting the octet string as a bitstring.
    268 
    269             /// Header of the DER-encoded payload before the public key.
    270             const HEADER: [u8; P256_HEADER_LEN] = [
    271                 CONSTRUCTED_SEQUENCE,
    272                 // `P256_LEN_U8` is the length of the entire payload; thus we subtract
    273                 // the "consumed" length.
    274                 P256_LEN_U8 - 2,
    275                 CONSTRUCTED_SEQUENCE,
    276                 7 + 2 + 8 + 2,
    277                 OID,
    278                 7,
    279                 42,
    280                 134,
    281                 72,
    282                 206,
    283                 61,
    284                 2,
    285                 1,
    286                 OID,
    287                 8,
    288                 42,
    289                 134,
    290                 72,
    291                 206,
    292                 61,
    293                 3,
    294                 1,
    295                 7,
    296                 BITSTRING,
    297                 // `P256_LEN_U8` is the length of the entire payload; thus we subtract
    298                 // the "consumed" length.
    299                 P256_LEN_U8 - 25,
    300                 // The number of unused bits.
    301                 0,
    302                 // SEC-1 tag for an uncompressed key.
    303                 4,
    304             ];
    305             der.split_at_checked(HEADER.len())
    306                 .ok_or(SubjectPublicKeyInfoErr::P256Len)
    307                 .and_then(|(header, header_rem)| {
    308                     if header == HEADER {
    309                         header_rem
    310                             .split_at_checked(P256_X_LEN)
    311                             .ok_or(SubjectPublicKeyInfoErr::P256Len)
    312                             .and_then(|(x, y)| {
    313                                 if y.len() == P256_Y_LEN {
    314                                     Ok(Self(x, y))
    315                                 } else {
    316                                     Err(SubjectPublicKeyInfoErr::P256Len)
    317                                 }
    318                             })
    319                     } else {
    320                         Err(SubjectPublicKeyInfoErr::P256Header)
    321                     }
    322                 })
    323         }
    324     }
    325     impl<'a> SubjectPublicKeyInfo<'a> for UncompressedP384PubKey<'a> {
    326         #[expect(single_use_lifetimes, reason = "false positive")]
    327         fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> {
    328             // ```asn
    329             // SubjectPublicKeyInfo ::= SEQUENCE {
    330             //     algorithm           AlgorithmIdentifier,
    331             //     subjectPublicKey    BIT STRING
    332             // }
    333             //
    334             // AlgorithmIdentifier ::= SEQUENCE {
    335             //     algorithm     OBJECT IDENTIFIER,
    336             //     parameters    ANY DEFINED BY algorithm OPTIONAL
    337             // }
    338             //
    339             // ECParameters ::= CHOICE {
    340             //     namedCurve         OBJECT IDENTIFIER
    341             //     -- implicitCurve   NULL
    342             //     -- specifiedCurve  SpecifiedECDomain
    343             // }
    344             // ```
    345             // [RFC 5480](https://www.rfc-editor.org/rfc/rfc5480#section-2.1.1) requires parameters to exist and
    346             // be of the `ECParameters` form with the requirement that `namedCurve` is chosen.
    347             //
    348             // RFC 5480 defines the OID for id-ecPublicKey as 1.2.840.10045.2.1 and the OID for the namedCurve
    349             // secp384r1 as 1.3.132.0.34. The former OID is encoded as 42.134.72.206.61.2.1 and the latter
    350             // is encoded as 43.129.4.0.34 per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en).
    351             //
    352             // [RFC 5480](https://www.rfc-editor.org/rfc/rfc5480#section-5) only requires support for the
    353             // uncompressed form and only states that the compressed form MAY be supported. In practice this means
    354             // DER-encoded payloads almost always are of the uncompressed form for compatibility reasons. This in
    355             // conjunction with the fact the COSE key is required to be in the uncompressed form means we only support
    356             // DER-encoded payloads containing uncompressed keys.
    357             //
    358             // [SEC 1](https://secg.org/sec1-v2.pdf) defines the point as an octet string, and the conversion
    359             // to a bitstring simply requires reinterpreting the octet string as a bitstring.
    360 
    361             /// Header of the DER-encoded payload before the public key.
    362             const HEADER: [u8; P384_HEADER_LEN] = [
    363                 CONSTRUCTED_SEQUENCE,
    364                 // `P384_LEN_U8` is the length of the entire payload; thus we subtract
    365                 // the "consumed" length.
    366                 P384_LEN_U8 - 2,
    367                 CONSTRUCTED_SEQUENCE,
    368                 7 + 2 + 5 + 2,
    369                 OID,
    370                 7,
    371                 42,
    372                 134,
    373                 72,
    374                 206,
    375                 61,
    376                 2,
    377                 1,
    378                 OID,
    379                 5,
    380                 43,
    381                 129,
    382                 4,
    383                 0,
    384                 34,
    385                 BITSTRING,
    386                 // `P384_LEN_U8` is the length of the entire payload; thus we subtract
    387                 // the "consumed" length.
    388                 P384_LEN_U8 - 22,
    389                 // The number of unused bits.
    390                 0,
    391                 // SEC-1 tag for an uncompressed key.
    392                 4,
    393             ];
    394             der.split_at_checked(HEADER.len())
    395                 .ok_or(SubjectPublicKeyInfoErr::P384Len)
    396                 .and_then(|(header, header_rem)| {
    397                     if header == HEADER {
    398                         header_rem
    399                             .split_at_checked(P384_X_LEN)
    400                             .ok_or(SubjectPublicKeyInfoErr::P384Len)
    401                             .and_then(|(x, y)| {
    402                                 if y.len() == P384_Y_LEN {
    403                                     Ok(Self(x, y))
    404                                 } else {
    405                                     Err(SubjectPublicKeyInfoErr::P384Len)
    406                                 }
    407                             })
    408                     } else {
    409                         Err(SubjectPublicKeyInfoErr::P384Header)
    410                     }
    411                 })
    412         }
    413     }
    414     impl<'a> SubjectPublicKeyInfo<'a> for RsaPubKey<&'a [u8]> {
    415         #[expect(single_use_lifetimes, reason = "false positive")]
    416         #[expect(
    417             clippy::arithmetic_side_effects,
    418             clippy::big_endian_bytes,
    419             clippy::indexing_slicing,
    420             clippy::missing_asserts_for_indexing,
    421             reason = "comments justify their correctness"
    422         )]
    423         #[expect(
    424             clippy::too_many_lines,
    425             reason = "rsa keys are the only type that would benefit from a modular SubjectPublicKeyInfo (similar to FromCbor). if more types benefit in the future, then this will be done."
    426         )]
    427         fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> {
    428             // ```asn
    429             // SubjectPublicKeyInfo ::= SEQUENCE {
    430             //     algorithm           AlgorithmIdentifier,
    431             //     subjectPublicKey    BIT STRING
    432             // }
    433             //
    434             // AlgorithmIdentifier ::= SEQUENCE {
    435             //     algorithm     OBJECT IDENTIFIER,
    436             //     parameters    ANY DEFINED BY algorithm OPTIONAL
    437             // }
    438             //
    439             // RSAPublicKey ::= SEQUENCE {
    440             //     modulus          INTEGER, -- n
    441             //     publicExponent   INTEGER  --e
    442             // }
    443             //
    444             // pkcs-1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840)
    445             //     rsadsi(113549) pkcs(1) 1
    446             // }
    447             //
    448             // rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1}
    449             // ```
    450             // [RFC 3279](https://www.rfc-editor.org/rfc/rfc3279#section-2.3.1) requires parameters to exist and
    451             // be null.
    452             //
    453             // RFC 3279 defines the OID for rsaEncryption as 1.2.840.113549.1.1.1 which is encoded as
    454             // 42.134.72.134.247.13.1.1.1 per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en).
    455             //
    456             // Note we only allow moduli that are 256 to 2048 bytes in length inclusively. Additionally
    457             // we only allow `u32` exponents; consequently all lengths that include the modulus will always be
    458             // encoded with two bytes.
    459 
    460             /// `AlgorithmIdentifier` header including the `BITSTRING` and number of bytes the length
    461             /// is encoded in of the `BITSTRING` type of `subjectPublicKey`
    462             /// (130-128 = 2 bytes to encode the length).
    463             const ALG_OID_HEADER: [u8; 17] = [
    464                 CONSTRUCTED_SEQUENCE,
    465                 9 + 2 + 2,
    466                 OID,
    467                 9,
    468                 42,
    469                 134,
    470                 72,
    471                 134,
    472                 247,
    473                 13,
    474                 1,
    475                 1,
    476                 1,
    477                 NULL,
    478                 0,
    479                 BITSTRING,
    480                 130,
    481             ];
    482             /// `CONSTRUCTED_SEQUENCE` whose length is encoded in two bytes.
    483             const SEQ_LONG: [u8; 2] = [CONSTRUCTED_SEQUENCE, 130];
    484             /// `INTEGER` whose length is encoded in two bytes.
    485             const INT_LONG: [u8; 2] = [INTEGER, 130];
    486             der.split_at_checked(SEQ_LONG.len())
    487                 .ok_or(SubjectPublicKeyInfoErr::RsaLen)
    488                 .and_then(|(seq, seq_rem)| {
    489                     if seq == SEQ_LONG {
    490                         seq_rem
    491                             .split_at_checked(2)
    492                             .ok_or(SubjectPublicKeyInfoErr::RsaLen)
    493                             .and_then(|(seq_len, seq_len_rem)| {
    494                                 let mut len = [0; 2];
    495                                 len.copy_from_slice(seq_len);
    496                                 let rem_len = usize::from(u16::from_be_bytes(len));
    497                                 if rem_len == seq_len_rem.len() {
    498                                     if rem_len > 255 {
    499                                         // We can safely split here since we know `seq_len_rem` is at least
    500                                         // 256 which is greater than `ALG_OID_HEADER.len()`.
    501                                         let (a_oid, a_oid_rem) = seq_len_rem.split_at(ALG_OID_HEADER.len());
    502                                         if a_oid == ALG_OID_HEADER {
    503                                             // `a_oid_rem.len()` is at least 239, so splitting is fine.
    504                                             let (bit_str_len_enc, bit_str_val) = a_oid_rem.split_at(2);
    505                                             let mut bit_string_len = [0; 2];
    506                                             bit_string_len.copy_from_slice(bit_str_len_enc);
    507                                             let bit_str_val_len = usize::from(u16::from_be_bytes(bit_string_len));
    508                                             if bit_str_val_len == bit_str_val.len() {
    509                                                 if bit_str_val_len > 255 {
    510                                                     // `bit_str_val.len() > 255`, so splitting is fine.
    511                                                     let (unused_bits, bits_rem) = bit_str_val.split_at(1);
    512                                                     if unused_bits == [0] {
    513                                                         // We can safely split here since we know `bits_rem.len()` is at least
    514                                                         // 255.
    515                                                         let (rsa_seq, rsa_seq_rem) = bits_rem.split_at(SEQ_LONG.len());
    516                                                         if rsa_seq == SEQ_LONG {
    517                                                             // `rsa_seq_rem.len()` is at least 253, so splitting is fine.
    518                                                             let (rsa_seq_len_enc, rsa_seq_len_enc_rem) = rsa_seq_rem.split_at(2);
    519                                                             let mut rsa_seq_len = [0; 2];
    520                                                             rsa_seq_len.copy_from_slice(rsa_seq_len_enc);
    521                                                             let rsa_key_info_len = usize::from(u16::from_be_bytes(rsa_seq_len));
    522                                                             if rsa_key_info_len == rsa_seq_len_enc_rem.len() {
    523                                                                 if rsa_key_info_len > 255 {
    524                                                                     // We can safely split here since we know `rsa_seq_len_enc_rem.len()`
    525                                                                     // is at least 256.
    526                                                                     let (n_meta, n_meta_rem) = rsa_seq_len_enc_rem.split_at(INT_LONG.len());
    527                                                                     if n_meta == INT_LONG {
    528                                                                         // `n_meta_rem.len()` is at least 254, so splitting is fine.
    529                                                                         let (n_len_enc, n_len_enc_rem) = n_meta_rem.split_at(2);
    530                                                                         let mut n_len = [0; 2];
    531                                                                         n_len.copy_from_slice(n_len_enc);
    532                                                                         let mod_len = usize::from(u16::from_be_bytes(n_len));
    533                                                                         if mod_len > 255 {
    534                                                                             n_len_enc_rem.split_at_checked(mod_len).ok_or(SubjectPublicKeyInfoErr::RsaLen).and_then(|(mut n, n_rem)| {
    535                                                                                 // `n.len() > 255`, so indexing is fine.
    536                                                                                 let n_first = n[0];
    537                                                                                 // DER integers are signed; thus the most significant bit must be 0.
    538                                                                                 // DER integers are minimally encoded; thus when a leading 0 exists,
    539                                                                                 // the second byte must be at least 128.
    540                                                                                 // `n.len() > 255`, so indexing is fine.
    541                                                                                 if n_first < 128 && (n_first != 0 || n[1] > 127) {
    542                                                                                     if n_first == 0 {
    543                                                                                         // `n.len() > 255`, so indexing is fine.
    544                                                                                         // We must remove the leading 0.
    545                                                                                         n = &n[1..];
    546                                                                                     }
    547                                                                                     n_rem.split_first().ok_or(SubjectPublicKeyInfoErr::RsaLen).and_then(|(e_type, e_type_rem)| {
    548                                                                                         if *e_type == INTEGER {
    549                                                                                             e_type_rem.split_first().ok_or(SubjectPublicKeyInfoErr::RsaLen).and_then(|(e_len, e_len_rem)| {
    550                                                                                                 let e_len_usize = usize::from(*e_len);
    551                                                                                                 if e_len_usize == e_len_rem.len() {
    552                                                                                                     e_len_rem.first().ok_or(SubjectPublicKeyInfoErr::RsaLen).and_then(|&e_first| {
    553                                                                                                         // DER integers are signed; thus the most significant bit must be 0.
    554                                                                                                         if e_first < 128 {
    555                                                                                                             // `RsaPubKey` only allows `u32` exponents, which means we only care
    556                                                                                                             // about lengths up to 5.
    557                                                                                                             match e_len_usize {
    558                                                                                                                 1 => Ok(u32::from(e_first)),
    559                                                                                                                 2..=5 => if e_first == 0 {
    560                                                                                                                     // DER integers are minimally encoded; thus when a leading
    561                                                                                                                     // 0 exists, the second byte must be at least 128.
    562                                                                                                                     // We know the length is at least 2; thus this won't `panic`.
    563                                                                                                                     if e_len_rem[1] > 127 {
    564                                                                                                                         let mut e = [0; 4];
    565                                                                                                                         if e_len_usize == 5 {
    566                                                                                                                             // We know the length is at least 2; thus this won't `panic`.
    567                                                                                                                             e.copy_from_slice(&e_len_rem[1..]);
    568                                                                                                                         } else {
    569                                                                                                                             // `e.len() == 4` and `e_len_usize` is at most 4; thus underflow
    570                                                                                                                             // won't occur nor will indexing `panic`. `e` is big-endian,
    571                                                                                                                             // so we start from the right.
    572                                                                                                                             e[4 - e_len_usize..].copy_from_slice(e_len_rem);
    573                                                                                                                         }
    574                                                                                                                         Ok(u32::from_be_bytes(e))
    575                                                                                                                     } else {
    576                                                                                                                         Err(SubjectPublicKeyInfoErr::RsaEncoding)
    577                                                                                                                     }
    578                                                                                                                 } else if e_len_usize == 5 {
    579                                                                                                                     // 5 bytes are only possible for `INTEGER`s that
    580                                                                                                                     // are greater than `i32::MAX`, which will be encoded
    581                                                                                                                     // with a leading 0.
    582                                                                                                                     Err(SubjectPublicKeyInfoErr::RsaEncoding)
    583                                                                                                                 } else {
    584                                                                                                                     let mut e = [0; 4];
    585                                                                                                                     // `e.len() == 4` and `e_len_usize` is at most 4; thus underflow
    586                                                                                                                     // won't occur nor will indexing `panic`. `e` is big-endian,
    587                                                                                                                     // so we start from the right.
    588                                                                                                                     e[4 - e_len_usize..].copy_from_slice(e_len_rem);
    589                                                                                                                     Ok(u32::from_be_bytes(e))
    590                                                                                                                 },
    591                                                                                                                 _ => Err(SubjectPublicKeyInfoErr::RsaExponentTooLarge),
    592                                                                                                             }.and_then(|e| Self::try_from((n, e)).map_err(SubjectPublicKeyInfoErr::RsaPubKey))
    593                                                                                                         } else {
    594                                                                                                             Err(SubjectPublicKeyInfoErr::RsaEncoding)
    595                                                                                                         }
    596                                                                                                     })
    597                                                                                                 } else {
    598                                                                                                     Err(SubjectPublicKeyInfoErr::RsaLen)
    599                                                                                                 }
    600                                                                                             })
    601                                                                                         } else {
    602                                                                                             Err(SubjectPublicKeyInfoErr::RsaEncoding)
    603                                                                                         }
    604                                                                                     })
    605                                                                                 } else {
    606                                                                                     Err(SubjectPublicKeyInfoErr::RsaEncoding)
    607                                                                                 }
    608                                                                             })
    609                                                                         } else {
    610                                                                             Err(SubjectPublicKeyInfoErr::RsaEncoding)
    611                                                                         }
    612                                                                     } else {
    613                                                                         Err(SubjectPublicKeyInfoErr::RsaEncoding)
    614                                                                     }
    615                                                                 } else {
    616                                                                     Err(SubjectPublicKeyInfoErr::RsaEncoding)
    617                                                                 }
    618                                                             } else {
    619                                                                 Err(SubjectPublicKeyInfoErr::RsaLen)
    620                                                             }
    621                                                         } else {
    622                                                             Err(SubjectPublicKeyInfoErr::RsaEncoding)
    623                                                         }
    624                                                     } else {
    625                                                         Err(SubjectPublicKeyInfoErr::RsaEncoding)
    626                                                     }
    627                                                 } else {
    628                                                     Err(SubjectPublicKeyInfoErr::RsaEncoding)
    629                                                 }
    630                                             } else {
    631                                                 Err(SubjectPublicKeyInfoErr::RsaLen)
    632                                             }
    633                                         } else {
    634                                             Err(SubjectPublicKeyInfoErr::RsaEncoding)
    635                                         }
    636                                     } else {
    637                                         Err(SubjectPublicKeyInfoErr::RsaEncoding)
    638                                     }
    639                                 } else {
    640                                     Err(SubjectPublicKeyInfoErr::RsaLen)
    641                                 }
    642                             })
    643                     } else {
    644                         Err(SubjectPublicKeyInfoErr::RsaEncoding)
    645                     }
    646                 })
    647         }
    648     }
    649     impl<'a> SubjectPublicKeyInfo<'a> for UncompressedPubKey<'a> {
    650         #[expect(single_use_lifetimes, reason = "false positive")]
    651         fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> {
    652             // The lengths of the three key types do not overlap.
    653             match der.len() {
    654                 // The minimum modulus we support for RSA is 2048 bits which is 256 bytes;
    655                 // thus clearly its encoding will be at least 256 which is greater than
    656                 // all of the other values.
    657                 ED25519_LEN => Ed25519PubKey::from_der(der).map(Self::Ed25519),
    658                 P256_LEN => UncompressedP256PubKey::from_der(der).map(Self::P256),
    659                 P384_LEN => UncompressedP384PubKey::from_der(der).map(Self::P384),
    660                 256.. => RsaPubKey::from_der(der).map(Self::Rsa),
    661                 _ => Err(SubjectPublicKeyInfoErr::Len),
    662             }
    663         }
    664     }
    665 }
    666 /// Helper type returned from [`AuthenticatorAttestationVisitor::visit_map`].
    667 ///
    668 /// The purpose of this type is to hopefully avoid re-parsing the raw attestation object multiple times. In
    669 /// particular [`Registration`] and [`super::ser_relaxed::RegistrationRelaxed`] will attempt to validate `id` is the
    670 /// same as the [`CredentialId`] within the attestation object.
    671 pub(super) struct AuthAttest {
    672     /// The data we care about.
    673     pub attest: AuthenticatorAttestation,
    674     /// [`CredentialId`] information. This is `None` iff `authenticatorData`, `publicKey`, and
    675     /// `publicKeyAlgorithm` do not exist and we are performing a `RELAXED` parsing. When `Some`, the first
    676     /// `usize` is the starting index of `CredentialId` within the attestation object; and the second `usize` is
    677     /// 1 past the last index of `CredentialId`.
    678     pub cred_info: Option<(usize, usize)>,
    679 }
    680 /// Fields in `AuthenticatorAttestationResponseJSON`.
    681 enum AttestField<const IGNORE_UNKNOWN: bool> {
    682     /// `clientDataJSON`.
    683     ClientDataJson,
    684     /// `attestationObject`.
    685     AttestationObject,
    686     /// `authenticatorData`.
    687     AuthenticatorData,
    688     /// `transports`.
    689     Transports,
    690     /// `publicKey`.
    691     PublicKey,
    692     /// `publicKeyAlgorithm`.
    693     PublicKeyAlgorithm,
    694     /// Unknown fields.
    695     Other,
    696 }
    697 impl<'e, const I: bool> Deserialize<'e> for AttestField<I> {
    698     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    699     where
    700         D: Deserializer<'e>,
    701     {
    702         /// `Visitor` for `AttestField`.
    703         struct AttestFieldVisitor<const IGNORE_UNKNOWN: bool>;
    704         impl<const IG: bool> Visitor<'_> for AttestFieldVisitor<IG> {
    705             type Value = AttestField<IG>;
    706             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    707                 write!(
    708                     formatter,
    709                     "'{CLIENT_DATA_JSON}', '{ATTESTATION_OBJECT}', '{AUTHENTICATOR_DATA}', '{TRANSPORTS}', '{PUBLIC_KEY}', or '{PUBLIC_KEY_ALGORITHM}'"
    710                 )
    711             }
    712             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    713             where
    714                 E: Error,
    715             {
    716                 match v {
    717                     CLIENT_DATA_JSON => Ok(AttestField::ClientDataJson),
    718                     ATTESTATION_OBJECT => Ok(AttestField::AttestationObject),
    719                     AUTHENTICATOR_DATA => Ok(AttestField::AuthenticatorData),
    720                     TRANSPORTS => Ok(AttestField::Transports),
    721                     PUBLIC_KEY => Ok(AttestField::PublicKey),
    722                     PUBLIC_KEY_ALGORITHM => Ok(AttestField::PublicKeyAlgorithm),
    723                     _ => {
    724                         if IG {
    725                             Ok(AttestField::Other)
    726                         } else {
    727                             Err(E::unknown_field(v, AUTH_ATTEST_FIELDS))
    728                         }
    729                     }
    730                 }
    731             }
    732         }
    733         deserializer.deserialize_identifier(AttestFieldVisitor::<I>)
    734     }
    735 }
    736 /// Attestation object. We use this instead of `Base64DecodedVal` since we want to manually
    737 /// allocate the `Vec` in order to avoid re-allocation. Internally `AuthenticatorAttestation::new`
    738 /// appends the SHA-256 hash to the passed attestation object `Vec` to avoid temporarily allocating
    739 /// a `Vec` that contains the attestation object and hash for signature verification. Calling code
    740 /// can avoid any reallocation that would occur when the capacity is not large enough by ensuring the
    741 /// passed `Vec` has at least 32 bytes of available capacity.
    742 pub(super) struct AttObj(pub Vec<u8>);
    743 impl<'e> Deserialize<'e> for AttObj {
    744     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    745     where
    746         D: Deserializer<'e>,
    747     {
    748         /// `Visitor` for `AttObj`.
    749         struct AttObjVisitor;
    750         impl Visitor<'_> for AttObjVisitor {
    751             type Value = AttObj;
    752             fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    753                 formatter.write_str("base64url-encoded attestation object")
    754             }
    755             #[expect(
    756                 clippy::arithmetic_side_effects,
    757                 reason = "comment justifies their correctness"
    758             )]
    759             fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    760             where
    761                 E: Error,
    762             {
    763                 base64url_nopad::decode_len(v.len())
    764                     .ok_or_else(|| E::invalid_value(Unexpected::Str(v), &"base64url-encoded value"))
    765                     .and_then(|len| {
    766                         // The decoded length is 3/4 of the encoded length, so overflow could only occur
    767                         // if usize::MAX / 4 < 32 => usize::MAX < 128 < u8::MAX; thus overflow is not
    768                         // possible. We add 32 since the SHA-256 hash of `clientDataJSON` will be added to
    769                         // the raw attestation object by `AuthenticatorAttestation::new`.
    770                         let mut att_obj = vec![0; len + Sha256::output_size()];
    771                         att_obj.truncate(len);
    772                         base64url_nopad::decode_buffer_exact(v.as_bytes(), &mut att_obj)
    773                             .map_err(E::custom)
    774                             .map(|()| AttObj(att_obj))
    775                     })
    776             }
    777         }
    778         deserializer.deserialize_str(AttObjVisitor)
    779     }
    780 }
    781 /// `Visitor` for `AuthenticatorAttestation`.
    782 ///
    783 /// Unknown fields are ignored and only `clientDataJSON` and `attestationObject` are required iff `RELAXED`.
    784 pub(super) struct AuthenticatorAttestationVisitor<const RELAXED: bool>;
    785 impl<'d, const R: bool> Visitor<'d> for AuthenticatorAttestationVisitor<R> {
    786     type Value = AuthAttest;
    787     fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
    788         formatter.write_str("AuthenticatorAttestation")
    789     }
    790     #[expect(clippy::too_many_lines, reason = "find it easier to reason about")]
    791     #[expect(
    792         clippy::arithmetic_side_effects,
    793         clippy::indexing_slicing,
    794         reason = "comments justify their correctness"
    795     )]
    796     fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
    797     where
    798         A: MapAccess<'d>,
    799     {
    800         use spki::SubjectPublicKeyInfo as _;
    801         let mut client_data = None;
    802         let mut attest = None;
    803         let mut auth = None;
    804         let mut pub_key = None;
    805         let mut key_alg = None;
    806         let mut trans = None;
    807         while let Some(key) = map.next_key::<AttestField<R>>()? {
    808             match key {
    809                 AttestField::ClientDataJson => {
    810                     if client_data.is_some() {
    811                         return Err(Error::duplicate_field(CLIENT_DATA_JSON));
    812                     }
    813                     client_data = map
    814                         .next_value::<Base64DecodedVal>()
    815                         .map(|c_data| Some(c_data.0))?;
    816                 }
    817                 AttestField::AttestationObject => {
    818                     if attest.is_some() {
    819                         return Err(Error::duplicate_field(ATTESTATION_OBJECT));
    820                     }
    821                     attest = map.next_value::<AttObj>().map(|att_obj| Some(att_obj.0))?;
    822                 }
    823                 AttestField::AuthenticatorData => {
    824                     if auth.is_some() {
    825                         return Err(Error::duplicate_field(AUTHENTICATOR_DATA));
    826                     }
    827                     auth = map.next_value::<Option<Base64DecodedVal>>().map(Some)?;
    828                 }
    829                 AttestField::Transports => {
    830                     if trans.is_some() {
    831                         return Err(Error::duplicate_field(TRANSPORTS));
    832                     }
    833                     trans = map.next_value::<Option<_>>().map(Some)?;
    834                 }
    835                 AttestField::PublicKey => {
    836                     if pub_key.is_some() {
    837                         return Err(Error::duplicate_field(PUBLIC_KEY));
    838                     }
    839                     pub_key = map.next_value::<Option<Base64DecodedVal>>().map(Some)?;
    840                 }
    841                 AttestField::PublicKeyAlgorithm => {
    842                     if key_alg.is_some() {
    843                         return Err(Error::duplicate_field(PUBLIC_KEY_ALGORITHM));
    844                     }
    845                     key_alg = map
    846                         .next_value::<Option<CoseAlgorithmIdentifier>>()
    847                         .map(Some)?;
    848                 }
    849                 AttestField::Other => map.next_value::<IgnoredAny>().map(|_| ())?,
    850             }
    851         }
    852         // Note the order of this matters from a performance perspective. In particular `auth` must be evaluated
    853         // before `pub_key` which must be evaluated before `key_alg` as this allows us to parse the attestation
    854         // object at most once and allow us to prioritize parsing `authenticatorData` over the attestation object.
    855         client_data.ok_or_else(|| Error::missing_field(CLIENT_DATA_JSON)).and_then(|client_data_json| attest.ok_or_else(|| Error::missing_field(ATTESTATION_OBJECT)).and_then(|attestation_object| {
    856             trans.ok_or(false).and_then(|opt_trans| opt_trans.ok_or(true)).or_else(
    857                 |flag| {
    858                     if R {
    859                         Ok(AuthTransports::new())
    860                     } else if flag {
    861                         Err(Error::invalid_type(Unexpected::Other("null"), &format!("{TRANSPORTS} to be a sequence of AuthenticatorTransports").as_str()))
    862                     } else {
    863                         Err(Error::missing_field(TRANSPORTS))
    864                     }
    865                 },
    866             ).and_then(|transports| {
    867                 auth.ok_or(false).and_then(|opt_auth| opt_auth.ok_or(true)).as_ref().map_or_else(
    868                     |flag| {
    869                         if R {
    870                             Ok(None)
    871                         } else if *flag {
    872                             Err(Error::invalid_type(Unexpected::Other("null"), &format!("{AUTHENTICATOR_DATA} to be a base64url-encoded AuthenticatorData").as_str()))
    873                         } else {
    874                             Err(Error::missing_field(AUTHENTICATOR_DATA))
    875                         }
    876                     },
    877                     |a_data| {
    878                         if a_data.0.len() > 37 {
    879                             // The last portion of attestation object is always authenticator data.
    880                             attestation_object.len().checked_sub(a_data.0.len()).ok_or_else(|| Error::invalid_value(Unexpected::Bytes(a_data.0.as_slice()), &format!("authenticator data to match the authenticator data portion of attestation object: {attestation_object:?}").as_str())).and_then(|idx| {
    881                                 // Indexing is fine; otherwise the above check would have returned `None`.
    882                                 if *a_data.0 == attestation_object[idx..] {
    883                                     // We know `a_data.len() > 37`; thus indexing is fine.
    884                                     // We start at 37 since that is the beginning of `attestedCredentialData`.
    885                                     // Recall the first 32 bytes are `rpIdHash`, then a 1 byte `flags`, then a
    886                                     // 4-byte big-endian integer `signCount`.
    887                                     // The starting index of `credentialId` is 18 within `attestedCredentialData`.
    888                                     // Recall the first 16 bytes are `aaguid`, then a 2-byte big-endian integer
    889                                     // `credentialIdLength`. Consequently the starting index within
    890                                     // `attestation_object` is `idx + 37 + 18` = `idx + 55`. Overflow cannot occur
    891                                     // since we successfully parsed `AttestedCredentialData`.
    892                                     AttestedCredentialData::from_cbor(&a_data.0[37..]).map_err(Error::custom).map(|success| Some((success.value, idx + 55)))
    893                                 } else {
    894                                     Err(Error::invalid_value(Unexpected::Bytes(a_data.0.as_slice()), &format!("authenticator data to match the authenticator data portion of attestation object: {:?}", &attestation_object[idx..]).as_str()))
    895                                 }
    896                             })
    897                         } else {
    898                             Err(Error::invalid_value(Unexpected::Bytes(a_data.0.as_slice()), &"authenticator data to be long enough to contain attested credential data"))
    899                         }
    900                     }
    901                 ).and_then(|attested_info| {
    902                     pub_key.ok_or(false).and_then(|opt_key| opt_key.ok_or(true)).map_or_else(
    903                         |flag| {
    904                             if R {
    905                                 attested_info.as_ref().map_or(Ok(None), |&(ref attested_data, cred_id_start)| Ok(Some((match attested_data.credential_public_key {
    906                                     UncompressedPubKey::Ed25519(_) => CoseAlgorithmIdentifier::Eddsa,
    907                                     UncompressedPubKey::P256(_) => CoseAlgorithmIdentifier::Es256,
    908                                     UncompressedPubKey::P384(_) => CoseAlgorithmIdentifier::Es384,
    909                                     UncompressedPubKey::Rsa(_) => CoseAlgorithmIdentifier::Rs256,
    910                                     // Overflow won't occur since this is correct as
    911                                     // `AttestedCredentialData::from_cbor` would have erred if not.
    912                                 }, cred_id_start, cred_id_start + attested_data.credential_id.0.len()))))
    913                             } else {
    914                                 // `publicKey` is only allowed to not exist when `CoseAlgorithmIdentifier::Eddsa`,
    915                                 // `CoseAlgorithmIdentifier::Es256`, or `CoseAlgorithmIdentifier::Rs256` is not
    916                                 // used.
    917                                 attested_info.as_ref().map_or_else(
    918                                     || AttestationObject::parse_data(attestation_object.as_slice()).map_err(Error::custom).and_then(|(att_obj, auth_idx)| {
    919                                         match att_obj.auth_data.attested_credential_data.credential_public_key {
    920                                             UncompressedPubKey::P384(_) => {
    921                                                 // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx`
    922                                                 // is the start of the raw authenticator data which itself contains the raw Credential ID.
    923                                                 Ok(Some((CoseAlgorithmIdentifier::Es384, auth_idx,  auth_idx + att_obj.auth_data.attested_credential_data.credential_id.0.len())))
    924                                             }
    925                                             UncompressedPubKey::Ed25519(_) | UncompressedPubKey::P256(_) | UncompressedPubKey::Rsa(_) => Err(Error::missing_field(PUBLIC_KEY)),
    926                                         }
    927                                     }),
    928                                     |&(ref attested_data, cred_id_start)| {
    929                                         match attested_data.credential_public_key {
    930                                             UncompressedPubKey::P384(_) => {
    931                                                 // Overflow won't occur since this is correct. This is correct since we successfully parsed
    932                                                 // `AttestedCredentialData` and calculated `cred_id_start` from it.
    933                                                 Ok(Some((CoseAlgorithmIdentifier::Es384, cred_id_start, cred_id_start + attested_data.credential_id.0.len())))
    934                                             }
    935                                             UncompressedPubKey::Ed25519(_) | UncompressedPubKey::P256(_) | UncompressedPubKey::Rsa(_) => if flag { Err(Error::invalid_type(Unexpected::Other("null"), &format!("{PUBLIC_KEY} to be a base64url-encoded DER-encoded SubjectPublicKeyInfo").as_str())) } else { Err(Error::missing_field(PUBLIC_KEY)) },
    936                                         }
    937                                     }
    938                                 )
    939                             }
    940                         },
    941                         |der| {
    942                             UncompressedPubKey::from_der(der.0.as_slice()).map_err(Error::custom).and_then(|key| {
    943                                 attested_info.as_ref().map_or_else(
    944                                     || AttestationObject::parse_data(attestation_object.as_slice()).map_err(Error::custom).and_then(|(att_obj, auth_idx)| {
    945                                         if key == att_obj.auth_data.attested_credential_data.credential_public_key {
    946                                             let alg = match att_obj.auth_data.attested_credential_data.credential_public_key {
    947                                                 UncompressedPubKey::Ed25519(_) => CoseAlgorithmIdentifier::Eddsa,
    948                                                 UncompressedPubKey::P256(_) => CoseAlgorithmIdentifier::Es256,
    949                                                 UncompressedPubKey::P384(_) => CoseAlgorithmIdentifier::Es384,
    950                                                 UncompressedPubKey::Rsa(_) => CoseAlgorithmIdentifier::Rs256,
    951                                             };
    952                                             // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx`
    953                                             // is the start of the raw authenticator data which itself contains the raw Credential ID.
    954                                             Ok(Some((alg, auth_idx, auth_idx+ att_obj.auth_data.attested_credential_data.credential_id.0.len())))
    955                                         } else {
    956                                             Err(Error::invalid_value(Unexpected::Bytes(der.0.as_slice()), &format!("DER-encoded public key to match the public key within the attestation object: {:?}", att_obj.auth_data.attested_credential_data.credential_public_key).as_str()))
    957                                         }
    958                                     }),
    959                                     |&(ref attested_data, cred_id_start)| {
    960                                         if key == attested_data.credential_public_key {
    961                                             let alg = match attested_data.credential_public_key {
    962                                                 UncompressedPubKey::Ed25519(_) => CoseAlgorithmIdentifier::Eddsa,
    963                                                 UncompressedPubKey::P256(_) => CoseAlgorithmIdentifier::Es256,
    964                                                 UncompressedPubKey::P384(_) => CoseAlgorithmIdentifier::Es384,
    965                                                 UncompressedPubKey::Rsa(_) => CoseAlgorithmIdentifier::Rs256,
    966                                             };
    967                                             // Overflow won't occur since this is correct. This is correct since we successfully parsed
    968                                             // `AttestedCredentialData` and calculated `cred_id_start` from it.
    969                                             Ok(Some((alg, cred_id_start, cred_id_start + attested_data.credential_id.0.len())))
    970                                         } else {
    971                                             Err(Error::invalid_value(Unexpected::Bytes(der.0.as_slice()), &format!("DER-encoded public key to match the public key within the attestation object: {:?}", attested_data.credential_public_key).as_str()))
    972                                         }
    973                                     }
    974                                 )
    975                             })
    976                         }
    977                     ).and_then(|cred_key_alg_cred_info| {
    978                         key_alg.ok_or(false).and_then(|opt_alg| opt_alg.ok_or(true)).map_or_else(
    979                             |flag| {
    980                                 if R {
    981                                     Ok(cred_key_alg_cred_info.map(|info| (info.1, info.2)))
    982                                 } else if flag {
    983                                     Err(Error::invalid_type(Unexpected::Other("null"), &format!("{PUBLIC_KEY_ALGORITHM} to be a base64url-encoded DER-encoded SubjectPublicKeyInfo").as_str()))
    984                                 } else {
    985                                     Err(Error::missing_field(PUBLIC_KEY_ALGORITHM))
    986                                 }
    987                             },
    988                             |alg| {
    989                                 cred_key_alg_cred_info.map_or_else(
    990                                     || AttestationObject::parse_data(attestation_object.as_slice()).map_err(Error::custom).and_then(|(att_obj, auth_idx)| {
    991                                         let att_obj_alg = match att_obj.auth_data.attested_credential_data.credential_public_key {
    992                                             UncompressedPubKey::Ed25519(_) => CoseAlgorithmIdentifier::Eddsa,
    993                                             UncompressedPubKey::P256(_) => CoseAlgorithmIdentifier::Es256,
    994                                             UncompressedPubKey::P384(_) => CoseAlgorithmIdentifier::Es384,
    995                                             UncompressedPubKey::Rsa(_) => CoseAlgorithmIdentifier::Rs256,
    996                                         };
    997                                         if alg == att_obj_alg {
    998                                             // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx`
    999                                             // is the start of the raw authenticator data which itself contains the raw Credential ID.
   1000                                             Ok(Some((auth_idx, auth_idx + att_obj.auth_data.attested_credential_data.credential_id.0.len())))
   1001                                         } else {
   1002                                             Err(Error::invalid_value(Unexpected::Other(format!("{alg:?}").as_str()), &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {att_obj_alg:?}").as_str()))
   1003                                         }
   1004                                     }),
   1005                                     |(a, start, last)| if alg == a {
   1006                                         Ok(Some((start, last)))
   1007                                     } else {
   1008                                         Err(Error::invalid_value(Unexpected::Other(format!("{alg:?}").as_str()), &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {a:?}").as_str()))
   1009                                     },
   1010                                 )
   1011                             }
   1012                         ).map(|cred_info| AuthAttest{ attest: AuthenticatorAttestation::new(client_data_json, attestation_object, transports), cred_info, })
   1013                     })
   1014                 })
   1015             })
   1016         }))
   1017     }
   1018 }
   1019 /// `"clientDataJSON"`
   1020 const CLIENT_DATA_JSON: &str = "clientDataJSON";
   1021 /// `"attestationObject"`
   1022 const ATTESTATION_OBJECT: &str = "attestationObject";
   1023 /// `"authenticatorData"`
   1024 const AUTHENTICATOR_DATA: &str = "authenticatorData";
   1025 /// `"transports"`
   1026 const TRANSPORTS: &str = "transports";
   1027 /// `"publicKey"`
   1028 const PUBLIC_KEY: &str = "publicKey";
   1029 /// `"publicKeyAlgorithm"`
   1030 const PUBLIC_KEY_ALGORITHM: &str = "publicKeyAlgorithm";
   1031 /// Fields in `AuthenticatorAttestationResponseJSON`.
   1032 pub(super) const AUTH_ATTEST_FIELDS: &[&str; 6] = &[
   1033     CLIENT_DATA_JSON,
   1034     ATTESTATION_OBJECT,
   1035     AUTHENTICATOR_DATA,
   1036     TRANSPORTS,
   1037     PUBLIC_KEY,
   1038     PUBLIC_KEY_ALGORITHM,
   1039 ];
   1040 impl<'de> Deserialize<'de> for AuthAttest {
   1041     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1042     where
   1043         D: Deserializer<'de>,
   1044     {
   1045         deserializer.deserialize_struct(
   1046             "AuthenticatorAttestation",
   1047             AUTH_ATTEST_FIELDS,
   1048             AuthenticatorAttestationVisitor::<false>,
   1049         )
   1050     }
   1051 }
   1052 impl<'de> Deserialize<'de> for AuthenticatorAttestation {
   1053     /// Deserializes a `struct` based on
   1054     /// [`AuthenticatorAttestationResponseJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticatorattestationresponsejson).
   1055     ///
   1056     /// Note unknown keys and duplicate keys are forbidden;
   1057     /// [`clientDataJSON`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-clientdatajson),
   1058     /// [`authenticatorData`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-authenticatordata),
   1059     /// [`publicKey`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-publickey)
   1060     /// and
   1061     /// [`attestationObject`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-attestationobject)
   1062     /// are base64url-decoded;
   1063     /// [`transports`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-transports)
   1064     /// is deserialized via [`AuthTransports::deserialize`]; the decoded `publicKey` is parsed according to the
   1065     /// applicable DER-encoded ASN.1 `SubjectPublicKeyInfo` schema;
   1066     /// [`publicKeyAlgorithm`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-publickeyalgorithm)
   1067     /// is deserialized according to
   1068     /// [`CoseAlgorithmIdentifier`](https://www.w3.org/TR/webauthn-3/#typedefdef-cosealgorithmidentifier); all `required`
   1069     /// fields in the `AuthenticatorAttestationResponseJSON` Web IDL `dictionary` exist (and must not be `null`); `publicKey`
   1070     /// exists when Ed25519, P-256 with SHA-256, or RSASSA-PKCS1-v1_5 with SHA-256 is used (and must not be `null`)
   1071     /// [per WebAuthn](https://www.w3.org/TR/webauthn-3/#sctn-public-key-easy); the `publicKeyAlgorithm` aligns
   1072     /// with
   1073     /// [`credentialPublicKey`](https://www.w3.org/TR/webauthn-3/#authdata-attestedcredentialdata-credentialpublickey)
   1074     /// within
   1075     /// [`attestedCredentialData`](https://www.w3.org/TR/webauthn-3/#authdata-attestedcredentialdata) within the
   1076     /// decoded `authenticatorData`; the decoded `publicKey` is the same as `credentialPublicKey` within
   1077     /// `attestedCredentialData` within the decoded `authenticatorData`; and the decoded `authenticatorData` is the
   1078     /// same as [`authData`](https://www.w3.org/TR/webauthn-3/#attestation-object) within the decoded
   1079     /// `attestationObject`.
   1080     #[inline]
   1081     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1082     where
   1083         D: Deserializer<'de>,
   1084     {
   1085         AuthAttest::deserialize(deserializer).map(|val| val.attest)
   1086     }
   1087 }
   1088 /// `Visitor` for `CredentialPropertiesOutput`.
   1089 ///
   1090 /// Unknown fields are ignored iff `RELAXED`.
   1091 pub(super) struct CredentialPropertiesOutputVisitor<const RELAXED: bool>;
   1092 impl<'d, const R: bool> Visitor<'d> for CredentialPropertiesOutputVisitor<R> {
   1093     type Value = CredentialPropertiesOutput;
   1094     fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1095         formatter.write_str("CredentialPropertiesOutput")
   1096     }
   1097     fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
   1098     where
   1099         A: MapAccess<'d>,
   1100     {
   1101         /// Allowed fields.
   1102         enum Field<const IGNORE_UNKNOWN: bool> {
   1103             /// `rk` field.
   1104             Rk,
   1105             /// Unknown field.
   1106             Other,
   1107         }
   1108         impl<'e, const I: bool> Deserialize<'e> for Field<I> {
   1109             fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1110             where
   1111                 D: Deserializer<'e>,
   1112             {
   1113                 /// `Visitor` for `Field`.
   1114                 ///
   1115                 /// Unknown fields are ignored iff `IGNORE_UNKNOWN`.
   1116                 struct FieldVisitor<const IGNORE_UNKNOWN: bool>;
   1117                 impl<const IG: bool> Visitor<'_> for FieldVisitor<IG> {
   1118                     type Value = Field<IG>;
   1119                     fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1120                         write!(formatter, "'{RK}'")
   1121                     }
   1122                     fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
   1123                     where
   1124                         E: Error,
   1125                     {
   1126                         match v {
   1127                             RK => Ok(Field::Rk),
   1128                             _ => {
   1129                                 if IG {
   1130                                     Ok(Field::Other)
   1131                                 } else {
   1132                                     Err(E::unknown_field(v, PROPS_FIELDS))
   1133                                 }
   1134                             }
   1135                         }
   1136                     }
   1137                 }
   1138                 deserializer.deserialize_identifier(FieldVisitor)
   1139             }
   1140         }
   1141         let mut rk = None;
   1142         while let Some(key) = map.next_key::<Field<R>>()? {
   1143             match key {
   1144                 Field::Rk => {
   1145                     if rk.is_some() {
   1146                         return Err(Error::duplicate_field(RK));
   1147                     }
   1148                     rk = map.next_value().map(Some)?;
   1149                 }
   1150                 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?,
   1151             }
   1152         }
   1153         Ok(CredentialPropertiesOutput { rk: rk.flatten() })
   1154     }
   1155 }
   1156 /// `"rk"`
   1157 const RK: &str = "rk";
   1158 /// `CredentialPropertiesOutput` fields.
   1159 pub(super) const PROPS_FIELDS: &[&str; 1] = &[RK];
   1160 impl<'de> Deserialize<'de> for CredentialPropertiesOutput {
   1161     /// Deserializes a `struct` based on
   1162     /// [`CredentialPropertiesOutput`](https://www.w3.org/TR/webauthn-3/#dictdef-credentialpropertiesoutput).
   1163     ///
   1164     /// Note unknown and duplicate keys are forbidden.
   1165     #[inline]
   1166     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1167     where
   1168         D: Deserializer<'de>,
   1169     {
   1170         deserializer.deserialize_struct(
   1171             "CredentialPropertiesOutput",
   1172             PROPS_FIELDS,
   1173             CredentialPropertiesOutputVisitor::<false>,
   1174         )
   1175     }
   1176 }
   1177 impl<'de> Deserialize<'de> for AuthenticationExtensionsPrfOutputs {
   1178     /// Deserializes a `struct` based on
   1179     /// [`AuthenticationExtensionsPRFOutputsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfoutputsjson).
   1180     ///
   1181     /// Note unknown and duplicate keys are forbidden;
   1182     /// [`enabled`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-enabled)
   1183     /// must exist (and not be `null`); and
   1184     /// [`results`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-results) must not exist,
   1185     /// be `null`, or be an
   1186     /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues)
   1187     /// with no unknown or duplicate keys,
   1188     /// [`first`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-first) must exist but be
   1189     /// `null`, and
   1190     /// [`second`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-second) can exist but
   1191     /// must be `null` if so.
   1192     #[inline]
   1193     #[expect(clippy::unreachable, reason = "we want to crash when there is a bug")]
   1194     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1195     where
   1196         D: Deserializer<'de>,
   1197     {
   1198         AuthenticationExtensionsPrfOutputsHelper::<false, true, AuthenticationExtensionsPrfValues>::deserialize(deserializer).map(|val| Self {
   1199             enabled: val.0.unwrap_or_else(|| {
   1200                 unreachable!(
   1201                     "there is a bug in AuthenticationExtensionsPrfOutputsHelper::deserialize"
   1202                 )
   1203             }),
   1204         })
   1205     }
   1206 }
   1207 /// `Visitor` for `ClientExtensionsOutputs`.
   1208 ///
   1209 /// Unknown fields are ignored iff `RELAXED`.
   1210 pub(super) struct ClientExtensionsOutputsVisitor<const RELAXED: bool, PROPS, PRF>(
   1211     pub PhantomData<fn() -> (PROPS, PRF)>,
   1212 );
   1213 impl<'d, const R: bool, C, P> Visitor<'d> for ClientExtensionsOutputsVisitor<R, C, P>
   1214 where
   1215     C: for<'a> Deserialize<'a> + Into<CredentialPropertiesOutput>,
   1216     P: for<'a> Deserialize<'a> + Into<AuthenticationExtensionsPrfOutputs>,
   1217 {
   1218     type Value = ClientExtensionsOutputs;
   1219     fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1220         formatter.write_str("ClientExtensionsOutputs")
   1221     }
   1222     fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
   1223     where
   1224         A: MapAccess<'d>,
   1225     {
   1226         /// Allowed fields.
   1227         enum Field<const IGNORE_UNKNOWN: bool> {
   1228             /// `credProps` field.
   1229             CredProps,
   1230             /// `prf` field.
   1231             Prf,
   1232             /// Unknown field.
   1233             Other,
   1234         }
   1235         impl<'e, const I: bool> Deserialize<'e> for Field<I> {
   1236             fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1237             where
   1238                 D: Deserializer<'e>,
   1239             {
   1240                 /// `Visitor` for `Field`.
   1241                 ///
   1242                 /// Unknown fields are ignored iff `IGNORE_UNKNOWN`.
   1243                 struct FieldVisitor<const IGNORE_UNKNOWN: bool>;
   1244                 impl<const IG: bool> Visitor<'_> for FieldVisitor<IG> {
   1245                     type Value = Field<IG>;
   1246                     fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
   1247                         write!(formatter, "'{CRED_PROPS}' or '{PRF}'")
   1248                     }
   1249                     fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
   1250                     where
   1251                         E: Error,
   1252                     {
   1253                         match v {
   1254                             CRED_PROPS => Ok(Field::CredProps),
   1255                             PRF => Ok(Field::Prf),
   1256                             _ => {
   1257                                 if IG {
   1258                                     Ok(Field::Other)
   1259                                 } else {
   1260                                     Err(E::unknown_field(v, EXT_FIELDS))
   1261                                 }
   1262                             }
   1263                         }
   1264                     }
   1265                 }
   1266                 deserializer.deserialize_identifier(FieldVisitor)
   1267             }
   1268         }
   1269         let mut cred_props = None;
   1270         let mut prf = None;
   1271         while let Some(key) = map.next_key::<Field<R>>()? {
   1272             match key {
   1273                 Field::CredProps => {
   1274                     if cred_props.is_some() {
   1275                         return Err(Error::duplicate_field(CRED_PROPS));
   1276                     }
   1277                     cred_props = map.next_value::<Option<C>>().map(Some)?;
   1278                 }
   1279                 Field::Prf => {
   1280                     if prf.is_some() {
   1281                         return Err(Error::duplicate_field(PRF));
   1282                     }
   1283                     prf = map.next_value::<Option<P>>().map(Some)?;
   1284                 }
   1285                 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?,
   1286             }
   1287         }
   1288         Ok(ClientExtensionsOutputs {
   1289             cred_props: cred_props.flatten().map(Into::into),
   1290             prf: prf.flatten().map(Into::into),
   1291         })
   1292     }
   1293 }
   1294 impl ClientExtensions for ClientExtensionsOutputs {
   1295     fn empty() -> Self {
   1296         Self {
   1297             prf: None,
   1298             cred_props: None,
   1299         }
   1300     }
   1301 }
   1302 /// `"credProps"`
   1303 const CRED_PROPS: &str = "credProps";
   1304 /// `"prf"`
   1305 const PRF: &str = "prf";
   1306 /// `AuthenticationExtensionsClientOutputsJSON` fields.
   1307 pub(super) const EXT_FIELDS: &[&str; 2] = &[CRED_PROPS, PRF];
   1308 impl<'de> Deserialize<'de> for ClientExtensionsOutputs {
   1309     /// Deserializes a `struct` based on
   1310     /// [`AuthenticationExtensionsClientOutputsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsclientoutputsjson).
   1311     ///
   1312     /// Note that unknown and duplicate keys are forbidden;
   1313     /// [`credProps`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsclientoutputs-credprops) is
   1314     /// `null` or deserialized via [`CredentialPropertiesOutput::deserialize`]; and
   1315     /// [`prf`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsclientoutputs-prf) is `null`
   1316     /// or deserialized via [`AuthenticationExtensionsPrfOutputs::deserialize`].
   1317     #[inline]
   1318     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1319     where
   1320         D: Deserializer<'de>,
   1321     {
   1322         deserializer.deserialize_struct(
   1323             "ClientExtensionsOutputs",
   1324             EXT_FIELDS,
   1325             ClientExtensionsOutputsVisitor::<
   1326                 false,
   1327                 CredentialPropertiesOutput,
   1328                 AuthenticationExtensionsPrfOutputs,
   1329             >(PhantomData),
   1330         )
   1331     }
   1332 }
   1333 impl<'de> Deserialize<'de> for Registration {
   1334     /// Deserializes a `struct` based on
   1335     /// [`RegistrationResponseJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-registrationresponsejson).
   1336     ///
   1337     /// Note that unknown and duplicate keys are forbidden;
   1338     /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-id) and
   1339     /// [`rawId`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-rawid) are deserialized
   1340     /// via [`CredentialId::deserialize`];
   1341     /// [`response`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-response) is deserialized
   1342     /// via [`AuthenticatorAttestation::deserialize`];
   1343     /// [`authenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-authenticatorattachment)
   1344     /// is `null` or deserialized via [`AuthenticatorAttachment::deserialize`];
   1345     /// [`clientExtensionResults`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-clientextensionresults)
   1346     /// is deserialized via [`ClientExtensionsOutputs::deserialize`]; all `required` fields in the
   1347     /// `RegistrationResponseJSON` Web IDL `dictionary` exist (and are not `null`);
   1348     /// [`type`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-type) is `"public-key"`;
   1349     /// and the decoded `id`, decoded `rawId`, and
   1350     /// [`credentialId`](https://www.w3.org/TR/webauthn-3/#authdata-attestedcredentialdata-credentialid) within
   1351     /// [`attestedCredentialData`](https://www.w3.org/TR/webauthn-3/#authdata-attestedcredentialdata) within
   1352     /// [`authData`](https://www.w3.org/TR/webauthn-3/#attestation-object) within the decoded
   1353     /// [`attestationObject`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-attestationobject)
   1354     /// are all the same.
   1355     #[expect(clippy::unreachable, reason = "when there is a bug, we want to crash")]
   1356     #[expect(clippy::indexing_slicing, reason = "comment justifies its correctness")]
   1357     #[inline]
   1358     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
   1359     where
   1360         D: Deserializer<'de>,
   1361     {
   1362         PublicKeyCredential::<false, true, AuthAttest, ClientExtensionsOutputs>::deserialize(deserializer).and_then(|cred| {
   1363             let id = cred.id.unwrap_or_else(|| unreachable!("there is a bug in PublicKeyCredential::deserialize"));
   1364             cred.response.cred_info.map_or_else(
   1365                 || AttestationObject::try_from(cred.response.attest.attestation_object()).map_err(Error::custom).and_then(|att_obj| {
   1366                     if id == att_obj.auth_data.attested_credential_data.credential_id {
   1367                         Ok(())
   1368                     } else {
   1369                         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()))
   1370                     }
   1371                 }),
   1372                 // `start` and `last` were calculated based on `cred.response.attest.attestation_object()`
   1373                 // and represent the starting and ending index of the `CredentialId`; therefore this is correct
   1374                 // let alone won't `panic`.
   1375                 |(start, last)| if id.0 == cred.response.attest.attestation_object()[start..last] {
   1376                     Ok(())
   1377                 } else {
   1378                     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.attest.attestation_object()[start..last]).as_str()))
   1379                 },
   1380             ).map(|()| Self { response: cred.response.attest, authenticator_attachment: cred.authenticator_attachment, client_extension_results: cred.client_extension_results, })
   1381         })
   1382     }
   1383 }
   1384 #[cfg(test)]
   1385 mod tests {
   1386     use super::{
   1387         super::{
   1388             ALG, AuthenticatorAttachment, EC2, EDDSA, ES256, ES384, Ed25519PubKey, KTY, OKP, RSA,
   1389             Registration, RsaPubKey, UncompressedP256PubKey, UncompressedP384PubKey, cbor,
   1390         },
   1391         CoseAlgorithmIdentifier,
   1392         spki::SubjectPublicKeyInfo as _,
   1393     };
   1394     use ed25519_dalek::{VerifyingKey, pkcs8::EncodePublicKey as _};
   1395     use p256::{
   1396         EncodedPoint as P256Pt, PublicKey as P256PubKey, SecretKey as P256Key,
   1397         elliptic_curve::sec1::{FromEncodedPoint as _, ToEncodedPoint as _},
   1398     };
   1399     use p384::{EncodedPoint as P384Pt, PublicKey as P384PubKey, SecretKey as P384Key};
   1400     use rsa::{
   1401         BigUint, RsaPrivateKey,
   1402         sha2::{Digest as _, Sha256},
   1403         traits::PublicKeyParts as _,
   1404     };
   1405     use serde::de::{Error as _, Unexpected};
   1406     use serde_json::Error;
   1407     #[expect(clippy::unwrap_used, reason = "OK in tests")]
   1408     #[test]
   1409     fn ed25519_spki() {
   1410         assert!(
   1411             Ed25519PubKey::from_der(
   1412                 VerifyingKey::from_bytes(&[1; 32])
   1413                     .unwrap()
   1414                     .to_public_key_der()
   1415                     .unwrap()
   1416                     .as_bytes()
   1417             )
   1418             .is_ok_and(|k| k.0 == [1; 32])
   1419         );
   1420     }
   1421     #[expect(clippy::unwrap_used, reason = "OK in tests")]
   1422     #[test]
   1423     fn p256_spki() {
   1424         let key = P256Key::from_bytes(
   1425             &[
   1426                 137, 133, 36, 206, 163, 47, 255, 5, 76, 144, 163, 141, 40, 109, 108, 240, 246, 115,
   1427                 178, 237, 169, 68, 6, 129, 92, 21, 238, 127, 55, 158, 207, 95,
   1428             ]
   1429             .into(),
   1430         )
   1431         .unwrap()
   1432         .public_key();
   1433         let enc_key = key.to_encoded_point(false);
   1434         assert!(
   1435             UncompressedP256PubKey::from_der(key.to_public_key_der().unwrap().as_bytes())
   1436                 .is_ok_and(|k| *k.0 == **enc_key.x().unwrap() && *k.1 == **enc_key.y().unwrap())
   1437         );
   1438     }
   1439     #[expect(clippy::unwrap_used, reason = "OK in tests")]
   1440     #[test]
   1441     fn p384_spki() {
   1442         let key = P384Key::from_bytes(
   1443             &[
   1444                 158, 99, 156, 49, 190, 211, 85, 167, 28, 2, 80, 57, 31, 22, 17, 38, 85, 78, 232,
   1445                 42, 45, 199, 154, 243, 136, 251, 84, 34, 5, 120, 208, 91, 61, 248, 64, 144, 87, 1,
   1446                 32, 86, 220, 68, 182, 11, 105, 223, 75, 70,
   1447             ]
   1448             .into(),
   1449         )
   1450         .unwrap()
   1451         .public_key();
   1452         let enc_key = key.to_encoded_point(false);
   1453         assert!(
   1454             UncompressedP384PubKey::from_der(key.to_public_key_der().unwrap().as_bytes())
   1455                 .is_ok_and(|k| *k.0 == **enc_key.x().unwrap() && *k.1 == **enc_key.y().unwrap())
   1456         );
   1457     }
   1458     #[expect(clippy::unwrap_used, reason = "OK in tests")]
   1459     #[test]
   1460     fn rsa_spki() {
   1461         let n = [
   1462             111, 183, 124, 133, 38, 167, 70, 148, 44, 50, 30, 60, 121, 14, 38, 37, 96, 114, 107,
   1463             195, 248, 64, 79, 36, 237, 140, 43, 27, 94, 74, 102, 152, 135, 102, 184, 150, 186, 206,
   1464             185, 19, 165, 209, 48, 98, 98, 9, 3, 205, 208, 82, 250, 105, 132, 201, 73, 62, 60, 165,
   1465             100, 128, 153, 9, 41, 118, 66, 95, 236, 214, 73, 135, 197, 68, 184, 10, 27, 116, 204,
   1466             145, 50, 174, 58, 42, 183, 181, 119, 232, 126, 252, 217, 96, 162, 190, 103, 122, 64,
   1467             87, 145, 45, 32, 207, 17, 239, 223, 3, 35, 14, 112, 119, 124, 141, 123, 208, 239, 105,
   1468             81, 217, 151, 162, 190, 17, 88, 182, 176, 158, 81, 200, 42, 166, 133, 48, 23, 236, 55,
   1469             117, 248, 233, 151, 203, 122, 155, 231, 46, 177, 20, 20, 151, 64, 222, 239, 226, 7, 21,
   1470             254, 81, 202, 64, 232, 161, 235, 22, 51, 246, 207, 213, 0, 229, 138, 46, 222, 205, 157,
   1471             108, 139, 253, 230, 80, 50, 2, 122, 212, 163, 100, 180, 114, 12, 113, 52, 56, 99, 188,
   1472             42, 198, 212, 23, 182, 222, 56, 221, 200, 79, 96, 239, 221, 135, 10, 17, 106, 183, 56,
   1473             104, 68, 94, 198, 196, 35, 200, 83, 204, 26, 185, 204, 212, 31, 183, 19, 111, 233, 13,
   1474             72, 93, 53, 65, 111, 59, 242, 122, 160, 244, 162, 126, 38, 235, 156, 47, 88, 39, 132,
   1475             153, 79, 0, 133, 78, 7, 218, 165, 241,
   1476         ];
   1477         let e = 0x0001_0001u32;
   1478         let d = [
   1479             145, 79, 21, 97, 233, 3, 192, 194, 177, 68, 181, 80, 120, 197, 23, 44, 185, 74, 144, 0,
   1480             132, 149, 139, 11, 16, 224, 4, 112, 236, 94, 238, 97, 121, 124, 213, 145, 24, 253, 168,
   1481             35, 190, 205, 132, 115, 33, 201, 38, 253, 246, 180, 66, 155, 165, 46, 3, 254, 68, 108,
   1482             154, 247, 246, 45, 187, 0, 204, 96, 185, 157, 249, 174, 158, 38, 62, 244, 183, 76, 102,
   1483             6, 219, 92, 212, 138, 59, 147, 163, 219, 111, 39, 105, 21, 236, 196, 38, 255, 114, 247,
   1484             82, 104, 113, 204, 29, 152, 209, 219, 48, 239, 74, 129, 19, 247, 33, 239, 119, 166,
   1485             216, 152, 94, 138, 238, 164, 242, 129, 50, 150, 57, 20, 53, 224, 56, 241, 138, 97, 111,
   1486             215, 107, 212, 195, 146, 108, 143, 0, 229, 181, 171, 73, 152, 105, 146, 25, 243, 242,
   1487             140, 252, 248, 162, 247, 63, 168, 180, 20, 153, 120, 10, 248, 211, 1, 71, 127, 212,
   1488             249, 237, 203, 202, 48, 26, 216, 226, 228, 186, 13, 204, 70, 255, 240, 89, 255, 59, 83,
   1489             31, 253, 55, 43, 158, 90, 248, 83, 32, 159, 105, 57, 134, 34, 96, 18, 255, 245, 153,
   1490             162, 60, 91, 99, 220, 51, 44, 85, 114, 67, 125, 202, 65, 217, 245, 40, 8, 81, 165, 142,
   1491             24, 245, 127, 122, 247, 152, 212, 75, 45, 59, 90, 184, 234, 31, 147, 36, 8, 212, 45,
   1492             50, 23, 3, 25, 253, 87, 227, 79, 119, 161,
   1493         ];
   1494         let p = BigUint::from_bytes_le(
   1495             [
   1496                 215, 166, 5, 21, 11, 179, 41, 77, 198, 92, 165, 48, 77, 162, 42, 41, 206, 141, 60,
   1497                 69, 47, 164, 19, 92, 46, 72, 100, 238, 100, 53, 214, 197, 163, 185, 6, 140, 229,
   1498                 250, 195, 77, 8, 12, 5, 236, 178, 173, 86, 201, 43, 213, 165, 51, 108, 101, 161,
   1499                 99, 76, 240, 14, 234, 76, 197, 137, 53, 198, 168, 135, 205, 212, 198, 120, 29, 16,
   1500                 82, 98, 233, 236, 177, 12, 171, 141, 100, 107, 146, 33, 176, 125, 202, 172, 79,
   1501                 147, 179, 30, 62, 247, 206, 169, 19, 168, 114, 26, 73, 108, 178, 105, 84, 89, 191,
   1502                 168, 253, 228, 214, 54, 16, 212, 199, 111, 72, 3, 41, 247, 227, 165, 244, 32, 188,
   1503                 24, 247,
   1504             ]
   1505             .as_slice(),
   1506         );
   1507         let p_2 = BigUint::from_bytes_le(
   1508             [
   1509                 41, 25, 198, 240, 134, 206, 121, 57, 11, 5, 134, 192, 212, 77, 229, 197, 14, 78,
   1510                 85, 212, 190, 114, 179, 188, 21, 171, 174, 12, 104, 74, 15, 164, 136, 173, 62, 177,
   1511                 141, 213, 93, 102, 147, 83, 59, 124, 146, 59, 175, 213, 55, 27, 25, 248, 154, 29,
   1512                 39, 85, 50, 235, 134, 60, 203, 106, 186, 195, 190, 185, 71, 169, 142, 236, 92, 11,
   1513                 250, 187, 198, 8, 201, 184, 120, 178, 227, 87, 63, 243, 89, 227, 234, 184, 28, 252,
   1514                 112, 211, 193, 69, 23, 92, 5, 72, 93, 53, 69, 159, 73, 160, 105, 244, 249, 94, 214,
   1515                 173, 9, 236, 4, 255, 129, 11, 224, 140, 252, 168, 57, 143, 176, 241, 60, 219, 90,
   1516                 250,
   1517             ]
   1518             .as_slice(),
   1519         );
   1520         let key = RsaPrivateKey::from_components(
   1521             BigUint::from_bytes_le(n.as_slice()),
   1522             e.into(),
   1523             BigUint::from_bytes_le(d.as_slice()),
   1524             vec![p, p_2],
   1525         )
   1526         .unwrap()
   1527         .to_public_key();
   1528         assert!(
   1529             RsaPubKey::from_der(key.to_public_key_der().unwrap().as_bytes())
   1530                 .is_ok_and(|k| k.0 == key.n().to_bytes_be() && BigUint::from(k.1) == *key.e())
   1531         );
   1532     }
   1533     #[expect(clippy::unwrap_used, reason = "OK in tests")]
   1534     #[expect(clippy::indexing_slicing, reason = "comments justify correctness")]
   1535     #[expect(
   1536         clippy::cognitive_complexity,
   1537         clippy::too_many_lines,
   1538         reason = "a lot to test"
   1539     )]
   1540     #[test]
   1541     fn eddsa_registration_deserialize_data_mismatch() {
   1542         let c_data_json = serde_json::json!({}).to_string();
   1543         let att_obj: [u8; 143] = [
   1544             cbor::MAP_3,
   1545             cbor::TEXT_3,
   1546             b'f',
   1547             b'm',
   1548             b't',
   1549             cbor::TEXT_4,
   1550             b'n',
   1551             b'o',
   1552             b'n',
   1553             b'e',
   1554             cbor::TEXT_7,
   1555             b'a',
   1556             b't',
   1557             b't',
   1558             b'S',
   1559             b't',
   1560             b'm',
   1561             b't',
   1562             cbor::MAP_0,
   1563             cbor::TEXT_8,
   1564             b'a',
   1565             b'u',
   1566             b't',
   1567             b'h',
   1568             b'D',
   1569             b'a',
   1570             b't',
   1571             b'a',
   1572             cbor::BYTES_INFO_24,
   1573             115,
   1574             // `rpIdHash`.
   1575             0,
   1576             0,
   1577             0,
   1578             0,
   1579             0,
   1580             0,
   1581             0,
   1582             0,
   1583             0,
   1584             0,
   1585             0,
   1586             0,
   1587             0,
   1588             0,
   1589             0,
   1590             0,
   1591             0,
   1592             0,
   1593             0,
   1594             0,
   1595             0,
   1596             0,
   1597             0,
   1598             0,
   1599             0,
   1600             0,
   1601             0,
   1602             0,
   1603             0,
   1604             0,
   1605             0,
   1606             0,
   1607             // `flags`.
   1608             0b0100_0101,
   1609             // `signCount`.
   1610             0,
   1611             0,
   1612             0,
   1613             0,
   1614             // `aaguid`.
   1615             0,
   1616             0,
   1617             0,
   1618             0,
   1619             0,
   1620             0,
   1621             0,
   1622             0,
   1623             0,
   1624             0,
   1625             0,
   1626             0,
   1627             0,
   1628             0,
   1629             0,
   1630             0,
   1631             // `credentialIdLength`.
   1632             0,
   1633             16,
   1634             // `credentialId`.
   1635             0,
   1636             0,
   1637             0,
   1638             0,
   1639             0,
   1640             0,
   1641             0,
   1642             0,
   1643             0,
   1644             0,
   1645             0,
   1646             0,
   1647             0,
   1648             0,
   1649             0,
   1650             0,
   1651             // Ed25519 COSE key.
   1652             cbor::MAP_4,
   1653             KTY,
   1654             OKP,
   1655             ALG,
   1656             EDDSA,
   1657             // `crv`.
   1658             cbor::NEG_ONE,
   1659             // `Ed25519`.
   1660             cbor::SIX,
   1661             // `x`.
   1662             cbor::NEG_TWO,
   1663             cbor::BYTES_INFO_24,
   1664             32,
   1665             // Compressed y-coordinate.
   1666             1,
   1667             1,
   1668             1,
   1669             1,
   1670             1,
   1671             1,
   1672             1,
   1673             1,
   1674             1,
   1675             1,
   1676             1,
   1677             1,
   1678             1,
   1679             1,
   1680             1,
   1681             1,
   1682             1,
   1683             1,
   1684             1,
   1685             1,
   1686             1,
   1687             1,
   1688             1,
   1689             1,
   1690             1,
   1691             1,
   1692             1,
   1693             1,
   1694             1,
   1695             1,
   1696             1,
   1697             1,
   1698         ];
   1699         let pub_key = VerifyingKey::from_bytes(&[1; 32])
   1700             .unwrap()
   1701             .to_public_key_der()
   1702             .unwrap();
   1703         let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes());
   1704         let att_obj_len = att_obj.len();
   1705         let auth_data_start = att_obj_len - 113;
   1706         let b64_adata = base64url_nopad::encode(&att_obj[auth_data_start..]);
   1707         let b64_key = base64url_nopad::encode(pub_key.as_bytes());
   1708         let b64_aobj = base64url_nopad::encode(att_obj.as_slice());
   1709         // Base case is valid.
   1710         assert!(
   1711             serde_json::from_str::<Registration>(
   1712                 serde_json::json!({
   1713                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1714                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1715                     "response": {
   1716                         "clientDataJSON": b64_cdata_json,
   1717                         "authenticatorData": b64_adata,
   1718                         "transports": ["ble", "usb", "hybrid", "internal", "nfc", "smart-card"],
   1719                         "publicKey": b64_key,
   1720                         "publicKeyAlgorithm": -8i8,
   1721                         "attestationObject": b64_aobj,
   1722                     },
   1723                     "authenticatorAttachment": "cross-platform",
   1724                     "clientExtensionResults": {},
   1725                     "type": "public-key"
   1726                 })
   1727                 .to_string()
   1728                 .as_str()
   1729             )
   1730             .is_ok_and(
   1731                 |reg| reg.response.client_data_json == c_data_json.as_bytes()
   1732                     && reg.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj
   1733                     && reg.response.attestation_object_and_c_data_hash[att_obj_len..]
   1734                         == *Sha256::digest(c_data_json.as_bytes())
   1735                     && reg.response.transports.count() == 6
   1736                     && matches!(
   1737                         reg.authenticator_attachment,
   1738                         AuthenticatorAttachment::CrossPlatform
   1739                     )
   1740                     && reg.client_extension_results.cred_props.is_none()
   1741                     && reg.client_extension_results.prf.is_none()
   1742             )
   1743         );
   1744         // `id` and `rawId` mismatch.
   1745         let mut err = Error::invalid_value(
   1746             Unexpected::Bytes(
   1747                 base64url_nopad::decode(b"ABABABABABABABABABABAA")
   1748                     .unwrap()
   1749                     .as_slice(),
   1750             ),
   1751             &format!("id and rawId to match: CredentialId({:?})", [0u8; 16]).as_str(),
   1752         )
   1753         .to_string()
   1754         .into_bytes();
   1755         assert_eq!(
   1756             serde_json::from_str::<Registration>(
   1757                 serde_json::json!({
   1758                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1759                     "rawId": "ABABABABABABABABABABAA",
   1760                     "response": {
   1761                         "clientDataJSON": b64_cdata_json,
   1762                         "authenticatorData": b64_adata,
   1763                         "transports": [],
   1764                         "publicKey": b64_key,
   1765                         "publicKeyAlgorithm": -8i8,
   1766                         "attestationObject": b64_aobj,
   1767                     },
   1768                     "clientExtensionResults": {},
   1769                     "type": "public-key"
   1770                 })
   1771                 .to_string()
   1772                 .as_str()
   1773             )
   1774             .unwrap_err()
   1775             .to_string()
   1776             .into_bytes()
   1777             .get(..err.len()),
   1778             Some(err.as_slice())
   1779         );
   1780         // missing `id`.
   1781         err = Error::missing_field("id").to_string().into_bytes();
   1782         assert_eq!(
   1783             serde_json::from_str::<Registration>(
   1784                 serde_json::json!({
   1785                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1786                     "response": {
   1787                         "clientDataJSON": b64_cdata_json,
   1788                         "authenticatorData": b64_adata,
   1789                         "transports": [],
   1790                         "publicKey": b64_key,
   1791                         "publicKeyAlgorithm": -8i8,
   1792                         "attestationObject": b64_aobj,
   1793                     },
   1794                     "clientExtensionResults": {},
   1795                     "type": "public-key"
   1796                 })
   1797                 .to_string()
   1798                 .as_str()
   1799             )
   1800             .unwrap_err()
   1801             .to_string()
   1802             .into_bytes()
   1803             .get(..err.len()),
   1804             Some(err.as_slice())
   1805         );
   1806         // `null` `id`.
   1807         err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId")
   1808             .to_string()
   1809             .into_bytes();
   1810         assert_eq!(
   1811             serde_json::from_str::<Registration>(
   1812                 serde_json::json!({
   1813                     "id": null,
   1814                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1815                     "response": {
   1816                         "clientDataJSON": b64_cdata_json,
   1817                         "authenticatorData": b64_adata,
   1818                         "transports": [],
   1819                         "publicKey": b64_key,
   1820                         "publicKeyAlgorithm": -8i8,
   1821                         "attestationObject": b64_aobj,
   1822                     },
   1823                     "clientExtensionResults": {},
   1824                     "type": "public-key"
   1825                 })
   1826                 .to_string()
   1827                 .as_str()
   1828             )
   1829             .unwrap_err()
   1830             .to_string()
   1831             .into_bytes()
   1832             .get(..err.len()),
   1833             Some(err.as_slice())
   1834         );
   1835         // missing `rawId`.
   1836         err = Error::missing_field("rawId").to_string().into_bytes();
   1837         assert_eq!(
   1838             serde_json::from_str::<Registration>(
   1839                 serde_json::json!({
   1840                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1841                     "response": {
   1842                         "clientDataJSON": b64_cdata_json,
   1843                         "authenticatorData": b64_adata,
   1844                         "transports": [],
   1845                         "publicKey": b64_key,
   1846                         "publicKeyAlgorithm": -8i8,
   1847                         "attestationObject": b64_aobj,
   1848                     },
   1849                     "clientExtensionResults": {},
   1850                     "type": "public-key"
   1851                 })
   1852                 .to_string()
   1853                 .as_str()
   1854             )
   1855             .unwrap_err()
   1856             .to_string()
   1857             .into_bytes()
   1858             .get(..err.len()),
   1859             Some(err.as_slice())
   1860         );
   1861         // `null` `rawId`.
   1862         err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId")
   1863             .to_string()
   1864             .into_bytes();
   1865         assert_eq!(
   1866             serde_json::from_str::<Registration>(
   1867                 serde_json::json!({
   1868                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1869                     "rawId": null,
   1870                     "response": {
   1871                         "clientDataJSON": b64_cdata_json,
   1872                         "authenticatorData": b64_adata,
   1873                         "transports": [],
   1874                         "publicKey": b64_key,
   1875                         "publicKeyAlgorithm": -8i8,
   1876                         "attestationObject": b64_aobj,
   1877                     },
   1878                     "clientExtensionResults": {},
   1879                     "type": "public-key"
   1880                 })
   1881                 .to_string()
   1882                 .as_str()
   1883             )
   1884             .unwrap_err()
   1885             .to_string()
   1886             .into_bytes()
   1887             .get(..err.len()),
   1888             Some(err.as_slice())
   1889         );
   1890         // `id` and the credential id in authenticator data mismatch.
   1891         err = Error::invalid_value(
   1892             Unexpected::Bytes(
   1893                 base64url_nopad
   1894                     ::decode(b"ABABABABABABABABABABAA")
   1895                     .unwrap()
   1896                     .as_slice(),
   1897             ),
   1898             &format!("id, rawId, and the credential id in the attested credential data to all match: {:?}", [0u8; 16]).as_str(),
   1899         )
   1900         .to_string().into_bytes();
   1901         assert_eq!(
   1902             serde_json::from_str::<Registration>(
   1903                 serde_json::json!({
   1904                     "id": "ABABABABABABABABABABAA",
   1905                     "rawId": "ABABABABABABABABABABAA",
   1906                     "response": {
   1907                         "clientDataJSON": b64_cdata_json,
   1908                         "authenticatorData": b64_adata,
   1909                         "transports": [],
   1910                         "publicKey": b64_key,
   1911                         "publicKeyAlgorithm": -8i8,
   1912                         "attestationObject": b64_aobj,
   1913                     },
   1914                     "clientExtensionResults": {},
   1915                     "type": "public-key"
   1916                 })
   1917                 .to_string()
   1918                 .as_str()
   1919             )
   1920             .unwrap_err()
   1921             .to_string()
   1922             .into_bytes()
   1923             .get(..err.len()),
   1924             Some(err.as_slice())
   1925         );
   1926         // `authenticatorData` mismatches `authData` in attestation object.
   1927         let mut bad_auth = [0; 113];
   1928         bad_auth.copy_from_slice(&att_obj[auth_data_start..]);
   1929         bad_auth[113 - 32..].copy_from_slice([0; 32].as_slice());
   1930         err = Error::invalid_value(
   1931             Unexpected::Bytes(bad_auth.as_slice()),
   1932             &format!("authenticator data to match the authenticator data portion of attestation object: {:?}", &att_obj[att_obj_len - bad_auth.len()..]).as_str(),
   1933         )
   1934         .to_string().into_bytes();
   1935         assert_eq!(
   1936             serde_json::from_str::<Registration>(
   1937                 serde_json::json!({
   1938                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1939                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1940                     "response": {
   1941                         "clientDataJSON": b64_cdata_json,
   1942                         "authenticatorData": base64url_nopad::encode(bad_auth.as_slice()),
   1943                         "transports": [],
   1944                         "publicKey": b64_key,
   1945                         "publicKeyAlgorithm": -8i8,
   1946                         "attestationObject": b64_aobj,
   1947                     },
   1948                     "clientExtensionResults": {},
   1949                     "type": "public-key"
   1950                 })
   1951                 .to_string()
   1952                 .as_str()
   1953             )
   1954             .unwrap_err()
   1955             .to_string()
   1956             .into_bytes()
   1957             .get(..err.len()),
   1958             Some(err.as_slice())
   1959         );
   1960         // Missing `authenticatorData`.
   1961         err = Error::missing_field("authenticatorData")
   1962             .to_string()
   1963             .into_bytes();
   1964         assert_eq!(
   1965             serde_json::from_str::<Registration>(
   1966                 serde_json::json!({
   1967                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1968                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1969                     "response": {
   1970                         "clientDataJSON": b64_cdata_json,
   1971                         "transports": [],
   1972                         "publicKey": b64_key,
   1973                         "publicKeyAlgorithm": -8i8,
   1974                         "attestationObject": b64_aobj,
   1975                     },
   1976                     "clientExtensionResults": {},
   1977                     "type": "public-key"
   1978                 })
   1979                 .to_string()
   1980                 .as_str()
   1981             )
   1982             .unwrap_err()
   1983             .to_string()
   1984             .into_bytes()
   1985             .get(..err.len()),
   1986             Some(err.as_slice())
   1987         );
   1988         // `null` `authenticatorData`.
   1989         err = Error::invalid_type(Unexpected::Other("null"), &"authenticatorData")
   1990             .to_string()
   1991             .into_bytes();
   1992         assert_eq!(
   1993             serde_json::from_str::<Registration>(
   1994                 serde_json::json!({
   1995                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   1996                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   1997                     "response": {
   1998                         "clientDataJSON": b64_cdata_json,
   1999                         "transports": [],
   2000                         "authenticatorData": null,
   2001                         "publicKey": b64_key,
   2002                         "publicKeyAlgorithm": -8i8,
   2003                         "attestationObject": b64_aobj,
   2004                     },
   2005                     "clientExtensionResults": {},
   2006                     "type": "public-key"
   2007                 })
   2008                 .to_string()
   2009                 .as_str()
   2010             )
   2011             .unwrap_err()
   2012             .to_string()
   2013             .into_bytes()
   2014             .get(..err.len()),
   2015             Some(err.as_slice())
   2016         );
   2017         // `publicKeyAlgorithm` mismatch.
   2018         err = Error::invalid_value(
   2019             Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()),
   2020             &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Eddsa).as_str()
   2021         )
   2022         .to_string().into_bytes();
   2023         assert_eq!(
   2024             serde_json::from_str::<Registration>(
   2025                 serde_json::json!({
   2026                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2027                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2028                     "response": {
   2029                         "clientDataJSON": b64_cdata_json,
   2030                         "authenticatorData": b64_adata,
   2031                         "transports": [],
   2032                         "publicKey": b64_key,
   2033                         "publicKeyAlgorithm": -7i8,
   2034                         "attestationObject": b64_aobj,
   2035                     },
   2036                     "clientExtensionResults": {},
   2037                     "type": "public-key"
   2038                 })
   2039                 .to_string()
   2040                 .as_str()
   2041             )
   2042             .unwrap_err()
   2043             .to_string()
   2044             .into_bytes()
   2045             .get(..err.len()),
   2046             Some(err.as_slice())
   2047         );
   2048         // Missing `publicKeyAlgorithm`.
   2049         err = Error::missing_field("publicKeyAlgorithm")
   2050             .to_string()
   2051             .into_bytes();
   2052         assert_eq!(
   2053             serde_json::from_str::<Registration>(
   2054                 serde_json::json!({
   2055                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2056                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2057                     "response": {
   2058                         "clientDataJSON": b64_cdata_json,
   2059                         "authenticatorData": b64_adata,
   2060                         "transports": [],
   2061                         "publicKey": b64_key,
   2062                         "attestationObject": b64_aobj,
   2063                     },
   2064                     "clientExtensionResults": {},
   2065                     "type": "public-key"
   2066                 })
   2067                 .to_string()
   2068                 .as_str()
   2069             )
   2070             .unwrap_err()
   2071             .to_string()
   2072             .into_bytes()
   2073             .get(..err.len()),
   2074             Some(err.as_slice())
   2075         );
   2076         // `null` `publicKeyAlgorithm`.
   2077         err = Error::invalid_type(Unexpected::Other("null"), &"publicKeyAlgorithm")
   2078             .to_string()
   2079             .into_bytes();
   2080         assert_eq!(
   2081             serde_json::from_str::<Registration>(
   2082                 serde_json::json!({
   2083                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2084                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2085                     "response": {
   2086                         "clientDataJSON": b64_cdata_json,
   2087                         "authenticatorData": b64_adata,
   2088                         "transports": [],
   2089                         "publicKey": b64_key,
   2090                         "publicKeyAlgorithm": null,
   2091                         "attestationObject": b64_aobj,
   2092                     },
   2093                     "clientExtensionResults": {},
   2094                     "type": "public-key"
   2095                 })
   2096                 .to_string()
   2097                 .as_str()
   2098             )
   2099             .unwrap_err()
   2100             .to_string()
   2101             .into_bytes()
   2102             .get(..err.len()),
   2103             Some(err.as_slice())
   2104         );
   2105         // `publicKey` mismatch.
   2106         err = Error::invalid_value(
   2107             Unexpected::Bytes([0; 32].as_slice()),
   2108             &format!(
   2109                 "DER-encoded public key to match the public key within the attestation object: Ed25519(Ed25519PubKey({:?}))",
   2110                 &att_obj[att_obj.len() - 32..],
   2111             )
   2112             .as_str(),
   2113         )
   2114         .to_string().into_bytes();
   2115         assert_eq!(serde_json::from_str::<Registration>(
   2116             serde_json::json!({
   2117                 "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2118                 "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2119                 "response": {
   2120                     "clientDataJSON": b64_cdata_json,
   2121                     "authenticatorData": b64_adata,
   2122                     "transports": [],
   2123                     "publicKey": base64url_nopad::encode(VerifyingKey::from_bytes(&[0; 32]).unwrap().to_public_key_der().unwrap().as_bytes()),
   2124                     "publicKeyAlgorithm": -8i8,
   2125                     "attestationObject": b64_aobj,
   2126                 },
   2127                 "clientExtensionResults": {},
   2128                 "type": "public-key"
   2129             })
   2130             .to_string()
   2131             .as_str()
   2132             )
   2133             .unwrap_err().to_string().into_bytes().get(..err.len()),
   2134             Some(err.as_slice())
   2135         );
   2136         // Missing `publicKey` when using EdDSA, ES256, or RS256.
   2137         err = Error::missing_field("publicKey").to_string().into_bytes();
   2138         assert_eq!(
   2139             serde_json::from_str::<Registration>(
   2140                 serde_json::json!({
   2141                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2142                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2143                     "response": {
   2144                         "clientDataJSON": b64_cdata_json,
   2145                         "authenticatorData": b64_adata,
   2146                         "transports": [],
   2147                         "publicKeyAlgorithm": -8i8,
   2148                         "attestationObject": b64_aobj,
   2149                     },
   2150                     "clientExtensionResults": {},
   2151                     "type": "public-key"
   2152                 })
   2153                 .to_string()
   2154                 .as_str()
   2155             )
   2156             .unwrap_err()
   2157             .to_string()
   2158             .into_bytes()
   2159             .get(..err.len()),
   2160             Some(err.as_slice())
   2161         );
   2162         // `null` `publicKey` when using EdDSA, ES256, or RS256.
   2163         err = Error::invalid_type(Unexpected::Other("null"), &"publicKey")
   2164             .to_string()
   2165             .into_bytes();
   2166         assert_eq!(
   2167             serde_json::from_str::<Registration>(
   2168                 serde_json::json!({
   2169                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2170                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2171                     "response": {
   2172                         "clientDataJSON": b64_cdata_json,
   2173                         "authenticatorData": b64_adata,
   2174                         "transports": [],
   2175                         "publicKey": null,
   2176                         "publicKeyAlgorithm": -8i8,
   2177                         "attestationObject": b64_aobj,
   2178                     },
   2179                     "clientExtensionResults": {},
   2180                     "type": "public-key"
   2181                 })
   2182                 .to_string()
   2183                 .as_str()
   2184             )
   2185             .unwrap_err()
   2186             .to_string()
   2187             .into_bytes()
   2188             .get(..err.len()),
   2189             Some(err.as_slice())
   2190         );
   2191         // Missing `transports`.
   2192         err = Error::missing_field("transports").to_string().into_bytes();
   2193         assert_eq!(
   2194             serde_json::from_str::<Registration>(
   2195                 serde_json::json!({
   2196                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2197                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2198                     "response": {
   2199                         "clientDataJSON": b64_cdata_json,
   2200                         "authenticatorData": b64_adata,
   2201                         "publicKey": b64_key,
   2202                         "publicKeyAlgorithm": -8i8,
   2203                         "attestationObject": b64_aobj,
   2204                     },
   2205                     "clientExtensionResults": {},
   2206                     "type": "public-key"
   2207                 })
   2208                 .to_string()
   2209                 .as_str()
   2210             )
   2211             .unwrap_err()
   2212             .to_string()
   2213             .into_bytes()
   2214             .get(..err.len()),
   2215             Some(err.as_slice())
   2216         );
   2217         // Duplicate `transports` are allowed.
   2218         assert!(
   2219             serde_json::from_str::<Registration>(
   2220                 serde_json::json!({
   2221                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2222                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2223                     "response": {
   2224                         "clientDataJSON": b64_cdata_json,
   2225                         "authenticatorData": b64_adata,
   2226                         "transports": ["usb", "usb"],
   2227                         "publicKey": b64_key,
   2228                         "publicKeyAlgorithm": -8i8,
   2229                         "attestationObject": b64_aobj,
   2230                     },
   2231                     "clientExtensionResults": {},
   2232                     "type": "public-key"
   2233                 })
   2234                 .to_string()
   2235                 .as_str()
   2236             )
   2237             .is_ok_and(|reg| reg.response.transports.count() == 1)
   2238         );
   2239         // `null` `transports`.
   2240         err = Error::invalid_type(Unexpected::Other("null"), &"transports")
   2241             .to_string()
   2242             .into_bytes();
   2243         assert_eq!(
   2244             serde_json::from_str::<Registration>(
   2245                 serde_json::json!({
   2246                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2247                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2248                     "response": {
   2249                         "clientDataJSON": b64_cdata_json,
   2250                         "authenticatorData": b64_adata,
   2251                         "transports": null,
   2252                         "publicKey": b64_key,
   2253                         "publicKeyAlgorithm": -8i8,
   2254                         "attestationObject": b64_aobj,
   2255                     },
   2256                     "clientExtensionResults": {},
   2257                     "type": "public-key"
   2258                 })
   2259                 .to_string()
   2260                 .as_str()
   2261             )
   2262             .unwrap_err()
   2263             .to_string()
   2264             .into_bytes()
   2265             .get(..err.len()),
   2266             Some(err.as_slice())
   2267         );
   2268         // Unknown `transports`.
   2269         err = Error::invalid_value(
   2270             Unexpected::Str("Usb"),
   2271             &"'ble', 'cable', 'hybrid', 'internal', 'nfc', 'smart-card', or 'usb'",
   2272         )
   2273         .to_string()
   2274         .into_bytes();
   2275         assert_eq!(
   2276             serde_json::from_str::<Registration>(
   2277                 serde_json::json!({
   2278                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2279                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2280                     "response": {
   2281                         "clientDataJSON": b64_cdata_json,
   2282                         "authenticatorData": b64_adata,
   2283                         "transports": ["Usb"],
   2284                         "publicKey": b64_key,
   2285                         "publicKeyAlgorithm": -8i8,
   2286                         "attestationObject": b64_aobj,
   2287                     },
   2288                     "clientExtensionResults": {},
   2289                     "type": "public-key"
   2290                 })
   2291                 .to_string()
   2292                 .as_str()
   2293             )
   2294             .unwrap_err()
   2295             .to_string()
   2296             .into_bytes()
   2297             .get(..err.len()),
   2298             Some(err.as_slice())
   2299         );
   2300         // `null` `authenticatorAttachment`.
   2301         assert!(
   2302             serde_json::from_str::<Registration>(
   2303                 serde_json::json!({
   2304                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2305                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2306                     "response": {
   2307                         "clientDataJSON": b64_cdata_json,
   2308                         "authenticatorData": b64_adata,
   2309                         "transports": [],
   2310                         "publicKey": b64_key,
   2311                         "publicKeyAlgorithm": -8i8,
   2312                         "attestationObject": b64_aobj,
   2313                     },
   2314                     "authenticatorAttachment": null,
   2315                     "clientExtensionResults": {},
   2316                     "type": "public-key"
   2317                 })
   2318                 .to_string()
   2319                 .as_str()
   2320             )
   2321             .is_ok_and(|reg| matches!(reg.authenticator_attachment, AuthenticatorAttachment::None))
   2322         );
   2323         // Unknown `authenticatorAttachment`.
   2324         err = Error::invalid_value(
   2325             Unexpected::Str("Platform"),
   2326             &"'platform' or 'cross-platform'",
   2327         )
   2328         .to_string()
   2329         .into_bytes();
   2330         assert_eq!(
   2331             serde_json::from_str::<Registration>(
   2332                 serde_json::json!({
   2333                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2334                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2335                     "response": {
   2336                         "clientDataJSON": b64_cdata_json,
   2337                         "authenticatorData": b64_adata,
   2338                         "transports": [],
   2339                         "publicKey": b64_key,
   2340                         "publicKeyAlgorithm": -8i8,
   2341                         "attestationObject": b64_aobj,
   2342                     },
   2343                     "authenticatorAttachment": "Platform",
   2344                     "clientExtensionResults": {},
   2345                     "type": "public-key"
   2346                 })
   2347                 .to_string()
   2348                 .as_str()
   2349             )
   2350             .unwrap_err()
   2351             .to_string()
   2352             .into_bytes()
   2353             .get(..err.len()),
   2354             Some(err.as_slice())
   2355         );
   2356         // Missing `clientDataJSON`.
   2357         err = Error::missing_field("clientDataJSON")
   2358             .to_string()
   2359             .into_bytes();
   2360         assert_eq!(
   2361             serde_json::from_str::<Registration>(
   2362                 serde_json::json!({
   2363                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2364                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2365                     "response": {
   2366                         "authenticatorData": b64_adata,
   2367                         "transports": [],
   2368                         "publicKey": b64_key,
   2369                         "publicKeyAlgorithm": -8i8,
   2370                         "attestationObject": b64_aobj,
   2371                     },
   2372                     "clientExtensionResults": {},
   2373                     "type": "public-key"
   2374                 })
   2375                 .to_string()
   2376                 .as_str()
   2377             )
   2378             .unwrap_err()
   2379             .to_string()
   2380             .into_bytes()
   2381             .get(..err.len()),
   2382             Some(err.as_slice())
   2383         );
   2384         // `null` `clientDataJSON`.
   2385         err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data")
   2386             .to_string()
   2387             .into_bytes();
   2388         assert_eq!(
   2389             serde_json::from_str::<Registration>(
   2390                 serde_json::json!({
   2391                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2392                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2393                     "response": {
   2394                         "clientDataJSON": null,
   2395                         "authenticatorData": b64_adata,
   2396                         "transports": [],
   2397                         "publicKey": b64_key,
   2398                         "publicKeyAlgorithm": -8i8,
   2399                         "attestationObject": b64_aobj,
   2400                     },
   2401                     "clientExtensionResults": {},
   2402                     "type": "public-key"
   2403                 })
   2404                 .to_string()
   2405                 .as_str()
   2406             )
   2407             .unwrap_err()
   2408             .to_string()
   2409             .into_bytes()
   2410             .get(..err.len()),
   2411             Some(err.as_slice())
   2412         );
   2413         // Missing `attestationObject`.
   2414         err = Error::missing_field("attestationObject")
   2415             .to_string()
   2416             .into_bytes();
   2417         assert_eq!(
   2418             serde_json::from_str::<Registration>(
   2419                 serde_json::json!({
   2420                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2421                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2422                     "response": {
   2423                         "clientDataJSON": b64_cdata_json,
   2424                         "authenticatorData": b64_adata,
   2425                         "transports": [],
   2426                         "publicKey": b64_key,
   2427                         "publicKeyAlgorithm": -8i8,
   2428                     },
   2429                     "clientExtensionResults": {},
   2430                     "type": "public-key"
   2431                 })
   2432                 .to_string()
   2433                 .as_str()
   2434             )
   2435             .unwrap_err()
   2436             .to_string()
   2437             .into_bytes()
   2438             .get(..err.len()),
   2439             Some(err.as_slice())
   2440         );
   2441         // `null` `attestationObject`.
   2442         err = Error::invalid_type(
   2443             Unexpected::Other("null"),
   2444             &"base64url-encoded attestation object",
   2445         )
   2446         .to_string()
   2447         .into_bytes();
   2448         assert_eq!(
   2449             serde_json::from_str::<Registration>(
   2450                 serde_json::json!({
   2451                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2452                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2453                     "response": {
   2454                         "clientDataJSON": b64_cdata_json,
   2455                         "authenticatorData": b64_adata,
   2456                         "transports": [],
   2457                         "publicKey": b64_key,
   2458                         "publicKeyAlgorithm": -8i8,
   2459                         "attestationObject": null,
   2460                     },
   2461                     "clientExtensionResults": {},
   2462                     "type": "public-key"
   2463                 })
   2464                 .to_string()
   2465                 .as_str()
   2466             )
   2467             .unwrap_err()
   2468             .to_string()
   2469             .into_bytes()
   2470             .get(..err.len()),
   2471             Some(err.as_slice())
   2472         );
   2473         // Missing `response`.
   2474         err = Error::missing_field("response").to_string().into_bytes();
   2475         assert_eq!(
   2476             serde_json::from_str::<Registration>(
   2477                 serde_json::json!({
   2478                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2479                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2480                     "clientExtensionResults": {},
   2481                     "type": "public-key"
   2482                 })
   2483                 .to_string()
   2484                 .as_str()
   2485             )
   2486             .unwrap_err()
   2487             .to_string()
   2488             .into_bytes()
   2489             .get(..err.len()),
   2490             Some(err.as_slice())
   2491         );
   2492         // `null` `response`.
   2493         err = Error::invalid_type(Unexpected::Other("null"), &"AuthenticatorAttestation")
   2494             .to_string()
   2495             .into_bytes();
   2496         assert_eq!(
   2497             serde_json::from_str::<Registration>(
   2498                 serde_json::json!({
   2499                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2500                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2501                     "response": null,
   2502                     "clientExtensionResults": {},
   2503                     "type": "public-key"
   2504                 })
   2505                 .to_string()
   2506                 .as_str()
   2507             )
   2508             .unwrap_err()
   2509             .to_string()
   2510             .into_bytes()
   2511             .get(..err.len()),
   2512             Some(err.as_slice())
   2513         );
   2514         // Empty `response`.
   2515         err = Error::missing_field("clientDataJSON")
   2516             .to_string()
   2517             .into_bytes();
   2518         assert_eq!(
   2519             serde_json::from_str::<Registration>(
   2520                 serde_json::json!({
   2521                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2522                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2523                     "response": {},
   2524                     "clientExtensionResults": {},
   2525                     "type": "public-key"
   2526                 })
   2527                 .to_string()
   2528                 .as_str()
   2529             )
   2530             .unwrap_err()
   2531             .to_string()
   2532             .into_bytes()
   2533             .get(..err.len()),
   2534             Some(err.as_slice())
   2535         );
   2536         // Missing `clientExtensionResults`.
   2537         err = Error::missing_field("clientExtensionResults")
   2538             .to_string()
   2539             .into_bytes();
   2540         assert_eq!(
   2541             serde_json::from_str::<Registration>(
   2542                 serde_json::json!({
   2543                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2544                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2545                     "response": {
   2546                         "clientDataJSON": b64_cdata_json,
   2547                         "authenticatorData": b64_adata,
   2548                         "transports": [],
   2549                         "publicKey": b64_key,
   2550                         "publicKeyAlgorithm": -8i8,
   2551                         "attestationObject": b64_aobj,
   2552                     },
   2553                     "type": "public-key"
   2554                 })
   2555                 .to_string()
   2556                 .as_str()
   2557             )
   2558             .unwrap_err()
   2559             .to_string()
   2560             .into_bytes()
   2561             .get(..err.len()),
   2562             Some(err.as_slice())
   2563         );
   2564         // `null` `clientExtensionResults`.
   2565         err = Error::invalid_type(
   2566             Unexpected::Other("null"),
   2567             &"clientExtensionResults to be a map of allowed client extensions",
   2568         )
   2569         .to_string()
   2570         .into_bytes();
   2571         assert_eq!(
   2572             serde_json::from_str::<Registration>(
   2573                 serde_json::json!({
   2574                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2575                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2576                     "response": {
   2577                         "clientDataJSON": b64_cdata_json,
   2578                         "authenticatorData": b64_adata,
   2579                         "transports": [],
   2580                         "publicKey": b64_key,
   2581                         "publicKeyAlgorithm": -8i8,
   2582                         "attestationObject": b64_aobj,
   2583                     },
   2584                     "clientExtensionResults": null,
   2585                     "type": "public-key"
   2586                 })
   2587                 .to_string()
   2588                 .as_str()
   2589             )
   2590             .unwrap_err()
   2591             .to_string()
   2592             .into_bytes()
   2593             .get(..err.len()),
   2594             Some(err.as_slice())
   2595         );
   2596         // Missing `type`.
   2597         err = Error::missing_field("type").to_string().into_bytes();
   2598         assert_eq!(
   2599             serde_json::from_str::<Registration>(
   2600                 serde_json::json!({
   2601                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2602                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2603                     "response": {
   2604                         "clientDataJSON": b64_cdata_json,
   2605                         "authenticatorData": b64_adata,
   2606                         "transports": [],
   2607                         "publicKey": b64_key,
   2608                         "publicKeyAlgorithm": -8i8,
   2609                         "attestationObject": b64_aobj,
   2610                     },
   2611                     "clientExtensionResults": {},
   2612                 })
   2613                 .to_string()
   2614                 .as_str()
   2615             )
   2616             .unwrap_err()
   2617             .to_string()
   2618             .into_bytes()
   2619             .get(..err.len()),
   2620             Some(err.as_slice())
   2621         );
   2622         // `null` `type`.
   2623         err = Error::invalid_type(Unexpected::Other("null"), &"public-key")
   2624             .to_string()
   2625             .into_bytes();
   2626         assert_eq!(
   2627             serde_json::from_str::<Registration>(
   2628                 serde_json::json!({
   2629                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2630                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2631                     "response": {
   2632                         "clientDataJSON": b64_cdata_json,
   2633                         "authenticatorData": b64_adata,
   2634                         "transports": [],
   2635                         "publicKey": b64_key,
   2636                         "publicKeyAlgorithm": -8i8,
   2637                         "attestationObject": b64_aobj,
   2638                     },
   2639                     "clientExtensionResults": {},
   2640                     "type": null
   2641                 })
   2642                 .to_string()
   2643                 .as_str()
   2644             )
   2645             .unwrap_err()
   2646             .to_string()
   2647             .into_bytes()
   2648             .get(..err.len()),
   2649             Some(err.as_slice())
   2650         );
   2651         // Not exactly `public-type` `type`.
   2652         err = Error::invalid_value(Unexpected::Str("Public-key"), &"public-key")
   2653             .to_string()
   2654             .into_bytes();
   2655         assert_eq!(
   2656             serde_json::from_str::<Registration>(
   2657                 serde_json::json!({
   2658                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2659                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2660                     "response": {
   2661                         "clientDataJSON": b64_cdata_json,
   2662                         "authenticatorData": b64_adata,
   2663                         "transports": [],
   2664                         "publicKey": b64_key,
   2665                         "publicKeyAlgorithm": -8i8,
   2666                         "attestationObject": b64_aobj,
   2667                     },
   2668                     "clientExtensionResults": {},
   2669                     "type": "Public-key"
   2670                 })
   2671                 .to_string()
   2672                 .as_str()
   2673             )
   2674             .unwrap_err()
   2675             .to_string()
   2676             .into_bytes()
   2677             .get(..err.len()),
   2678             Some(err.as_slice())
   2679         );
   2680         // `null`.
   2681         err = Error::invalid_type(Unexpected::Other("null"), &"PublicKeyCredential")
   2682             .to_string()
   2683             .into_bytes();
   2684         assert_eq!(
   2685             serde_json::from_str::<Registration>(serde_json::json!(null).to_string().as_str())
   2686                 .unwrap_err()
   2687                 .to_string()
   2688                 .into_bytes()
   2689                 .get(..err.len()),
   2690             Some(err.as_slice())
   2691         );
   2692         // Empty.
   2693         err = Error::missing_field("response").to_string().into_bytes();
   2694         assert_eq!(
   2695             serde_json::from_str::<Registration>(serde_json::json!({}).to_string().as_str())
   2696                 .unwrap_err()
   2697                 .to_string()
   2698                 .into_bytes()
   2699                 .get(..err.len()),
   2700             Some(err.as_slice())
   2701         );
   2702         // Unknown field in `response`.
   2703         err = Error::unknown_field(
   2704             "foo",
   2705             [
   2706                 "clientDataJSON",
   2707                 "attestationObject",
   2708                 "authenticatorData",
   2709                 "transports",
   2710                 "publicKey",
   2711                 "publicKeyAlgorithm",
   2712             ]
   2713             .as_slice(),
   2714         )
   2715         .to_string()
   2716         .into_bytes();
   2717         assert_eq!(
   2718             serde_json::from_str::<Registration>(
   2719                 serde_json::json!({
   2720                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2721                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2722                     "response": {
   2723                         "clientDataJSON": b64_cdata_json,
   2724                         "authenticatorData": b64_adata,
   2725                         "transports": [],
   2726                         "publicKey": b64_key,
   2727                         "publicKeyAlgorithm": -8i8,
   2728                         "attestationObject": b64_aobj,
   2729                         "foo": true,
   2730                     },
   2731                     "clientExtensionResults": {},
   2732                     "type": "public-key"
   2733                 })
   2734                 .to_string()
   2735                 .as_str()
   2736             )
   2737             .unwrap_err()
   2738             .to_string()
   2739             .into_bytes()
   2740             .get(..err.len()),
   2741             Some(err.as_slice())
   2742         );
   2743         // Duplicate field in `response`.
   2744         err = Error::duplicate_field("transports")
   2745             .to_string()
   2746             .into_bytes();
   2747         assert_eq!(
   2748             serde_json::from_str::<Registration>(
   2749                 format!(
   2750                     "{{
   2751                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   2752                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   2753                        \"response\": {{
   2754                            \"clientDataJSON\": \"{b64_cdata_json}\",
   2755                            \"authenticatorData\": \"{b64_adata}\",
   2756                            \"transports\": [],
   2757                            \"publicKey\": \"{b64_key}\",
   2758                            \"publicKeyAlgorithm\": -8,
   2759                            \"attestationObject\": \"{b64_aobj}\",
   2760                            \"transports\": []
   2761                        }},
   2762                        \"clientExtensionResults\": {{}},
   2763                        \"type\": \"public-key\"
   2764                             
   2765                      }}"
   2766                 )
   2767                 .as_str()
   2768             )
   2769             .unwrap_err()
   2770             .to_string()
   2771             .into_bytes()
   2772             .get(..err.len()),
   2773             Some(err.as_slice())
   2774         );
   2775         // Unknown field in `PublicKeyCredential`.
   2776         err = Error::unknown_field(
   2777             "foo",
   2778             [
   2779                 "id",
   2780                 "type",
   2781                 "rawId",
   2782                 "response",
   2783                 "authenticatorAttachment",
   2784                 "clientExtensionResults",
   2785             ]
   2786             .as_slice(),
   2787         )
   2788         .to_string()
   2789         .into_bytes();
   2790         assert_eq!(
   2791             serde_json::from_str::<Registration>(
   2792                 serde_json::json!({
   2793                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   2794                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   2795                     "response": {
   2796                         "clientDataJSON": b64_cdata_json,
   2797                         "authenticatorData": b64_adata,
   2798                         "transports": [],
   2799                         "publicKey": b64_key,
   2800                         "publicKeyAlgorithm": -8i8,
   2801                         "attestationObject": b64_aobj
   2802                     },
   2803                     "clientExtensionResults": {},
   2804                     "type": "public-key",
   2805                     "foo": true,
   2806                 })
   2807                 .to_string()
   2808                 .as_str()
   2809             )
   2810             .unwrap_err()
   2811             .to_string()
   2812             .into_bytes()
   2813             .get(..err.len()),
   2814             Some(err.as_slice())
   2815         );
   2816         // Duplicate field in `PublicKeyCredential`.
   2817         err = Error::duplicate_field("id").to_string().into_bytes();
   2818         assert_eq!(
   2819             serde_json::from_str::<Registration>(
   2820                 format!(
   2821                     "{{
   2822                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   2823                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   2824                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   2825                        \"response\": {{
   2826                            \"clientDataJSON\": \"{b64_cdata_json}\",
   2827                            \"authenticatorData\": \"{b64_adata}\",
   2828                            \"transports\": [],
   2829                            \"publicKey\": \"{b64_key}\",
   2830                            \"publicKeyAlgorithm\": -8,
   2831                            \"attestationObject\": \"{b64_aobj}\"
   2832                        }},
   2833                        \"clientExtensionResults\": {{}},
   2834                        \"type\": \"public-key\"
   2835                             
   2836                      }}"
   2837                 )
   2838                 .as_str()
   2839             )
   2840             .unwrap_err()
   2841             .to_string()
   2842             .into_bytes()
   2843             .get(..err.len()),
   2844             Some(err.as_slice())
   2845         );
   2846     }
   2847     #[expect(clippy::unwrap_used, reason = "OK in tests")]
   2848     #[expect(clippy::indexing_slicing, reason = "comments justify correctness")]
   2849     #[expect(
   2850         clippy::cognitive_complexity,
   2851         clippy::too_many_lines,
   2852         reason = "a lot to test"
   2853     )]
   2854     #[test]
   2855     fn client_extensions() {
   2856         let c_data_json = serde_json::json!({}).to_string();
   2857         let att_obj: [u8; 143] = [
   2858             cbor::MAP_3,
   2859             cbor::TEXT_3,
   2860             b'f',
   2861             b'm',
   2862             b't',
   2863             cbor::TEXT_4,
   2864             b'n',
   2865             b'o',
   2866             b'n',
   2867             b'e',
   2868             cbor::TEXT_7,
   2869             b'a',
   2870             b't',
   2871             b't',
   2872             b'S',
   2873             b't',
   2874             b'm',
   2875             b't',
   2876             cbor::MAP_0,
   2877             cbor::TEXT_8,
   2878             b'a',
   2879             b'u',
   2880             b't',
   2881             b'h',
   2882             b'D',
   2883             b'a',
   2884             b't',
   2885             b'a',
   2886             cbor::BYTES_INFO_24,
   2887             113,
   2888             // `rpIdHash`.
   2889             0,
   2890             0,
   2891             0,
   2892             0,
   2893             0,
   2894             0,
   2895             0,
   2896             0,
   2897             0,
   2898             0,
   2899             0,
   2900             0,
   2901             0,
   2902             0,
   2903             0,
   2904             0,
   2905             0,
   2906             0,
   2907             0,
   2908             0,
   2909             0,
   2910             0,
   2911             0,
   2912             0,
   2913             0,
   2914             0,
   2915             0,
   2916             0,
   2917             0,
   2918             0,
   2919             0,
   2920             0,
   2921             // `flags`.
   2922             0b0100_0101,
   2923             // `signCount`.
   2924             0,
   2925             0,
   2926             0,
   2927             0,
   2928             // `aaguid`.
   2929             0,
   2930             0,
   2931             0,
   2932             0,
   2933             0,
   2934             0,
   2935             0,
   2936             0,
   2937             0,
   2938             0,
   2939             0,
   2940             0,
   2941             0,
   2942             0,
   2943             0,
   2944             0,
   2945             // `credentialIdLength`.
   2946             0,
   2947             16,
   2948             // `credentialId`.
   2949             0,
   2950             0,
   2951             0,
   2952             0,
   2953             0,
   2954             0,
   2955             0,
   2956             0,
   2957             0,
   2958             0,
   2959             0,
   2960             0,
   2961             0,
   2962             0,
   2963             0,
   2964             0,
   2965             // Ed25519 COSE key.
   2966             cbor::MAP_4,
   2967             KTY,
   2968             OKP,
   2969             ALG,
   2970             EDDSA,
   2971             // `crv`.
   2972             cbor::NEG_ONE,
   2973             // `Ed25519`.
   2974             cbor::SIX,
   2975             // `x`.
   2976             cbor::NEG_TWO,
   2977             cbor::BYTES_INFO_24,
   2978             32,
   2979             // Compressed y-coordinate.
   2980             1,
   2981             1,
   2982             1,
   2983             1,
   2984             1,
   2985             1,
   2986             1,
   2987             1,
   2988             1,
   2989             1,
   2990             1,
   2991             1,
   2992             1,
   2993             1,
   2994             1,
   2995             1,
   2996             1,
   2997             1,
   2998             1,
   2999             1,
   3000             1,
   3001             1,
   3002             1,
   3003             1,
   3004             1,
   3005             1,
   3006             1,
   3007             1,
   3008             1,
   3009             1,
   3010             1,
   3011             1,
   3012         ];
   3013         let pub_key = VerifyingKey::from_bytes(&[1; 32])
   3014             .unwrap()
   3015             .to_public_key_der()
   3016             .unwrap();
   3017         let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes());
   3018         let b64_adata = base64url_nopad::encode(&att_obj[att_obj.len() - 113..]);
   3019         let b64_key = base64url_nopad::encode(pub_key.as_bytes());
   3020         let b64_aobj = base64url_nopad::encode(att_obj.as_slice());
   3021         // Base case is valid.
   3022         assert!(
   3023             serde_json::from_str::<Registration>(
   3024                 serde_json::json!({
   3025                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3026                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3027                     "response": {
   3028                         "clientDataJSON": b64_cdata_json,
   3029                         "authenticatorData": b64_adata,
   3030                         "transports": [],
   3031                         "publicKey": b64_key,
   3032                         "publicKeyAlgorithm": -8i8,
   3033                         "attestationObject": b64_aobj,
   3034                     },
   3035                     "clientExtensionResults": {},
   3036                     "type": "public-key"
   3037                 })
   3038                 .to_string()
   3039                 .as_str()
   3040             )
   3041             .is_ok_and(
   3042                 |reg| reg.response.client_data_json == c_data_json.as_bytes()
   3043                     && reg.response.attestation_object_and_c_data_hash[..att_obj.len()] == att_obj
   3044                     && reg.response.attestation_object_and_c_data_hash[att_obj.len()..]
   3045                         == *Sha256::digest(c_data_json.as_bytes())
   3046                     && reg.response.transports.is_empty()
   3047                     && matches!(reg.authenticator_attachment, AuthenticatorAttachment::None)
   3048                     && reg.client_extension_results.cred_props.is_none()
   3049                     && reg.client_extension_results.prf.is_none()
   3050             )
   3051         );
   3052         // `null` `credProps`.
   3053         assert!(
   3054             serde_json::from_str::<Registration>(
   3055                 serde_json::json!({
   3056                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3057                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3058                     "response": {
   3059                         "clientDataJSON": b64_cdata_json,
   3060                         "authenticatorData": b64_adata,
   3061                         "transports": [],
   3062                         "publicKey": b64_key,
   3063                         "publicKeyAlgorithm": -8i8,
   3064                         "attestationObject": b64_aobj,
   3065                     },
   3066                     "clientExtensionResults": {
   3067                         "credProps": null
   3068                     },
   3069                     "type": "public-key"
   3070                 })
   3071                 .to_string()
   3072                 .as_str()
   3073             )
   3074             .is_ok_and(|reg| reg.client_extension_results.cred_props.is_none()
   3075                 && reg.client_extension_results.prf.is_none())
   3076         );
   3077         // `null` `prf`.
   3078         assert!(
   3079             serde_json::from_str::<Registration>(
   3080                 serde_json::json!({
   3081                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3082                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3083                     "response": {
   3084                         "clientDataJSON": b64_cdata_json,
   3085                         "authenticatorData": b64_adata,
   3086                         "transports": [],
   3087                         "publicKey": b64_key,
   3088                         "publicKeyAlgorithm": -8i8,
   3089                         "attestationObject": b64_aobj,
   3090                     },
   3091                     "clientExtensionResults": {
   3092                         "prf": null
   3093                     },
   3094                     "type": "public-key"
   3095                 })
   3096                 .to_string()
   3097                 .as_str()
   3098             )
   3099             .is_ok_and(|reg| reg.client_extension_results.cred_props.is_none()
   3100                 && reg.client_extension_results.prf.is_none())
   3101         );
   3102         // Unknown `clientExtensionResults`.
   3103         let mut err = Error::unknown_field("CredProps", ["credProps", "prf"].as_slice())
   3104             .to_string()
   3105             .into_bytes();
   3106         assert_eq!(
   3107             serde_json::from_str::<Registration>(
   3108                 serde_json::json!({
   3109                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3110                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3111                     "response": {
   3112                         "clientDataJSON": b64_cdata_json,
   3113                         "authenticatorData": b64_adata,
   3114                         "transports": [],
   3115                         "publicKey": b64_key,
   3116                         "publicKeyAlgorithm": -8i8,
   3117                         "attestationObject": b64_aobj,
   3118                     },
   3119                     "clientExtensionResults": {
   3120                         "CredProps": {
   3121                             "rk": true
   3122                         }
   3123                     },
   3124                     "type": "public-key"
   3125                 })
   3126                 .to_string()
   3127                 .as_str()
   3128             )
   3129             .unwrap_err()
   3130             .to_string()
   3131             .into_bytes()
   3132             .get(..err.len()),
   3133             Some(err.as_slice())
   3134         );
   3135         // Duplicate field.
   3136         err = Error::duplicate_field("credProps").to_string().into_bytes();
   3137         assert_eq!(
   3138             serde_json::from_str::<Registration>(
   3139                 format!(
   3140                     "{{
   3141                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   3142                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   3143                        \"response\": {{
   3144                            \"clientDataJSON\": \"{b64_cdata_json}\",
   3145                            \"authenticatorData\": \"{b64_adata}\",
   3146                            \"transports\": [],
   3147                            \"publicKey\": \"{b64_key}\",
   3148                            \"publicKeyAlgorithm\": -8,
   3149                            \"attestationObject\": \"{b64_aobj}\"
   3150                        }},
   3151                        \"clientExtensionResults\": {{
   3152                            \"credProps\": null,
   3153                            \"credProps\": null
   3154                        }},
   3155                        \"type\": \"public-key\"
   3156                      }}"
   3157                 )
   3158                 .as_str()
   3159             )
   3160             .unwrap_err()
   3161             .to_string()
   3162             .into_bytes()
   3163             .get(..err.len()),
   3164             Some(err.as_slice())
   3165         );
   3166         // `null` `rk`.
   3167         assert!(
   3168             serde_json::from_str::<Registration>(
   3169                 serde_json::json!({
   3170                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3171                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3172                     "response": {
   3173                         "clientDataJSON": b64_cdata_json,
   3174                         "authenticatorData": b64_adata,
   3175                         "transports": [],
   3176                         "publicKey": b64_key,
   3177                         "publicKeyAlgorithm": -8i8,
   3178                         "attestationObject": b64_aobj,
   3179                     },
   3180                     "clientExtensionResults": {
   3181                         "credProps": {
   3182                             "rk": null
   3183                         }
   3184                     },
   3185                     "type": "public-key"
   3186                 })
   3187                 .to_string()
   3188                 .as_str()
   3189             )
   3190             .is_ok_and(|reg| reg
   3191                 .client_extension_results
   3192                 .cred_props
   3193                 .is_some_and(|props| props.rk.is_none())
   3194                 && reg.client_extension_results.prf.is_none())
   3195         );
   3196         // Missing `rk`.
   3197         assert!(
   3198             serde_json::from_str::<Registration>(
   3199                 serde_json::json!({
   3200                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3201                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3202                     "response": {
   3203                         "clientDataJSON": b64_cdata_json,
   3204                         "authenticatorData": b64_adata,
   3205                         "transports": [],
   3206                         "publicKey": b64_key,
   3207                         "publicKeyAlgorithm": -8i8,
   3208                         "attestationObject": b64_aobj,
   3209                     },
   3210                     "clientExtensionResults": {
   3211                         "credProps": {}
   3212                     },
   3213                     "type": "public-key"
   3214                 })
   3215                 .to_string()
   3216                 .as_str()
   3217             )
   3218             .is_ok_and(|reg| reg
   3219                 .client_extension_results
   3220                 .cred_props
   3221                 .is_some_and(|props| props.rk.is_none())
   3222                 && reg.client_extension_results.prf.is_none())
   3223         );
   3224         // `true` rk`.
   3225         assert!(
   3226             serde_json::from_str::<Registration>(
   3227                 serde_json::json!({
   3228                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3229                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3230                     "response": {
   3231                         "clientDataJSON": b64_cdata_json,
   3232                         "authenticatorData": b64_adata,
   3233                         "transports": [],
   3234                         "publicKey": b64_key,
   3235                         "publicKeyAlgorithm": -8i8,
   3236                         "attestationObject": b64_aobj,
   3237                     },
   3238                     "clientExtensionResults": {
   3239                         "credProps": {
   3240                             "rk": true
   3241                         }
   3242                     },
   3243                     "type": "public-key"
   3244                 })
   3245                 .to_string()
   3246                 .as_str()
   3247             )
   3248             .is_ok_and(|reg| reg
   3249                 .client_extension_results
   3250                 .cred_props
   3251                 .is_some_and(|props| props.rk.unwrap_or_default())
   3252                 && reg.client_extension_results.prf.is_none())
   3253         );
   3254         // `false` rk`.
   3255         assert!(
   3256             serde_json::from_str::<Registration>(
   3257                 serde_json::json!({
   3258                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3259                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3260                     "response": {
   3261                         "clientDataJSON": b64_cdata_json,
   3262                         "authenticatorData": b64_adata,
   3263                         "transports": [],
   3264                         "publicKey": b64_key,
   3265                         "publicKeyAlgorithm": -8i8,
   3266                         "attestationObject": b64_aobj,
   3267                     },
   3268                     "clientExtensionResults": {
   3269                         "credProps": {
   3270                             "rk": false
   3271                         }
   3272                     },
   3273                     "type": "public-key"
   3274                 })
   3275                 .to_string()
   3276                 .as_str()
   3277             )
   3278             .is_ok_and(|reg| reg
   3279                 .client_extension_results
   3280                 .cred_props
   3281                 .is_some_and(|props| props.rk.is_some_and(|rk| !rk))
   3282                 && reg.client_extension_results.prf.is_none())
   3283         );
   3284         // Invalid `rk`.
   3285         err = Error::invalid_type(Unexpected::Unsigned(3), &"a boolean")
   3286             .to_string()
   3287             .into_bytes();
   3288         assert_eq!(
   3289             serde_json::from_str::<Registration>(
   3290                 serde_json::json!({
   3291                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3292                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3293                     "response": {
   3294                         "clientDataJSON": b64_cdata_json,
   3295                         "authenticatorData": b64_adata,
   3296                         "transports": [],
   3297                         "publicKey": b64_key,
   3298                         "publicKeyAlgorithm": -8i8,
   3299                         "attestationObject": b64_aobj,
   3300                     },
   3301                     "clientExtensionResults": {
   3302                         "credProps": {
   3303                             "rk": 3u8
   3304                         }
   3305                     },
   3306                     "type": "public-key"
   3307                 })
   3308                 .to_string()
   3309                 .as_str()
   3310             )
   3311             .unwrap_err()
   3312             .to_string()
   3313             .into_bytes()
   3314             .get(..err.len()),
   3315             Some(err.as_slice())
   3316         );
   3317         // Unknown `credProps` field.
   3318         err = Error::unknown_field("Rk", ["rk"].as_slice())
   3319             .to_string()
   3320             .into_bytes();
   3321         assert_eq!(
   3322             serde_json::from_str::<Registration>(
   3323                 serde_json::json!({
   3324                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3325                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3326                     "response": {
   3327                         "clientDataJSON": b64_cdata_json,
   3328                         "authenticatorData": b64_adata,
   3329                         "transports": [],
   3330                         "publicKey": b64_key,
   3331                         "publicKeyAlgorithm": -8i8,
   3332                         "attestationObject": b64_aobj,
   3333                     },
   3334                     "clientExtensionResults": {
   3335                         "credProps": {
   3336                             "Rk": true,
   3337                         }
   3338                     },
   3339                     "type": "public-key"
   3340                 })
   3341                 .to_string()
   3342                 .as_str()
   3343             )
   3344             .unwrap_err()
   3345             .to_string()
   3346             .into_bytes()
   3347             .get(..err.len()),
   3348             Some(err.as_slice())
   3349         );
   3350         // Duplicate field in `credProps`.
   3351         err = Error::duplicate_field("rk").to_string().into_bytes();
   3352         assert_eq!(
   3353             serde_json::from_str::<Registration>(
   3354                 format!(
   3355                     "{{
   3356                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   3357                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   3358                        \"response\": {{
   3359                            \"clientDataJSON\": \"{b64_cdata_json}\",
   3360                            \"authenticatorData\": \"{b64_adata}\",
   3361                            \"transports\": [],
   3362                            \"publicKey\": \"{b64_key}\",
   3363                            \"publicKeyAlgorithm\": -8,
   3364                            \"attestationObject\": \"{b64_aobj}\"
   3365                        }},
   3366                        \"clientExtensionResults\": {{
   3367                            \"credProps\": {{
   3368                                \"rk\": true,
   3369                                \"rk\": true
   3370                            }}
   3371                        }},
   3372                        \"type\": \"public-key\"
   3373                      }}"
   3374                 )
   3375                 .as_str()
   3376             )
   3377             .unwrap_err()
   3378             .to_string()
   3379             .into_bytes()
   3380             .get(..err.len()),
   3381             Some(err.as_slice())
   3382         );
   3383         // `null` `enabled`.
   3384         err = Error::invalid_type(Unexpected::Other("null"), &"a boolean")
   3385             .to_string()
   3386             .into_bytes();
   3387         assert_eq!(
   3388             serde_json::from_str::<Registration>(
   3389                 serde_json::json!({
   3390                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3391                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3392                     "response": {
   3393                         "clientDataJSON": b64_cdata_json,
   3394                         "authenticatorData": b64_adata,
   3395                         "transports": [],
   3396                         "publicKey": b64_key,
   3397                         "publicKeyAlgorithm": -8i8,
   3398                         "attestationObject": b64_aobj,
   3399                     },
   3400                     "clientExtensionResults": {
   3401                         "prf": {
   3402                             "enabled": null
   3403                         }
   3404                     },
   3405                     "type": "public-key"
   3406                 })
   3407                 .to_string()
   3408                 .as_str()
   3409             )
   3410             .unwrap_err()
   3411             .to_string()
   3412             .into_bytes()
   3413             .get(..err.len()),
   3414             Some(err.as_slice())
   3415         );
   3416         // Missing `enabled`.
   3417         err = Error::missing_field("enabled").to_string().into_bytes();
   3418         assert_eq!(
   3419             serde_json::from_str::<Registration>(
   3420                 serde_json::json!({
   3421                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3422                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3423                     "response": {
   3424                         "clientDataJSON": b64_cdata_json,
   3425                         "authenticatorData": b64_adata,
   3426                         "transports": [],
   3427                         "publicKey": b64_key,
   3428                         "publicKeyAlgorithm": -8i8,
   3429                         "attestationObject": b64_aobj,
   3430                     },
   3431                     "clientExtensionResults": {
   3432                         "prf": {}
   3433                     },
   3434                     "type": "public-key"
   3435                 })
   3436                 .to_string()
   3437                 .as_str()
   3438             )
   3439             .unwrap_err()
   3440             .to_string()
   3441             .into_bytes()
   3442             .get(..err.len()),
   3443             Some(err.as_slice())
   3444         );
   3445         // `true` `enabled`.
   3446         assert!(
   3447             serde_json::from_str::<Registration>(
   3448                 serde_json::json!({
   3449                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3450                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3451                     "response": {
   3452                         "clientDataJSON": b64_cdata_json,
   3453                         "authenticatorData": b64_adata,
   3454                         "transports": [],
   3455                         "publicKey": b64_key,
   3456                         "publicKeyAlgorithm": -8i8,
   3457                         "attestationObject": b64_aobj,
   3458                     },
   3459                     "clientExtensionResults": {
   3460                         "prf": {
   3461                             "enabled": true
   3462                         }
   3463                     },
   3464                     "type": "public-key"
   3465                 })
   3466                 .to_string()
   3467                 .as_str()
   3468             )
   3469             .is_ok_and(|reg| reg.client_extension_results.cred_props.is_none()
   3470                 && reg
   3471                     .client_extension_results
   3472                     .prf
   3473                     .is_some_and(|prf| prf.enabled))
   3474         );
   3475         // `false` `enabled`.
   3476         assert!(
   3477             serde_json::from_str::<Registration>(
   3478                 serde_json::json!({
   3479                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3480                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3481                     "response": {
   3482                         "clientDataJSON": b64_cdata_json,
   3483                         "authenticatorData": b64_adata,
   3484                         "transports": [],
   3485                         "publicKey": b64_key,
   3486                         "publicKeyAlgorithm": -8i8,
   3487                         "attestationObject": b64_aobj,
   3488                     },
   3489                     "clientExtensionResults": {
   3490                         "prf": {
   3491                             "enabled": false,
   3492                         }
   3493                     },
   3494                     "type": "public-key"
   3495                 })
   3496                 .to_string()
   3497                 .as_str()
   3498             )
   3499             .is_ok_and(|reg| reg.client_extension_results.cred_props.is_none()
   3500                 && reg
   3501                     .client_extension_results
   3502                     .prf
   3503                     .is_some_and(|prf| !prf.enabled))
   3504         );
   3505         // Invalid `enabled`.
   3506         err = Error::invalid_type(Unexpected::Unsigned(3), &"a boolean")
   3507             .to_string()
   3508             .into_bytes();
   3509         assert_eq!(
   3510             serde_json::from_str::<Registration>(
   3511                 serde_json::json!({
   3512                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3513                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3514                     "response": {
   3515                         "clientDataJSON": b64_cdata_json,
   3516                         "authenticatorData": b64_adata,
   3517                         "transports": [],
   3518                         "publicKey": b64_key,
   3519                         "publicKeyAlgorithm": -8i8,
   3520                         "attestationObject": b64_aobj,
   3521                     },
   3522                     "clientExtensionResults": {
   3523                         "prf": {
   3524                             "enabled": 3u8
   3525                         }
   3526                     },
   3527                     "type": "public-key"
   3528                 })
   3529                 .to_string()
   3530                 .as_str()
   3531             )
   3532             .unwrap_err()
   3533             .to_string()
   3534             .into_bytes()
   3535             .get(..err.len()),
   3536             Some(err.as_slice())
   3537         );
   3538         // `null` `results` with `enabled` `true`.
   3539         assert!(
   3540             serde_json::from_str::<Registration>(
   3541                 serde_json::json!({
   3542                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3543                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3544                     "response": {
   3545                         "clientDataJSON": b64_cdata_json,
   3546                         "authenticatorData": b64_adata,
   3547                         "transports": [],
   3548                         "publicKey": b64_key,
   3549                         "publicKeyAlgorithm": -8i8,
   3550                         "attestationObject": b64_aobj,
   3551                     },
   3552                     "clientExtensionResults": {
   3553                         "prf": {
   3554                             "enabled": true,
   3555                             "results": null,
   3556                         }
   3557                     },
   3558                     "type": "public-key"
   3559                 })
   3560                 .to_string()
   3561                 .as_str()
   3562             )
   3563             .is_ok_and(|reg| reg.client_extension_results.cred_props.is_none()
   3564                 && reg
   3565                     .client_extension_results
   3566                     .prf
   3567                     .is_some_and(|prf| prf.enabled))
   3568         );
   3569         // `null` `results` with `enabled` `false`.
   3570         err = Error::custom(
   3571             "prf must not have 'results', including a null 'results', if 'enabled' is false",
   3572         )
   3573         .to_string()
   3574         .into_bytes();
   3575         assert_eq!(
   3576             serde_json::from_str::<Registration>(
   3577                 serde_json::json!({
   3578                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3579                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3580                     "response": {
   3581                         "clientDataJSON": b64_cdata_json,
   3582                         "authenticatorData": b64_adata,
   3583                         "transports": [],
   3584                         "publicKey": b64_key,
   3585                         "publicKeyAlgorithm": -8i8,
   3586                         "attestationObject": b64_aobj,
   3587                     },
   3588                     "clientExtensionResults": {
   3589                         "prf": {
   3590                             "enabled": false,
   3591                             "results": null
   3592                         }
   3593                     },
   3594                     "type": "public-key"
   3595                 })
   3596                 .to_string()
   3597                 .as_str()
   3598             )
   3599             .unwrap_err()
   3600             .to_string()
   3601             .into_bytes()
   3602             .get(..err.len()),
   3603             Some(err.as_slice())
   3604         );
   3605         // Duplicate field in `prf`.
   3606         err = Error::duplicate_field("enabled").to_string().into_bytes();
   3607         assert_eq!(
   3608             serde_json::from_str::<Registration>(
   3609                 format!(
   3610                     "{{
   3611                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   3612                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   3613                        \"response\": {{
   3614                            \"clientDataJSON\": \"{b64_cdata_json}\",
   3615                            \"authenticatorData\": \"{b64_adata}\",
   3616                            \"transports\": [],
   3617                            \"publicKey\": \"{b64_key}\",
   3618                            \"publicKeyAlgorithm\": -8,
   3619                            \"attestationObject\": \"{b64_aobj}\"
   3620                        }},
   3621                        \"clientExtensionResults\": {{
   3622                            \"prf\": {{
   3623                                \"enabled\": true,
   3624                                \"enabled\": true
   3625                            }}
   3626                        }},
   3627                        \"type\": \"public-key\"
   3628                      }}"
   3629                 )
   3630                 .as_str()
   3631             )
   3632             .unwrap_err()
   3633             .to_string()
   3634             .into_bytes()
   3635             .get(..err.len()),
   3636             Some(err.as_slice())
   3637         );
   3638         // Missing `first`.
   3639         err = Error::missing_field("first").to_string().into_bytes();
   3640         assert_eq!(
   3641             serde_json::from_str::<Registration>(
   3642                 serde_json::json!({
   3643                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3644                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3645                     "response": {
   3646                         "clientDataJSON": b64_cdata_json,
   3647                         "authenticatorData": b64_adata,
   3648                         "transports": [],
   3649                         "publicKey": b64_key,
   3650                         "publicKeyAlgorithm": -8i8,
   3651                         "attestationObject": b64_aobj,
   3652                     },
   3653                     "clientExtensionResults": {
   3654                         "prf": {
   3655                             "enabled": true,
   3656                             "results": {},
   3657                         }
   3658                     },
   3659                     "type": "public-key"
   3660                 })
   3661                 .to_string()
   3662                 .as_str()
   3663             )
   3664             .unwrap_err()
   3665             .to_string()
   3666             .into_bytes()
   3667             .get(..err.len()),
   3668             Some(err.as_slice())
   3669         );
   3670         // `null` `first`.
   3671         assert!(
   3672             serde_json::from_str::<Registration>(
   3673                 serde_json::json!({
   3674                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3675                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3676                     "response": {
   3677                         "clientDataJSON": b64_cdata_json,
   3678                         "authenticatorData": b64_adata,
   3679                         "transports": [],
   3680                         "publicKey": b64_key,
   3681                         "publicKeyAlgorithm": -8i8,
   3682                         "attestationObject": b64_aobj,
   3683                     },
   3684                     "clientExtensionResults": {
   3685                         "prf": {
   3686                             "enabled": true,
   3687                             "results": {
   3688                                 "first": null
   3689                             },
   3690                         }
   3691                     },
   3692                     "type": "public-key"
   3693                 })
   3694                 .to_string()
   3695                 .as_str()
   3696             )
   3697             .is_ok_and(|reg| reg.client_extension_results.cred_props.is_none()
   3698                 && reg
   3699                     .client_extension_results
   3700                     .prf
   3701                     .is_some_and(|prf| prf.enabled))
   3702         );
   3703         // `null` `second`.
   3704         assert!(
   3705             serde_json::from_str::<Registration>(
   3706                 serde_json::json!({
   3707                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3708                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3709                     "response": {
   3710                         "clientDataJSON": b64_cdata_json,
   3711                         "authenticatorData": b64_adata,
   3712                         "transports": [],
   3713                         "publicKey": b64_key,
   3714                         "publicKeyAlgorithm": -8i8,
   3715                         "attestationObject": b64_aobj,
   3716                     },
   3717                     "clientExtensionResults": {
   3718                         "prf": {
   3719                             "enabled": true,
   3720                             "results": {
   3721                                 "first": null,
   3722                                 "second": null
   3723                             },
   3724                         }
   3725                     },
   3726                     "type": "public-key"
   3727                 })
   3728                 .to_string()
   3729                 .as_str()
   3730             )
   3731             .is_ok_and(|reg| reg.client_extension_results.cred_props.is_none()
   3732                 && reg
   3733                     .client_extension_results
   3734                     .prf
   3735                     .is_some_and(|prf| prf.enabled))
   3736         );
   3737         // Non-`null` `first`.
   3738         err = Error::invalid_type(Unexpected::Option, &"null")
   3739             .to_string()
   3740             .into_bytes();
   3741         assert_eq!(
   3742             serde_json::from_str::<Registration>(
   3743                 serde_json::json!({
   3744                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3745                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3746                     "response": {
   3747                         "clientDataJSON": b64_cdata_json,
   3748                         "authenticatorData": b64_adata,
   3749                         "transports": [],
   3750                         "publicKey": b64_key,
   3751                         "publicKeyAlgorithm": -8i8,
   3752                         "attestationObject": b64_aobj,
   3753                     },
   3754                     "clientExtensionResults": {
   3755                         "prf": {
   3756                             "enabled": true,
   3757                             "results": {
   3758                                 "first": ""
   3759                             },
   3760                         }
   3761                     },
   3762                     "type": "public-key"
   3763                 })
   3764                 .to_string()
   3765                 .as_str()
   3766             )
   3767             .unwrap_err()
   3768             .to_string()
   3769             .into_bytes()
   3770             .get(..err.len()),
   3771             Some(err.as_slice())
   3772         );
   3773         // Non-`null` `second`.
   3774         err = Error::invalid_type(Unexpected::Option, &"null")
   3775             .to_string()
   3776             .into_bytes();
   3777         assert_eq!(
   3778             serde_json::from_str::<Registration>(
   3779                 serde_json::json!({
   3780                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3781                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3782                     "response": {
   3783                         "clientDataJSON": b64_cdata_json,
   3784                         "authenticatorData": b64_adata,
   3785                         "transports": [],
   3786                         "publicKey": b64_key,
   3787                         "publicKeyAlgorithm": -8i8,
   3788                         "attestationObject": b64_aobj,
   3789                     },
   3790                     "clientExtensionResults": {
   3791                         "prf": {
   3792                             "enabled": true,
   3793                             "results": {
   3794                                 "first": null,
   3795                                 "second": ""
   3796                             },
   3797                         }
   3798                     },
   3799                     "type": "public-key"
   3800                 })
   3801                 .to_string()
   3802                 .as_str()
   3803             )
   3804             .unwrap_err()
   3805             .to_string()
   3806             .into_bytes()
   3807             .get(..err.len()),
   3808             Some(err.as_slice())
   3809         );
   3810         // Unknown `prf` field.
   3811         err = Error::unknown_field("Results", ["enabled", "results"].as_slice())
   3812             .to_string()
   3813             .into_bytes();
   3814         assert_eq!(
   3815             serde_json::from_str::<Registration>(
   3816                 serde_json::json!({
   3817                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3818                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3819                     "response": {
   3820                         "clientDataJSON": b64_cdata_json,
   3821                         "authenticatorData": b64_adata,
   3822                         "transports": [],
   3823                         "publicKey": b64_key,
   3824                         "publicKeyAlgorithm": -8i8,
   3825                         "attestationObject": b64_aobj,
   3826                     },
   3827                     "clientExtensionResults": {
   3828                         "prf": {
   3829                             "enabled": true,
   3830                             "Results": null
   3831                         }
   3832                     },
   3833                     "type": "public-key"
   3834                 })
   3835                 .to_string()
   3836                 .as_str()
   3837             )
   3838             .unwrap_err()
   3839             .to_string()
   3840             .into_bytes()
   3841             .get(..err.len()),
   3842             Some(err.as_slice())
   3843         );
   3844         // Unknown `results` field.
   3845         err = Error::unknown_field("Second", ["first", "second"].as_slice())
   3846             .to_string()
   3847             .into_bytes();
   3848         assert_eq!(
   3849             serde_json::from_str::<Registration>(
   3850                 serde_json::json!({
   3851                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   3852                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   3853                     "response": {
   3854                         "clientDataJSON": b64_cdata_json,
   3855                         "authenticatorData": b64_adata,
   3856                         "transports": [],
   3857                         "publicKey": b64_key,
   3858                         "publicKeyAlgorithm": -8i8,
   3859                         "attestationObject": b64_aobj,
   3860                     },
   3861                     "clientExtensionResults": {
   3862                         "prf": {
   3863                             "enabled": true,
   3864                             "results": {
   3865                                 "first": null,
   3866                                 "Second": null
   3867                             }
   3868                         }
   3869                     },
   3870                     "type": "public-key"
   3871                 })
   3872                 .to_string()
   3873                 .as_str()
   3874             )
   3875             .unwrap_err()
   3876             .to_string()
   3877             .into_bytes()
   3878             .get(..err.len()),
   3879             Some(err.as_slice())
   3880         );
   3881         // Duplicate field in `results`.
   3882         err = Error::duplicate_field("first").to_string().into_bytes();
   3883         assert_eq!(
   3884             serde_json::from_str::<Registration>(
   3885                 format!(
   3886                     "{{
   3887                        \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   3888                        \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\",
   3889                        \"response\": {{
   3890                            \"clientDataJSON\": \"{b64_cdata_json}\",
   3891                            \"authenticatorData\": \"{b64_adata}\",
   3892                            \"transports\": [],
   3893                            \"publicKey\": \"{b64_key}\",
   3894                            \"publicKeyAlgorithm\": -8,
   3895                            \"attestationObject\": \"{b64_aobj}\"
   3896                        }},
   3897                        \"clientExtensionResults\": {{
   3898                            \"prf\": {{
   3899                                \"enabled\": true,
   3900                                \"results\": {{
   3901                                    \"first\": null,
   3902                                    \"first\": null
   3903                                }}
   3904                            }}
   3905                        }},
   3906                        \"type\": \"public-key\"
   3907                      }}"
   3908                 )
   3909                 .as_str()
   3910             )
   3911             .unwrap_err()
   3912             .to_string()
   3913             .into_bytes()
   3914             .get(..err.len()),
   3915             Some(err.as_slice())
   3916         );
   3917     }
   3918     #[expect(clippy::unwrap_used, reason = "OK in tests")]
   3919     #[expect(clippy::indexing_slicing, reason = "comments justify correctness")]
   3920     #[expect(clippy::too_many_lines, reason = "a lot to test")]
   3921     #[test]
   3922     fn es256_registration_deserialize_data_mismatch() {
   3923         let c_data_json = serde_json::json!({}).to_string();
   3924         let mut att_obj: [u8; 178] = [
   3925             cbor::MAP_3,
   3926             cbor::TEXT_3,
   3927             b'f',
   3928             b'm',
   3929             b't',
   3930             cbor::TEXT_4,
   3931             b'n',
   3932             b'o',
   3933             b'n',
   3934             b'e',
   3935             cbor::TEXT_7,
   3936             b'a',
   3937             b't',
   3938             b't',
   3939             b'S',
   3940             b't',
   3941             b'm',
   3942             b't',
   3943             cbor::MAP_0,
   3944             cbor::TEXT_8,
   3945             b'a',
   3946             b'u',
   3947             b't',
   3948             b'h',
   3949             b'D',
   3950             b'a',
   3951             b't',
   3952             b'a',
   3953             cbor::BYTES_INFO_24,
   3954             148,
   3955             // `rpIdHash`.
   3956             0,
   3957             0,
   3958             0,
   3959             0,
   3960             0,
   3961             0,
   3962             0,
   3963             0,
   3964             0,
   3965             0,
   3966             0,
   3967             0,
   3968             0,
   3969             0,
   3970             0,
   3971             0,
   3972             0,
   3973             0,
   3974             0,
   3975             0,
   3976             0,
   3977             0,
   3978             0,
   3979             0,
   3980             0,
   3981             0,
   3982             0,
   3983             0,
   3984             0,
   3985             0,
   3986             0,
   3987             0,
   3988             // `flags`.
   3989             0b0100_0101,
   3990             // `signCount`.
   3991             0,
   3992             0,
   3993             0,
   3994             0,
   3995             // `aaguid`.
   3996             0,
   3997             0,
   3998             0,
   3999             0,
   4000             0,
   4001             0,
   4002             0,
   4003             0,
   4004             0,
   4005             0,
   4006             0,
   4007             0,
   4008             0,
   4009             0,
   4010             0,
   4011             0,
   4012             // `credentialIdLength`.
   4013             0,
   4014             16,
   4015             // `credentialId`.
   4016             0,
   4017             0,
   4018             0,
   4019             0,
   4020             0,
   4021             0,
   4022             0,
   4023             0,
   4024             0,
   4025             0,
   4026             0,
   4027             0,
   4028             0,
   4029             0,
   4030             0,
   4031             0,
   4032             // P-256 COSE key.
   4033             cbor::MAP_5,
   4034             KTY,
   4035             EC2,
   4036             ALG,
   4037             ES256,
   4038             // `crv`.
   4039             cbor::NEG_ONE,
   4040             // `P-256`.
   4041             cbor::ONE,
   4042             // `x`.
   4043             cbor::NEG_TWO,
   4044             cbor::BYTES_INFO_24,
   4045             32,
   4046             // x-coordinate. This will be overwritten later.
   4047             0,
   4048             0,
   4049             0,
   4050             0,
   4051             0,
   4052             0,
   4053             0,
   4054             0,
   4055             0,
   4056             0,
   4057             0,
   4058             0,
   4059             0,
   4060             0,
   4061             0,
   4062             0,
   4063             0,
   4064             0,
   4065             0,
   4066             0,
   4067             0,
   4068             0,
   4069             0,
   4070             0,
   4071             0,
   4072             0,
   4073             0,
   4074             0,
   4075             0,
   4076             0,
   4077             0,
   4078             0,
   4079             // `y`.
   4080             cbor::NEG_THREE,
   4081             cbor::BYTES_INFO_24,
   4082             32,
   4083             // y-coordinate. This will be overwritten later.
   4084             0,
   4085             0,
   4086             0,
   4087             0,
   4088             0,
   4089             0,
   4090             0,
   4091             0,
   4092             0,
   4093             0,
   4094             0,
   4095             0,
   4096             0,
   4097             0,
   4098             0,
   4099             0,
   4100             0,
   4101             0,
   4102             0,
   4103             0,
   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         ];
   4117         let key = P256Key::from_bytes(
   4118             &[
   4119                 137, 133, 36, 206, 163, 47, 255, 5, 76, 144, 163, 141, 40, 109, 108, 240, 246, 115,
   4120                 178, 237, 169, 68, 6, 129, 92, 21, 238, 127, 55, 158, 207, 95,
   4121             ]
   4122             .into(),
   4123         )
   4124         .unwrap()
   4125         .public_key();
   4126         let enc_key = key.to_encoded_point(false);
   4127         let pub_key = key.to_public_key_der().unwrap();
   4128         let att_obj_len = att_obj.len();
   4129         let x_start = att_obj_len - 67;
   4130         let y_meta_start = x_start + 32;
   4131         let y_start = y_meta_start + 3;
   4132         att_obj[x_start..y_meta_start].copy_from_slice(enc_key.x().unwrap());
   4133         att_obj[y_start..].copy_from_slice(enc_key.y().unwrap());
   4134         let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes());
   4135         let b64_adata = base64url_nopad::encode(&att_obj[att_obj.len() - 148..]);
   4136         let b64_key = base64url_nopad::encode(pub_key.as_bytes());
   4137         let b64_aobj = base64url_nopad::encode(att_obj.as_slice());
   4138         // Base case is valid.
   4139         assert!(
   4140             serde_json::from_str::<Registration>(
   4141                 serde_json::json!({
   4142                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4143                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4144                     "response": {
   4145                         "clientDataJSON": b64_cdata_json,
   4146                         "authenticatorData": b64_adata,
   4147                         "transports": [],
   4148                         "publicKey": b64_key,
   4149                         "publicKeyAlgorithm": -7i8,
   4150                         "attestationObject": b64_aobj,
   4151                     },
   4152                     "clientExtensionResults": {},
   4153                     "type": "public-key"
   4154                 })
   4155                 .to_string()
   4156                 .as_str()
   4157             )
   4158             .is_ok_and(
   4159                 |reg| reg.response.client_data_json == c_data_json.as_bytes()
   4160                     && reg.response.attestation_object_and_c_data_hash[..att_obj.len()] == att_obj
   4161                     && reg.response.attestation_object_and_c_data_hash[att_obj.len()..]
   4162                         == *Sha256::digest(c_data_json.as_bytes())
   4163                     && reg.response.transports.is_empty()
   4164                     && matches!(reg.authenticator_attachment, AuthenticatorAttachment::None)
   4165                     && reg.client_extension_results.cred_props.is_none()
   4166                     && reg.client_extension_results.prf.is_none()
   4167             )
   4168         );
   4169         // `publicKeyAlgorithm` mismatch.
   4170         let mut err = Error::invalid_value(
   4171             Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()),
   4172             &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es256).as_str()
   4173         )
   4174         .to_string().into_bytes();
   4175         assert_eq!(
   4176             serde_json::from_str::<Registration>(
   4177                 serde_json::json!({
   4178                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4179                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4180                     "response": {
   4181                         "clientDataJSON": b64_cdata_json,
   4182                         "authenticatorData": b64_adata,
   4183                         "transports": [],
   4184                         "publicKey": b64_key,
   4185                         "publicKeyAlgorithm": -8i8,
   4186                         "attestationObject": b64_aobj,
   4187                     },
   4188                     "clientExtensionResults": {},
   4189                     "type": "public-key"
   4190                 })
   4191                 .to_string()
   4192                 .as_str()
   4193             )
   4194             .unwrap_err()
   4195             .to_string()
   4196             .into_bytes()
   4197             .get(..err.len()),
   4198             Some(err.as_slice())
   4199         );
   4200         // Missing `publicKeyAlgorithm`.
   4201         err = Error::missing_field("publicKeyAlgorithm")
   4202             .to_string()
   4203             .into_bytes();
   4204         assert_eq!(
   4205             serde_json::from_str::<Registration>(
   4206                 serde_json::json!({
   4207                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4208                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4209                     "response": {
   4210                         "clientDataJSON": b64_cdata_json,
   4211                         "authenticatorData": b64_adata,
   4212                         "transports": [],
   4213                         "publicKey": b64_key,
   4214                         "attestationObject": b64_aobj,
   4215                     },
   4216                     "clientExtensionResults": {},
   4217                     "type": "public-key"
   4218                 })
   4219                 .to_string()
   4220                 .as_str()
   4221             )
   4222             .unwrap_err()
   4223             .to_string()
   4224             .into_bytes()
   4225             .get(..err.len()),
   4226             Some(err.as_slice())
   4227         );
   4228         // `null` `publicKeyAlgorithm`.
   4229         err = Error::invalid_type(Unexpected::Other("null"), &"publicKeyAlgorithm")
   4230             .to_string()
   4231             .into_bytes();
   4232         assert_eq!(
   4233             serde_json::from_str::<Registration>(
   4234                 serde_json::json!({
   4235                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4236                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4237                     "response": {
   4238                         "clientDataJSON": b64_cdata_json,
   4239                         "authenticatorData": b64_adata,
   4240                         "transports": [],
   4241                         "publicKey": b64_key,
   4242                         "publicKeyAlgorithm": null,
   4243                         "attestationObject": b64_aobj,
   4244                     },
   4245                     "clientExtensionResults": {},
   4246                     "type": "public-key"
   4247                 })
   4248                 .to_string()
   4249                 .as_str()
   4250             )
   4251             .unwrap_err()
   4252             .to_string()
   4253             .into_bytes()
   4254             .get(..err.len()),
   4255             Some(err.as_slice())
   4256         );
   4257         // `publicKey` mismatch.
   4258         let bad_pub_key = P256PubKey::from_encoded_point(&P256Pt::from_affine_coordinates(
   4259             &[
   4260                 66, 71, 188, 41, 125, 2, 226, 44, 148, 62, 63, 190, 172, 64, 33, 214, 6, 37, 148,
   4261                 23, 240, 235, 203, 84, 112, 219, 232, 197, 54, 182, 17, 235,
   4262             ]
   4263             .into(),
   4264             &[
   4265                 22, 172, 123, 13, 170, 242, 217, 248, 193, 209, 206, 163, 92, 4, 162, 168, 113, 63,
   4266                 2, 117, 16, 223, 239, 196, 109, 179, 10, 130, 43, 213, 205, 92,
   4267             ]
   4268             .into(),
   4269             false,
   4270         ))
   4271         .unwrap();
   4272         err = Error::invalid_value(
   4273             Unexpected::Bytes([0; 32].as_slice()),
   4274             &format!(
   4275                 "DER-encoded public key to match the public key within the attestation object: P256(UncompressedP256PubKey({:?}, {:?}))",
   4276                 &att_obj[x_start..y_meta_start],
   4277                 &att_obj[y_start..],
   4278             )
   4279             .as_str(),
   4280         )
   4281         .to_string().into_bytes();
   4282         assert_eq!(serde_json::from_str::<Registration>(
   4283             serde_json::json!({
   4284                 "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4285                 "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4286                 "response": {
   4287                     "clientDataJSON": b64_cdata_json,
   4288                     "authenticatorData": b64_adata,
   4289                     "transports": [],
   4290                     "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()),
   4291                     "publicKeyAlgorithm": -7i8,
   4292                     "attestationObject": b64_aobj,
   4293                 },
   4294                 "clientExtensionResults": {},
   4295                 "type": "public-key"
   4296             })
   4297             .to_string()
   4298             .as_str()
   4299             )
   4300             .unwrap_err().to_string().into_bytes().get(..err.len()),
   4301             Some(err.as_slice())
   4302         );
   4303         // Missing `publicKey` when using EdDSA, ES256, or RS256.
   4304         err = Error::missing_field("publicKey").to_string().into_bytes();
   4305         assert_eq!(
   4306             serde_json::from_str::<Registration>(
   4307                 serde_json::json!({
   4308                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4309                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4310                     "response": {
   4311                         "clientDataJSON": b64_cdata_json,
   4312                         "authenticatorData": b64_adata,
   4313                         "transports": [],
   4314                         "publicKeyAlgorithm": -7i8,
   4315                         "attestationObject": b64_aobj,
   4316                     },
   4317                     "clientExtensionResults": {},
   4318                     "type": "public-key"
   4319                 })
   4320                 .to_string()
   4321                 .as_str()
   4322             )
   4323             .unwrap_err()
   4324             .to_string()
   4325             .into_bytes()
   4326             .get(..err.len()),
   4327             Some(err.as_slice())
   4328         );
   4329         // `null` `publicKey` when using EdDSA, ES256, or RS256.
   4330         err = Error::invalid_type(Unexpected::Other("null"), &"publicKey")
   4331             .to_string()
   4332             .into_bytes();
   4333         assert_eq!(
   4334             serde_json::from_str::<Registration>(
   4335                 serde_json::json!({
   4336                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4337                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4338                     "response": {
   4339                         "clientDataJSON": b64_cdata_json,
   4340                         "authenticatorData": b64_adata,
   4341                         "transports": [],
   4342                         "publicKey": null,
   4343                         "publicKeyAlgorithm": -7i8,
   4344                         "attestationObject": b64_aobj,
   4345                     },
   4346                     "clientExtensionResults": {},
   4347                     "type": "public-key"
   4348                 })
   4349                 .to_string()
   4350                 .as_str()
   4351             )
   4352             .unwrap_err()
   4353             .to_string()
   4354             .into_bytes()
   4355             .get(..err.len()),
   4356             Some(err.as_slice())
   4357         );
   4358     }
   4359     #[expect(
   4360         clippy::assertions_on_result_states,
   4361         clippy::unwrap_used,
   4362         reason = "OK in tests"
   4363     )]
   4364     #[expect(clippy::indexing_slicing, reason = "comments justify correctness")]
   4365     #[expect(clippy::too_many_lines, reason = "a lot to test")]
   4366     #[test]
   4367     fn es384_registration_deserialize_data_mismatch() {
   4368         let c_data_json = serde_json::json!({}).to_string();
   4369         let mut att_obj: [u8; 211] = [
   4370             cbor::MAP_3,
   4371             cbor::TEXT_3,
   4372             b'f',
   4373             b'm',
   4374             b't',
   4375             cbor::TEXT_4,
   4376             b'n',
   4377             b'o',
   4378             b'n',
   4379             b'e',
   4380             cbor::TEXT_7,
   4381             b'a',
   4382             b't',
   4383             b't',
   4384             b'S',
   4385             b't',
   4386             b'm',
   4387             b't',
   4388             cbor::MAP_0,
   4389             cbor::TEXT_8,
   4390             b'a',
   4391             b'u',
   4392             b't',
   4393             b'h',
   4394             b'D',
   4395             b'a',
   4396             b't',
   4397             b'a',
   4398             cbor::BYTES_INFO_24,
   4399             181,
   4400             // `rpIdHash`.
   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             // `flags`.
   4434             0b0100_0101,
   4435             // `signCount`.
   4436             0,
   4437             0,
   4438             0,
   4439             0,
   4440             // `aaguid`.
   4441             0,
   4442             0,
   4443             0,
   4444             0,
   4445             0,
   4446             0,
   4447             0,
   4448             0,
   4449             0,
   4450             0,
   4451             0,
   4452             0,
   4453             0,
   4454             0,
   4455             0,
   4456             0,
   4457             // `credentialIdLength`.
   4458             0,
   4459             16,
   4460             // `credentialId`.
   4461             0,
   4462             0,
   4463             0,
   4464             0,
   4465             0,
   4466             0,
   4467             0,
   4468             0,
   4469             0,
   4470             0,
   4471             0,
   4472             0,
   4473             0,
   4474             0,
   4475             0,
   4476             0,
   4477             // P-384 COSE key.
   4478             cbor::MAP_5,
   4479             KTY,
   4480             EC2,
   4481             ALG,
   4482             cbor::NEG_INFO_24,
   4483             ES384,
   4484             // `crv`.
   4485             cbor::NEG_ONE,
   4486             // `P-384`.
   4487             cbor::TWO,
   4488             // `x`.
   4489             cbor::NEG_TWO,
   4490             cbor::BYTES_INFO_24,
   4491             48,
   4492             // x-coordinate. This will be overwritten later.
   4493             0,
   4494             0,
   4495             0,
   4496             0,
   4497             0,
   4498             0,
   4499             0,
   4500             0,
   4501             0,
   4502             0,
   4503             0,
   4504             0,
   4505             0,
   4506             0,
   4507             0,
   4508             0,
   4509             0,
   4510             0,
   4511             0,
   4512             0,
   4513             0,
   4514             0,
   4515             0,
   4516             0,
   4517             0,
   4518             0,
   4519             0,
   4520             0,
   4521             0,
   4522             0,
   4523             0,
   4524             0,
   4525             0,
   4526             0,
   4527             0,
   4528             0,
   4529             0,
   4530             0,
   4531             0,
   4532             0,
   4533             0,
   4534             0,
   4535             0,
   4536             0,
   4537             0,
   4538             0,
   4539             0,
   4540             0,
   4541             // `y`.
   4542             cbor::NEG_THREE,
   4543             cbor::BYTES_INFO_24,
   4544             48,
   4545             // y-coordinate. This will be overwritten later.
   4546             0,
   4547             0,
   4548             0,
   4549             0,
   4550             0,
   4551             0,
   4552             0,
   4553             0,
   4554             0,
   4555             0,
   4556             0,
   4557             0,
   4558             0,
   4559             0,
   4560             0,
   4561             0,
   4562             0,
   4563             0,
   4564             0,
   4565             0,
   4566             0,
   4567             0,
   4568             0,
   4569             0,
   4570             0,
   4571             0,
   4572             0,
   4573             0,
   4574             0,
   4575             0,
   4576             0,
   4577             0,
   4578             0,
   4579             0,
   4580             0,
   4581             0,
   4582             0,
   4583             0,
   4584             0,
   4585             0,
   4586             0,
   4587             0,
   4588             0,
   4589             0,
   4590             0,
   4591             0,
   4592             0,
   4593             0,
   4594         ];
   4595         let key = P384Key::from_bytes(
   4596             &[
   4597                 158, 99, 156, 49, 190, 211, 85, 167, 28, 2, 80, 57, 31, 22, 17, 38, 85, 78, 232,
   4598                 42, 45, 199, 154, 243, 136, 251, 84, 34, 5, 120, 208, 91, 61, 248, 64, 144, 87, 1,
   4599                 32, 86, 220, 68, 182, 11, 105, 223, 75, 70,
   4600             ]
   4601             .into(),
   4602         )
   4603         .unwrap()
   4604         .public_key();
   4605         let enc_key = key.to_encoded_point(false);
   4606         let pub_key = key.to_public_key_der().unwrap();
   4607         let att_obj_len = att_obj.len();
   4608         let x_start = att_obj_len - 99;
   4609         let y_meta_start = x_start + 48;
   4610         let y_start = y_meta_start + 3;
   4611         att_obj[x_start..y_meta_start].copy_from_slice(enc_key.x().unwrap());
   4612         att_obj[y_start..].copy_from_slice(enc_key.y().unwrap());
   4613         let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes());
   4614         let b64_adata = base64url_nopad::encode(&att_obj[att_obj_len - 181..]);
   4615         let b64_key = base64url_nopad::encode(pub_key.as_bytes());
   4616         let b64_aobj = base64url_nopad::encode(att_obj.as_slice());
   4617         // Base case is valid.
   4618         assert!(
   4619             serde_json::from_str::<Registration>(
   4620                 serde_json::json!({
   4621                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4622                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4623                     "response": {
   4624                         "clientDataJSON": b64_cdata_json,
   4625                         "authenticatorData": b64_adata,
   4626                         "transports": [],
   4627                         "publicKey": b64_key,
   4628                         "publicKeyAlgorithm": -35i8,
   4629                         "attestationObject": b64_aobj,
   4630                     },
   4631                     "clientExtensionResults": {},
   4632                     "type": "public-key"
   4633                 })
   4634                 .to_string()
   4635                 .as_str()
   4636             )
   4637             .is_ok_and(
   4638                 |reg| reg.response.client_data_json == c_data_json.as_bytes()
   4639                     && reg.response.attestation_object_and_c_data_hash[..att_obj.len()] == att_obj
   4640                     && reg.response.attestation_object_and_c_data_hash[att_obj.len()..]
   4641                         == *Sha256::digest(c_data_json.as_bytes())
   4642                     && reg.response.transports.is_empty()
   4643                     && matches!(reg.authenticator_attachment, AuthenticatorAttachment::None)
   4644                     && reg.client_extension_results.cred_props.is_none()
   4645                     && reg.client_extension_results.prf.is_none()
   4646             )
   4647         );
   4648         // `publicKeyAlgorithm` mismatch.
   4649         let mut err = Error::invalid_value(
   4650             Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()),
   4651             &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es384).as_str()
   4652         )
   4653         .to_string().into_bytes();
   4654         assert_eq!(
   4655             serde_json::from_str::<Registration>(
   4656                 serde_json::json!({
   4657                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4658                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4659                     "response": {
   4660                         "clientDataJSON": b64_cdata_json,
   4661                         "authenticatorData": b64_adata,
   4662                         "transports": [],
   4663                         "publicKey": b64_key,
   4664                         "publicKeyAlgorithm": -7i8,
   4665                         "attestationObject": b64_aobj,
   4666                     },
   4667                     "clientExtensionResults": {},
   4668                     "type": "public-key"
   4669                 })
   4670                 .to_string()
   4671                 .as_str()
   4672             )
   4673             .unwrap_err()
   4674             .to_string()
   4675             .into_bytes()
   4676             .get(..err.len()),
   4677             Some(err.as_slice())
   4678         );
   4679         // Missing `publicKeyAlgorithm`.
   4680         err = Error::missing_field("publicKeyAlgorithm")
   4681             .to_string()
   4682             .into_bytes();
   4683         assert_eq!(
   4684             serde_json::from_str::<Registration>(
   4685                 serde_json::json!({
   4686                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4687                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4688                     "response": {
   4689                         "clientDataJSON": b64_cdata_json,
   4690                         "authenticatorData": b64_adata,
   4691                         "transports": [],
   4692                         "publicKey": b64_key,
   4693                         "attestationObject": b64_aobj,
   4694                     },
   4695                     "clientExtensionResults": {},
   4696                     "type": "public-key"
   4697                 })
   4698                 .to_string()
   4699                 .as_str()
   4700             )
   4701             .unwrap_err()
   4702             .to_string()
   4703             .into_bytes()
   4704             .get(..err.len()),
   4705             Some(err.as_slice())
   4706         );
   4707         // `null` `publicKeyAlgorithm`.
   4708         err = Error::invalid_type(Unexpected::Other("null"), &"publicKeyAlgorithm")
   4709             .to_string()
   4710             .into_bytes();
   4711         assert_eq!(
   4712             serde_json::from_str::<Registration>(
   4713                 serde_json::json!({
   4714                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4715                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4716                     "response": {
   4717                         "clientDataJSON": b64_cdata_json,
   4718                         "authenticatorData": b64_adata,
   4719                         "transports": [],
   4720                         "publicKey": b64_key,
   4721                         "publicKeyAlgorithm": null,
   4722                         "attestationObject": b64_aobj,
   4723                     },
   4724                     "clientExtensionResults": {},
   4725                     "type": "public-key"
   4726                 })
   4727                 .to_string()
   4728                 .as_str()
   4729             )
   4730             .unwrap_err()
   4731             .to_string()
   4732             .into_bytes()
   4733             .get(..err.len()),
   4734             Some(err.as_slice())
   4735         );
   4736         // `publicKey` mismatch.
   4737         let bad_pub_key = P384PubKey::from_encoded_point(&P384Pt::from_affine_coordinates(
   4738             &[
   4739                 192, 10, 27, 46, 66, 67, 80, 98, 33, 230, 156, 95, 1, 135, 150, 110, 64, 243, 22,
   4740                 118, 5, 255, 107, 44, 234, 111, 217, 105, 125, 114, 39, 7, 126, 2, 191, 111, 48,
   4741                 93, 234, 175, 18, 172, 59, 28, 97, 106, 178, 152,
   4742             ]
   4743             .into(),
   4744             &[
   4745                 57, 36, 196, 12, 109, 129, 253, 115, 88, 154, 6, 43, 195, 85, 169, 5, 230, 51, 28,
   4746                 205, 142, 28, 150, 35, 24, 222, 170, 253, 14, 248, 84, 151, 109, 191, 152, 111,
   4747                 222, 70, 134, 247, 109, 171, 211, 33, 214, 217, 200, 111,
   4748             ]
   4749             .into(),
   4750             false,
   4751         ))
   4752         .unwrap();
   4753         err = Error::invalid_value(
   4754             Unexpected::Bytes([0; 32].as_slice()),
   4755             &format!(
   4756                 "DER-encoded public key to match the public key within the attestation object: P384(UncompressedP384PubKey({:?}, {:?}))",
   4757                 &att_obj[x_start..y_meta_start],
   4758                 &att_obj[y_start..],
   4759             )
   4760             .as_str(),
   4761         )
   4762         .to_string().into_bytes();
   4763         assert_eq!(serde_json::from_str::<Registration>(
   4764             serde_json::json!({
   4765                 "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4766                 "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4767                 "response": {
   4768                     "clientDataJSON": b64_cdata_json,
   4769                     "authenticatorData": b64_adata,
   4770                     "transports": [],
   4771                     "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()),
   4772                     "publicKeyAlgorithm": -35i8,
   4773                     "attestationObject": b64_aobj,
   4774                 },
   4775                 "clientExtensionResults": {},
   4776                 "type": "public-key"
   4777             })
   4778             .to_string()
   4779             .as_str()
   4780             )
   4781             .unwrap_err().to_string().into_bytes().get(..err.len()),
   4782             Some(err.as_slice())
   4783         );
   4784         // Missing `publicKey` is allowed when not using EdDSA, ES256, or RS256.
   4785         assert!(
   4786             serde_json::from_str::<Registration>(
   4787                 serde_json::json!({
   4788                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4789                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4790                     "response": {
   4791                         "clientDataJSON": b64_cdata_json,
   4792                         "authenticatorData": b64_adata,
   4793                         "transports": [],
   4794                         "publicKeyAlgorithm": -35i8,
   4795                         "attestationObject": b64_aobj,
   4796                     },
   4797                     "clientExtensionResults": {},
   4798                     "type": "public-key"
   4799                 })
   4800                 .to_string()
   4801                 .as_str()
   4802             )
   4803             .is_ok()
   4804         );
   4805         // `publicKeyAlgorithm` mismatch when `publicKey` does not exist.
   4806         err = Error::invalid_value(
   4807             Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()),
   4808             &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es384).as_str()
   4809         )
   4810         .to_string().into_bytes();
   4811         assert_eq!(
   4812             serde_json::from_str::<Registration>(
   4813                 serde_json::json!({
   4814                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4815                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4816                     "response": {
   4817                         "clientDataJSON": b64_cdata_json,
   4818                         "authenticatorData": b64_adata,
   4819                         "transports": [],
   4820                         "publicKeyAlgorithm": -7i8,
   4821                         "attestationObject": b64_aobj,
   4822                     },
   4823                     "clientExtensionResults": {},
   4824                     "type": "public-key"
   4825                 })
   4826                 .to_string()
   4827                 .as_str()
   4828             )
   4829             .unwrap_err()
   4830             .to_string()
   4831             .into_bytes()
   4832             .get(..err.len()),
   4833             Some(err.as_slice())
   4834         );
   4835         // `null` `publicKey` is allowed when not using EdDSA, ES256, or RS256.
   4836         assert!(
   4837             serde_json::from_str::<Registration>(
   4838                 serde_json::json!({
   4839                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4840                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4841                     "response": {
   4842                         "clientDataJSON": b64_cdata_json,
   4843                         "authenticatorData": b64_adata,
   4844                         "transports": [],
   4845                         "publicKey": null,
   4846                         "publicKeyAlgorithm": -35i8,
   4847                         "attestationObject": b64_aobj,
   4848                     },
   4849                     "clientExtensionResults": {},
   4850                     "type": "public-key"
   4851                 })
   4852                 .to_string()
   4853                 .as_str()
   4854             )
   4855             .is_ok()
   4856         );
   4857         // `publicKeyAlgorithm` mismatch when `publicKey` is null.
   4858         err = Error::invalid_value(
   4859             Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()),
   4860             &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es384).as_str()
   4861         )
   4862         .to_string().into_bytes();
   4863         assert_eq!(
   4864             serde_json::from_str::<Registration>(
   4865                 serde_json::json!({
   4866                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   4867                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   4868                     "response": {
   4869                         "clientDataJSON": b64_cdata_json,
   4870                         "authenticatorData": b64_adata,
   4871                         "transports": [],
   4872                         "publicKey": null,
   4873                         "publicKeyAlgorithm": -7i8,
   4874                         "attestationObject": b64_aobj,
   4875                     },
   4876                     "clientExtensionResults": {},
   4877                     "type": "public-key"
   4878                 })
   4879                 .to_string()
   4880                 .as_str()
   4881             )
   4882             .unwrap_err()
   4883             .to_string()
   4884             .into_bytes()
   4885             .get(..err.len()),
   4886             Some(err.as_slice())
   4887         );
   4888     }
   4889     #[expect(clippy::unwrap_used, reason = "OK in tests")]
   4890     #[expect(clippy::indexing_slicing, reason = "comments justify correctness")]
   4891     #[expect(clippy::too_many_lines, reason = "a lot to test")]
   4892     #[test]
   4893     fn rs256_registration_deserialize_data_mismatch() {
   4894         let c_data_json = serde_json::json!({}).to_string();
   4895         let mut att_obj: [u8; 374] = [
   4896             cbor::MAP_3,
   4897             cbor::TEXT_3,
   4898             b'f',
   4899             b'm',
   4900             b't',
   4901             cbor::TEXT_4,
   4902             b'n',
   4903             b'o',
   4904             b'n',
   4905             b'e',
   4906             cbor::TEXT_7,
   4907             b'a',
   4908             b't',
   4909             b't',
   4910             b'S',
   4911             b't',
   4912             b'm',
   4913             b't',
   4914             cbor::MAP_0,
   4915             cbor::TEXT_8,
   4916             b'a',
   4917             b'u',
   4918             b't',
   4919             b'h',
   4920             b'D',
   4921             b'a',
   4922             b't',
   4923             b'a',
   4924             cbor::BYTES_INFO_25,
   4925             1,
   4926             87,
   4927             // `rpIdHash`.
   4928             0,
   4929             0,
   4930             0,
   4931             0,
   4932             0,
   4933             0,
   4934             0,
   4935             0,
   4936             0,
   4937             0,
   4938             0,
   4939             0,
   4940             0,
   4941             0,
   4942             0,
   4943             0,
   4944             0,
   4945             0,
   4946             0,
   4947             0,
   4948             0,
   4949             0,
   4950             0,
   4951             0,
   4952             0,
   4953             0,
   4954             0,
   4955             0,
   4956             0,
   4957             0,
   4958             0,
   4959             0,
   4960             // `flags`.
   4961             0b0100_0101,
   4962             // `signCount`.
   4963             0,
   4964             0,
   4965             0,
   4966             0,
   4967             // `aaguid`.
   4968             0,
   4969             0,
   4970             0,
   4971             0,
   4972             0,
   4973             0,
   4974             0,
   4975             0,
   4976             0,
   4977             0,
   4978             0,
   4979             0,
   4980             0,
   4981             0,
   4982             0,
   4983             0,
   4984             // `credentialIdLength`.
   4985             0,
   4986             16,
   4987             // `credentialId`.
   4988             0,
   4989             0,
   4990             0,
   4991             0,
   4992             0,
   4993             0,
   4994             0,
   4995             0,
   4996             0,
   4997             0,
   4998             0,
   4999             0,
   5000             0,
   5001             0,
   5002             0,
   5003             0,
   5004             // RSA COSE key.
   5005             cbor::MAP_4,
   5006             KTY,
   5007             RSA,
   5008             ALG,
   5009             cbor::NEG_INFO_25,
   5010             // RS256.
   5011             1,
   5012             0,
   5013             // `n`.
   5014             cbor::NEG_ONE,
   5015             cbor::BYTES_INFO_25,
   5016             1,
   5017             0,
   5018             // n. This will be overwritten later.
   5019             0,
   5020             0,
   5021             0,
   5022             0,
   5023             0,
   5024             0,
   5025             0,
   5026             0,
   5027             0,
   5028             0,
   5029             0,
   5030             0,
   5031             0,
   5032             0,
   5033             0,
   5034             0,
   5035             0,
   5036             0,
   5037             0,
   5038             0,
   5039             0,
   5040             0,
   5041             0,
   5042             0,
   5043             0,
   5044             0,
   5045             0,
   5046             0,
   5047             0,
   5048             0,
   5049             0,
   5050             0,
   5051             0,
   5052             0,
   5053             0,
   5054             0,
   5055             0,
   5056             0,
   5057             0,
   5058             0,
   5059             0,
   5060             0,
   5061             0,
   5062             0,
   5063             0,
   5064             0,
   5065             0,
   5066             0,
   5067             0,
   5068             0,
   5069             0,
   5070             0,
   5071             0,
   5072             0,
   5073             0,
   5074             0,
   5075             0,
   5076             0,
   5077             0,
   5078             0,
   5079             0,
   5080             0,
   5081             0,
   5082             0,
   5083             0,
   5084             0,
   5085             0,
   5086             0,
   5087             0,
   5088             0,
   5089             0,
   5090             0,
   5091             0,
   5092             0,
   5093             0,
   5094             0,
   5095             0,
   5096             0,
   5097             0,
   5098             0,
   5099             0,
   5100             0,
   5101             0,
   5102             0,
   5103             0,
   5104             0,
   5105             0,
   5106             0,
   5107             0,
   5108             0,
   5109             0,
   5110             0,
   5111             0,
   5112             0,
   5113             0,
   5114             0,
   5115             0,
   5116             0,
   5117             0,
   5118             0,
   5119             0,
   5120             0,
   5121             0,
   5122             0,
   5123             0,
   5124             0,
   5125             0,
   5126             0,
   5127             0,
   5128             0,
   5129             0,
   5130             0,
   5131             0,
   5132             0,
   5133             0,
   5134             0,
   5135             0,
   5136             0,
   5137             0,
   5138             0,
   5139             0,
   5140             0,
   5141             0,
   5142             0,
   5143             0,
   5144             0,
   5145             0,
   5146             0,
   5147             0,
   5148             0,
   5149             0,
   5150             0,
   5151             0,
   5152             0,
   5153             0,
   5154             0,
   5155             0,
   5156             0,
   5157             0,
   5158             0,
   5159             0,
   5160             0,
   5161             0,
   5162             0,
   5163             0,
   5164             0,
   5165             0,
   5166             0,
   5167             0,
   5168             0,
   5169             0,
   5170             0,
   5171             0,
   5172             0,
   5173             0,
   5174             0,
   5175             0,
   5176             0,
   5177             0,
   5178             0,
   5179             0,
   5180             0,
   5181             0,
   5182             0,
   5183             0,
   5184             0,
   5185             0,
   5186             0,
   5187             0,
   5188             0,
   5189             0,
   5190             0,
   5191             0,
   5192             0,
   5193             0,
   5194             0,
   5195             0,
   5196             0,
   5197             0,
   5198             0,
   5199             0,
   5200             0,
   5201             0,
   5202             0,
   5203             0,
   5204             0,
   5205             0,
   5206             0,
   5207             0,
   5208             0,
   5209             0,
   5210             0,
   5211             0,
   5212             0,
   5213             0,
   5214             0,
   5215             0,
   5216             0,
   5217             0,
   5218             0,
   5219             0,
   5220             0,
   5221             0,
   5222             0,
   5223             0,
   5224             0,
   5225             0,
   5226             0,
   5227             0,
   5228             0,
   5229             0,
   5230             0,
   5231             0,
   5232             0,
   5233             0,
   5234             0,
   5235             0,
   5236             0,
   5237             0,
   5238             0,
   5239             0,
   5240             0,
   5241             0,
   5242             0,
   5243             0,
   5244             0,
   5245             0,
   5246             0,
   5247             0,
   5248             0,
   5249             0,
   5250             0,
   5251             0,
   5252             0,
   5253             0,
   5254             0,
   5255             0,
   5256             0,
   5257             0,
   5258             0,
   5259             0,
   5260             0,
   5261             0,
   5262             0,
   5263             0,
   5264             0,
   5265             0,
   5266             0,
   5267             0,
   5268             0,
   5269             0,
   5270             0,
   5271             0,
   5272             0,
   5273             0,
   5274             0,
   5275             // `e`.
   5276             cbor::NEG_TWO,
   5277             cbor::BYTES | 3,
   5278             // e.
   5279             1,
   5280             0,
   5281             1,
   5282         ];
   5283         let n = [
   5284             111, 183, 124, 133, 38, 167, 70, 148, 44, 50, 30, 60, 121, 14, 38, 37, 96, 114, 107,
   5285             195, 248, 64, 79, 36, 237, 140, 43, 27, 94, 74, 102, 152, 135, 102, 184, 150, 186, 206,
   5286             185, 19, 165, 209, 48, 98, 98, 9, 3, 205, 208, 82, 250, 105, 132, 201, 73, 62, 60, 165,
   5287             100, 128, 153, 9, 41, 118, 66, 95, 236, 214, 73, 135, 197, 68, 184, 10, 27, 116, 204,
   5288             145, 50, 174, 58, 42, 183, 181, 119, 232, 126, 252, 217, 96, 162, 190, 103, 122, 64,
   5289             87, 145, 45, 32, 207, 17, 239, 223, 3, 35, 14, 112, 119, 124, 141, 123, 208, 239, 105,
   5290             81, 217, 151, 162, 190, 17, 88, 182, 176, 158, 81, 200, 42, 166, 133, 48, 23, 236, 55,
   5291             117, 248, 233, 151, 203, 122, 155, 231, 46, 177, 20, 20, 151, 64, 222, 239, 226, 7, 21,
   5292             254, 81, 202, 64, 232, 161, 235, 22, 51, 246, 207, 213, 0, 229, 138, 46, 222, 205, 157,
   5293             108, 139, 253, 230, 80, 50, 2, 122, 212, 163, 100, 180, 114, 12, 113, 52, 56, 99, 188,
   5294             42, 198, 212, 23, 182, 222, 56, 221, 200, 79, 96, 239, 221, 135, 10, 17, 106, 183, 56,
   5295             104, 68, 94, 198, 196, 35, 200, 83, 204, 26, 185, 204, 212, 31, 183, 19, 111, 233, 13,
   5296             72, 93, 53, 65, 111, 59, 242, 122, 160, 244, 162, 126, 38, 235, 156, 47, 88, 39, 132,
   5297             153, 79, 0, 133, 78, 7, 218, 165, 241,
   5298         ];
   5299         let e = 0x0001_0001u32;
   5300         let d = [
   5301             145, 79, 21, 97, 233, 3, 192, 194, 177, 68, 181, 80, 120, 197, 23, 44, 185, 74, 144, 0,
   5302             132, 149, 139, 11, 16, 224, 4, 112, 236, 94, 238, 97, 121, 124, 213, 145, 24, 253, 168,
   5303             35, 190, 205, 132, 115, 33, 201, 38, 253, 246, 180, 66, 155, 165, 46, 3, 254, 68, 108,
   5304             154, 247, 246, 45, 187, 0, 204, 96, 185, 157, 249, 174, 158, 38, 62, 244, 183, 76, 102,
   5305             6, 219, 92, 212, 138, 59, 147, 163, 219, 111, 39, 105, 21, 236, 196, 38, 255, 114, 247,
   5306             82, 104, 113, 204, 29, 152, 209, 219, 48, 239, 74, 129, 19, 247, 33, 239, 119, 166,
   5307             216, 152, 94, 138, 238, 164, 242, 129, 50, 150, 57, 20, 53, 224, 56, 241, 138, 97, 111,
   5308             215, 107, 212, 195, 146, 108, 143, 0, 229, 181, 171, 73, 152, 105, 146, 25, 243, 242,
   5309             140, 252, 248, 162, 247, 63, 168, 180, 20, 153, 120, 10, 248, 211, 1, 71, 127, 212,
   5310             249, 237, 203, 202, 48, 26, 216, 226, 228, 186, 13, 204, 70, 255, 240, 89, 255, 59, 83,
   5311             31, 253, 55, 43, 158, 90, 248, 83, 32, 159, 105, 57, 134, 34, 96, 18, 255, 245, 153,
   5312             162, 60, 91, 99, 220, 51, 44, 85, 114, 67, 125, 202, 65, 217, 245, 40, 8, 81, 165, 142,
   5313             24, 245, 127, 122, 247, 152, 212, 75, 45, 59, 90, 184, 234, 31, 147, 36, 8, 212, 45,
   5314             50, 23, 3, 25, 253, 87, 227, 79, 119, 161,
   5315         ];
   5316         let p = BigUint::from_bytes_le(
   5317             [
   5318                 215, 166, 5, 21, 11, 179, 41, 77, 198, 92, 165, 48, 77, 162, 42, 41, 206, 141, 60,
   5319                 69, 47, 164, 19, 92, 46, 72, 100, 238, 100, 53, 214, 197, 163, 185, 6, 140, 229,
   5320                 250, 195, 77, 8, 12, 5, 236, 178, 173, 86, 201, 43, 213, 165, 51, 108, 101, 161,
   5321                 99, 76, 240, 14, 234, 76, 197, 137, 53, 198, 168, 135, 205, 212, 198, 120, 29, 16,
   5322                 82, 98, 233, 236, 177, 12, 171, 141, 100, 107, 146, 33, 176, 125, 202, 172, 79,
   5323                 147, 179, 30, 62, 247, 206, 169, 19, 168, 114, 26, 73, 108, 178, 105, 84, 89, 191,
   5324                 168, 253, 228, 214, 54, 16, 212, 199, 111, 72, 3, 41, 247, 227, 165, 244, 32, 188,
   5325                 24, 247,
   5326             ]
   5327             .as_slice(),
   5328         );
   5329         let p_2 = BigUint::from_bytes_le(
   5330             [
   5331                 41, 25, 198, 240, 134, 206, 121, 57, 11, 5, 134, 192, 212, 77, 229, 197, 14, 78,
   5332                 85, 212, 190, 114, 179, 188, 21, 171, 174, 12, 104, 74, 15, 164, 136, 173, 62, 177,
   5333                 141, 213, 93, 102, 147, 83, 59, 124, 146, 59, 175, 213, 55, 27, 25, 248, 154, 29,
   5334                 39, 85, 50, 235, 134, 60, 203, 106, 186, 195, 190, 185, 71, 169, 142, 236, 92, 11,
   5335                 250, 187, 198, 8, 201, 184, 120, 178, 227, 87, 63, 243, 89, 227, 234, 184, 28, 252,
   5336                 112, 211, 193, 69, 23, 92, 5, 72, 93, 53, 69, 159, 73, 160, 105, 244, 249, 94, 214,
   5337                 173, 9, 236, 4, 255, 129, 11, 224, 140, 252, 168, 57, 143, 176, 241, 60, 219, 90,
   5338                 250,
   5339             ]
   5340             .as_slice(),
   5341         );
   5342         let key = RsaPrivateKey::from_components(
   5343             BigUint::from_bytes_le(n.as_slice()),
   5344             e.into(),
   5345             BigUint::from_bytes_le(d.as_slice()),
   5346             vec![p, p_2],
   5347         )
   5348         .unwrap()
   5349         .to_public_key();
   5350         let pub_key = key.to_public_key_der().unwrap();
   5351         let att_obj_len = att_obj.len();
   5352         let n_start_idx = att_obj_len - 261;
   5353         let e_meta_start_idx = n_start_idx + 256;
   5354         // Correct and won't `panic`.
   5355         att_obj[n_start_idx..e_meta_start_idx].copy_from_slice(key.n().to_bytes_be().as_slice());
   5356         let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes());
   5357         // Won't `panic`.
   5358         let b64_adata = base64url_nopad::encode(&att_obj[31..]);
   5359         let b64_key = base64url_nopad::encode(pub_key.as_bytes());
   5360         let b64_aobj = base64url_nopad::encode(att_obj.as_slice());
   5361         // Base case is valid.
   5362         assert!(
   5363             serde_json::from_str::<Registration>(
   5364                 serde_json::json!({
   5365                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   5366                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   5367                     "response": {
   5368                         "clientDataJSON": b64_cdata_json,
   5369                         "authenticatorData": b64_adata,
   5370                         "transports": [],
   5371                         "publicKey": b64_key,
   5372                         "publicKeyAlgorithm": -257i16,
   5373                         "attestationObject": b64_aobj,
   5374                     },
   5375                     "clientExtensionResults": {},
   5376                     "type": "public-key"
   5377                 })
   5378                 .to_string()
   5379                 .as_str()
   5380             )
   5381             .is_ok_and(
   5382                 |reg| reg.response.client_data_json == c_data_json.as_bytes()
   5383                     && reg.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj
   5384                     && reg.response.attestation_object_and_c_data_hash[att_obj_len..]
   5385                         == *Sha256::digest(c_data_json.as_bytes())
   5386                     && reg.response.transports.is_empty()
   5387                     && matches!(reg.authenticator_attachment, AuthenticatorAttachment::None)
   5388                     && reg.client_extension_results.cred_props.is_none()
   5389                     && reg.client_extension_results.prf.is_none()
   5390             )
   5391         );
   5392         // `publicKeyAlgorithm` mismatch.
   5393         let mut err = Error::invalid_value(
   5394             Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()),
   5395             &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Rs256).as_str()
   5396         )
   5397         .to_string().into_bytes();
   5398         assert_eq!(
   5399             serde_json::from_str::<Registration>(
   5400                 serde_json::json!({
   5401                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   5402                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   5403                     "response": {
   5404                         "clientDataJSON": b64_cdata_json,
   5405                         "authenticatorData": b64_adata,
   5406                         "transports": [],
   5407                         "publicKey": b64_key,
   5408                         "publicKeyAlgorithm": -8i8,
   5409                         "attestationObject": b64_aobj,
   5410                     },
   5411                     "clientExtensionResults": {},
   5412                     "type": "public-key"
   5413                 })
   5414                 .to_string()
   5415                 .as_str()
   5416             )
   5417             .unwrap_err()
   5418             .to_string()
   5419             .into_bytes()
   5420             .get(..err.len()),
   5421             Some(err.as_slice())
   5422         );
   5423         // Missing `publicKeyAlgorithm`.
   5424         err = Error::missing_field("publicKeyAlgorithm")
   5425             .to_string()
   5426             .into_bytes();
   5427         assert_eq!(
   5428             serde_json::from_str::<Registration>(
   5429                 serde_json::json!({
   5430                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   5431                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   5432                     "response": {
   5433                         "clientDataJSON": b64_cdata_json,
   5434                         "authenticatorData": b64_adata,
   5435                         "transports": [],
   5436                         "publicKey": b64_key,
   5437                         "attestationObject": b64_aobj,
   5438                     },
   5439                     "clientExtensionResults": {},
   5440                     "type": "public-key"
   5441                 })
   5442                 .to_string()
   5443                 .as_str()
   5444             )
   5445             .unwrap_err()
   5446             .to_string()
   5447             .into_bytes()
   5448             .get(..err.len()),
   5449             Some(err.as_slice())
   5450         );
   5451         // `null` `publicKeyAlgorithm`.
   5452         err = Error::invalid_type(Unexpected::Other("null"), &"publicKeyAlgorithm")
   5453             .to_string()
   5454             .into_bytes();
   5455         assert_eq!(
   5456             serde_json::from_str::<Registration>(
   5457                 serde_json::json!({
   5458                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   5459                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   5460                     "response": {
   5461                         "clientDataJSON": b64_cdata_json,
   5462                         "authenticatorData": b64_adata,
   5463                         "transports": [],
   5464                         "publicKey": b64_key,
   5465                         "publicKeyAlgorithm": null,
   5466                         "attestationObject": b64_aobj,
   5467                     },
   5468                     "clientExtensionResults": {},
   5469                     "type": "public-key"
   5470                 })
   5471                 .to_string()
   5472                 .as_str()
   5473             )
   5474             .unwrap_err()
   5475             .to_string()
   5476             .into_bytes()
   5477             .get(..err.len()),
   5478             Some(err.as_slice())
   5479         );
   5480         // `publicKey` mismatch.
   5481         let bad_pub_key = RsaPrivateKey::from_components(
   5482             BigUint::from_bytes_le(
   5483                 [
   5484                     175, 161, 161, 75, 52, 244, 72, 168, 29, 119, 33, 120, 3, 222, 231, 152, 222,
   5485                     119, 112, 83, 221, 237, 74, 174, 79, 216, 147, 251, 245, 94, 234, 114, 254, 21,
   5486                     17, 254, 8, 115, 75, 127, 150, 87, 59, 109, 230, 116, 85, 90, 11, 160, 63, 217,
   5487                     9, 38, 187, 250, 226, 183, 38, 164, 182, 218, 22, 19, 58, 189, 83, 219, 11,
   5488                     144, 15, 99, 151, 166, 46, 57, 17, 111, 189, 131, 142, 113, 85, 122, 188, 238,
   5489                     52, 21, 116, 125, 102, 195, 182, 165, 29, 156, 213, 182, 125, 156, 88, 56, 221,
   5490                     2, 98, 43, 210, 115, 32, 4, 105, 88, 181, 158, 207, 236, 162, 250, 253, 240,
   5491                     72, 8, 253, 50, 220, 247, 76, 170, 143, 68, 225, 231, 113, 64, 244, 17, 138,
   5492                     162, 233, 33, 2, 67, 11, 223, 188, 232, 152, 193, 20, 32, 243, 52, 64, 43, 2,
   5493                     243, 8, 77, 150, 232, 109, 148, 95, 127, 55, 71, 162, 34, 54, 83, 135, 52, 172,
   5494                     191, 32, 42, 106, 43, 211, 206, 100, 104, 110, 232, 5, 43, 120, 180, 166, 40,
   5495                     144, 233, 239, 103, 134, 103, 255, 224, 138, 184, 208, 137, 127, 36, 189, 143,
   5496                     248, 201, 2, 218, 51, 232, 96, 30, 83, 124, 109, 241, 23, 179, 247, 151, 238,
   5497                     212, 204, 44, 43, 223, 148, 241, 172, 10, 235, 155, 94, 68, 116, 24, 116, 191,
   5498                     86, 53, 127, 35, 133, 198, 204, 59, 76, 110, 16, 1, 15, 148, 135, 157,
   5499                 ]
   5500                 .as_slice(),
   5501             ),
   5502             0x0001_0001u32.into(),
   5503             BigUint::from_bytes_le(
   5504                 [
   5505                     129, 93, 123, 251, 104, 29, 84, 203, 116, 100, 75, 237, 111, 160, 12, 100, 172,
   5506                     76, 57, 178, 144, 235, 81, 61, 115, 243, 28, 40, 183, 22, 56, 150, 68, 38, 220,
   5507                     62, 233, 110, 48, 174, 35, 197, 244, 109, 148, 109, 36, 69, 69, 82, 225, 113,
   5508                     175, 6, 239, 27, 193, 101, 50, 239, 122, 102, 7, 46, 98, 79, 195, 116, 155,
   5509                     158, 138, 147, 51, 93, 24, 237, 246, 82, 14, 109, 144, 250, 239, 93, 63, 214,
   5510                     96, 130, 226, 134, 198, 145, 161, 11, 231, 97, 214, 180, 255, 95, 158, 88, 108,
   5511                     254, 243, 177, 133, 184, 92, 95, 148, 88, 55, 124, 245, 244, 84, 86, 4, 121,
   5512                     44, 231, 97, 176, 190, 29, 155, 40, 57, 69, 165, 80, 168, 9, 56, 43, 233, 6,
   5513                     14, 157, 112, 223, 64, 88, 141, 7, 65, 23, 64, 208, 6, 83, 61, 8, 182, 248,
   5514                     126, 84, 179, 163, 80, 238, 90, 133, 4, 14, 71, 177, 175, 27, 29, 151, 211,
   5515                     108, 162, 195, 7, 157, 167, 86, 169, 3, 87, 235, 89, 158, 237, 216, 31, 243,
   5516                     197, 62, 5, 84, 131, 230, 186, 248, 49, 12, 93, 244, 61, 135, 180, 17, 162,
   5517                     241, 13, 115, 241, 138, 219, 98, 155, 166, 191, 63, 12, 37, 1, 165, 178, 84,
   5518                     200, 72, 80, 41, 77, 136, 217, 141, 246, 209, 31, 243, 159, 71, 43, 246, 159,
   5519                     182, 171, 116, 12, 3, 142, 235, 218, 164, 70, 90, 147, 238, 42, 75,
   5520                 ]
   5521                 .as_slice(),
   5522             ),
   5523             vec![
   5524                 BigUint::from_bytes_le(
   5525                     [
   5526                         215, 199, 110, 28, 64, 16, 16, 109, 106, 152, 150, 124, 52, 166, 121, 92,
   5527                         242, 13, 0, 69, 7, 152, 72, 172, 118, 63, 156, 180, 140, 39, 53, 29, 197,
   5528                         224, 177, 48, 41, 221, 102, 65, 17, 185, 55, 62, 219, 152, 227, 7, 78, 219,
   5529                         14, 139, 71, 204, 144, 152, 14, 39, 247, 244, 165, 224, 234, 60, 213, 74,
   5530                         237, 30, 102, 177, 242, 138, 168, 31, 122, 47, 206, 155, 225, 113, 103,
   5531                         175, 152, 244, 27, 233, 112, 223, 248, 38, 215, 178, 20, 244, 8, 121, 26,
   5532                         11, 70, 122, 16, 85, 167, 87, 64, 216, 228, 227, 173, 57, 250, 8, 221, 38,
   5533                         12, 203, 212, 1, 112, 43, 72, 91, 225, 97, 228, 57, 154, 193,
   5534                     ]
   5535                     .as_slice(),
   5536                 ),
   5537                 BigUint::from_bytes_le(
   5538                     [
   5539                         233, 89, 204, 152, 31, 242, 8, 110, 38, 190, 111, 159, 105, 105, 45, 85,
   5540                         15, 244, 30, 250, 174, 226, 219, 111, 107, 191, 196, 135, 17, 123, 186,
   5541                         167, 85, 13, 120, 197, 159, 129, 78, 237, 152, 31, 230, 26, 229, 253, 197,
   5542                         211, 105, 204, 126, 142, 250, 55, 26, 172, 65, 160, 45, 6, 99, 86, 66, 238,
   5543                         107, 6, 98, 171, 93, 224, 201, 160, 31, 204, 82, 120, 228, 158, 238, 6,
   5544                         190, 12, 150, 153, 239, 95, 57, 71, 100, 239, 235, 155, 73, 200, 5, 225,
   5545                         127, 185, 46, 48, 243, 84, 33, 142, 17, 19, 20, 23, 215, 16, 114, 58, 211,
   5546                         14, 73, 148, 168, 252, 159, 252, 125, 57, 101, 211, 188, 12, 77, 208,
   5547                     ]
   5548                     .as_slice(),
   5549                 ),
   5550             ],
   5551         )
   5552         .unwrap()
   5553         .to_public_key();
   5554         err = Error::invalid_value(
   5555             Unexpected::Bytes([0; 32].as_slice()),
   5556             &format!(
   5557                 "DER-encoded public key to match the public key within the attestation object: Rsa(RsaPubKey({:?}, 65537))",
   5558                 // Correct and won't `panic`.
   5559                 &att_obj[n_start_idx..e_meta_start_idx],
   5560             )
   5561             .as_str(),
   5562         )
   5563         .to_string().into_bytes();
   5564         assert_eq!(serde_json::from_str::<Registration>(
   5565             serde_json::json!({
   5566                 "id": "AAAAAAAAAAAAAAAAAAAAAA",
   5567                 "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   5568                 "response": {
   5569                     "clientDataJSON": b64_cdata_json,
   5570                     "authenticatorData": b64_adata,
   5571                     "transports": [],
   5572                     "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()),
   5573                     "publicKeyAlgorithm": -257i16,
   5574                     "attestationObject": b64_aobj,
   5575                 },
   5576                 "clientExtensionResults": {},
   5577                 "type": "public-key"
   5578             })
   5579             .to_string()
   5580             .as_str()
   5581             )
   5582             .unwrap_err().to_string().into_bytes().get(..err.len()),
   5583             Some(err.as_slice())
   5584         );
   5585         // Missing `publicKey` when using EdDSA, ES256, or RS256.
   5586         err = Error::missing_field("publicKey").to_string().into_bytes();
   5587         assert_eq!(
   5588             serde_json::from_str::<Registration>(
   5589                 serde_json::json!({
   5590                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   5591                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   5592                     "response": {
   5593                         "clientDataJSON": b64_cdata_json,
   5594                         "authenticatorData": b64_adata,
   5595                         "transports": [],
   5596                         "publicKeyAlgorithm": -257i16,
   5597                         "attestationObject": b64_aobj,
   5598                     },
   5599                     "clientExtensionResults": {},
   5600                     "type": "public-key"
   5601                 })
   5602                 .to_string()
   5603                 .as_str()
   5604             )
   5605             .unwrap_err()
   5606             .to_string()
   5607             .into_bytes()
   5608             .get(..err.len()),
   5609             Some(err.as_slice())
   5610         );
   5611         // `null` `publicKey` when using EdDSA, ES256, or RS256.
   5612         err = Error::invalid_type(Unexpected::Other("null"), &"publicKey")
   5613             .to_string()
   5614             .into_bytes();
   5615         assert_eq!(
   5616             serde_json::from_str::<Registration>(
   5617                 serde_json::json!({
   5618                     "id": "AAAAAAAAAAAAAAAAAAAAAA",
   5619                     "rawId": "AAAAAAAAAAAAAAAAAAAAAA",
   5620                     "response": {
   5621                         "clientDataJSON": b64_cdata_json,
   5622                         "authenticatorData": b64_adata,
   5623                         "transports": [],
   5624                         "publicKey": null,
   5625                         "publicKeyAlgorithm": -257i16,
   5626                         "attestationObject": b64_aobj,
   5627                     },
   5628                     "clientExtensionResults": {},
   5629                     "type": "public-key"
   5630                 })
   5631                 .to_string()
   5632                 .as_str()
   5633             )
   5634             .unwrap_err()
   5635             .to_string()
   5636             .into_bytes()
   5637             .get(..err.len()),
   5638             Some(err.as_slice())
   5639         );
   5640     }
   5641 }