ser.rs (373797B)
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, MlDsa44PubKey, MlDsa65PubKey, MlDsa87PubKey, RsaPubKey, RsaPubKeyErr, 28 UncompressedP256PubKey, UncompressedP384PubKey, UncompressedPubKey, 29 }; 30 use core::fmt::{self, Display, Formatter}; 31 use p256::{ 32 NistP256, 33 elliptic_curve::{Curve, common::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 encoded key in a DER-encoded ASN.1 `SubjectPublicKeyInfo` 58 /// for an ML-DSA-87 public key. 59 const MLDSA87_HEADER_LEN: usize = 22; 60 /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for ML-DSA-87 public key. 61 const MLDSA87_LEN: usize = MLDSA87_HEADER_LEN + 2592; 62 /// Length of the header before the encoded key in a DER-encoded ASN.1 `SubjectPublicKeyInfo` 63 /// for an ML-DSA-65 public key. 64 const MLDSA65_HEADER_LEN: usize = 22; 65 /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for ML-DSA-65 public key. 66 const MLDSA65_LEN: usize = MLDSA65_HEADER_LEN + 1952; 67 /// Length of the header before the encoded key in a DER-encoded ASN.1 `SubjectPublicKeyInfo` 68 /// for an ML-DSA-44 public key. 69 const MLDSA44_HEADER_LEN: usize = 22; 70 /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for ML-DSA-44 public key. 71 const MLDSA44_LEN: usize = MLDSA44_HEADER_LEN + 1312; 72 /// Length of the header before the compressed y-coordinate in a DER-encoded ASN.1 `SubjectPublicKeyInfo` 73 /// for an Ed25519 public key. 74 const ED25519_HEADER_LEN: usize = 12; 75 /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for Ed25519 public key. 76 const ED25519_LEN: usize = ED25519_HEADER_LEN + ed25519_dalek::PUBLIC_KEY_LENGTH; 77 /// `ED25519_LEN` as a `u8`. 78 // `44 as u8` is clearly OK. 79 #[expect( 80 clippy::as_conversions, 81 clippy::cast_possible_truncation, 82 reason = "comments above justify their correctness" 83 )] 84 const ED25519_LEN_U8: u8 = ED25519_LEN as u8; 85 /// Length of the header before the uncompressed SEC- 1 pubic key in a DER-encoded ASN.1 `SubjectPublicKeyInfo` 86 /// for an uncompressed ECDSA public key based on secp256r1/P-256. 87 const P256_HEADER_LEN: usize = 27; 88 /// Number of bytes the x-coordinate takes in an uncompressed P-256 public key. 89 const P256_X_LEN: usize = <NistP256 as Curve>::FieldBytesSize::INT; 90 /// Number of bytes the y-coordinate takes in an uncompressed P-256 public key. 91 const P256_Y_LEN: usize = <NistP256 as Curve>::FieldBytesSize::INT; 92 /// Number of bytes the x and y coordinates take in an uncompressed P-256 public key when concatenated together. 93 const P256_COORD_LEN: usize = P256_X_LEN + P256_Y_LEN; 94 /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for an uncompressed SEC-1 ECDSA public key 95 /// based on secp256r1/P-256. 96 const P256_LEN: usize = P256_HEADER_LEN + P256_COORD_LEN; 97 /// `P256_LEN` as a `u8`. 98 // `91 as u8` is clearly OK. 99 #[expect( 100 clippy::as_conversions, 101 clippy::cast_possible_truncation, 102 reason = "comments above justify their correctness" 103 )] 104 const P256_LEN_U8: u8 = P256_LEN as u8; 105 /// Length of the header before the uncompressed SEC- 1 pubic key in a DER-encoded ASN.1 `SubjectPublicKeyInfo` 106 /// for an uncompressed ECDSA public key based on secp384r1/P-384. 107 const P384_HEADER_LEN: usize = 24; 108 /// Number of bytes the x-coordinate takes in an uncompressed P-384 public key. 109 const P384_X_LEN: usize = <NistP384 as Curve>::FieldBytesSize::INT; 110 /// Number of bytes the y-coordinate takes in an uncompressed P-384 public key. 111 const P384_Y_LEN: usize = <NistP384 as Curve>::FieldBytesSize::INT; 112 /// Number of bytes the x and y coordinates take in an uncompressed P-384 public key when concatenated together. 113 const P384_COORD_LEN: usize = P384_X_LEN + P384_Y_LEN; 114 /// Length of a DER-encoded ASN.1 `SubjectPublicKeyInfo` for an uncompressed SEC-1 ECDSA public key 115 /// based on secp384r1/P-384. 116 const P384_LEN: usize = P384_HEADER_LEN + P384_COORD_LEN; 117 /// `P384_LEN` as a `u8`. 118 // `120 as u8` is clearly OK. 119 #[expect( 120 clippy::as_conversions, 121 clippy::cast_possible_truncation, 122 reason = "comments above justify their correctness" 123 )] 124 const P384_LEN_U8: u8 = P384_LEN as u8; 125 /// Error returned from [`SubjectPublicKeyInfo::from_der`]. 126 pub(super) enum SubjectPublicKeyInfoErr { 127 /// The length of the DER-encoded ML-DSA-87 key was invalid. 128 MlDsa87Len, 129 /// The header of the DER-encoded ML-DSA-87 key was invalid. 130 MlDsa87Header, 131 /// The length of the DER-encoded ML-DSA-65 key was invalid. 132 MlDsa65Len, 133 /// The header of the DER-encoded ML-DSA-65 key was invalid. 134 MlDsa65Header, 135 /// The length of the DER-encoded ML-DSA-44 key was invalid. 136 MlDsa44Len, 137 /// The header of the DER-encoded ML-DSA-44 key was invalid. 138 MlDsa44Header, 139 /// The length of the DER-encoded Ed25519 key was invalid. 140 Ed25519Len, 141 /// The header of the DER-encoded Ed25519 key was invalid. 142 Ed25519Header, 143 /// The length of the DER-encoded P-256 key was invalid. 144 P256Len, 145 /// The header of the DER-encoded P-256 key was invalid. 146 P256Header, 147 /// The length of the DER-encoded P-384 key was invalid. 148 P384Len, 149 /// The header of the DER-encoded P-384 key was invalid. 150 P384Header, 151 /// The length of the DER-encoded RSA key was invalid. 152 RsaLen, 153 /// The DER-encoding of the RSA key was invalid. 154 RsaEncoding, 155 /// The exponent of the DER-encoded RSA key was too large. 156 RsaExponentTooLarge, 157 /// The DER-encoded RSA key was not a valid [`RsaPubKey`]. 158 RsaPubKey(RsaPubKeyErr), 159 } 160 impl Display for SubjectPublicKeyInfoErr { 161 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 162 match *self { 163 Self::MlDsa87Len => { 164 f.write_str("length of the DER-encoded ML-DSA-87 key was invalid") 165 } 166 Self::MlDsa87Header => { 167 f.write_str("header of the DER-encoded ML-DSA-87 key was invalid") 168 } 169 Self::MlDsa65Len => { 170 f.write_str("length of the DER-encoded ML-DSA-65 key was invalid") 171 } 172 Self::MlDsa65Header => { 173 f.write_str("header of the DER-encoded ML-DSA-65 key was invalid") 174 } 175 Self::MlDsa44Len => { 176 f.write_str("length of the DER-encoded ML-DSA-44 key was invalid") 177 } 178 Self::MlDsa44Header => { 179 f.write_str("header of the DER-encoded ML-DSA-44 key was invalid") 180 } 181 Self::Ed25519Len => { 182 f.write_str("length of the DER-encoded Ed25519 key was invalid") 183 } 184 Self::Ed25519Header => { 185 f.write_str("header of the DER-encoded Ed25519 key was invalid") 186 } 187 Self::P256Len => f.write_str("length of the DER-encoded P-256 key was invalid"), 188 Self::P256Header => f.write_str("header of the DER-encoded P-256 key was invalid"), 189 Self::P384Len => f.write_str("length of the DER-encoded P-384 key was invalid"), 190 Self::P384Header => f.write_str("header of the DER-encoded P-384 key was invalid"), 191 Self::RsaLen => f.write_str("length of the DER-encoded RSA key was invalid"), 192 Self::RsaEncoding => { 193 f.write_str("the DER-encoding of the RSA public key was invalid") 194 } 195 Self::RsaExponentTooLarge => { 196 f.write_str("the DER-encoded RSA public key had an exponent that was too large") 197 } 198 Self::RsaPubKey(err) => { 199 write!(f, "the DER-encoded RSA public was not valid: {err}") 200 } 201 } 202 } 203 } 204 /// Types that can be deserialized from the DER-encoded ASN.1 `SubjectPublicKeyInfo` as defined in 205 /// [RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280#appendix-A.1) and other applicable RFCs 206 /// and documents (e.g., [ITU-T X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en)). 207 pub(super) trait SubjectPublicKeyInfo<'a>: Sized { 208 /// Transforms the DER-encoded ASN.1 `SubjectPublicKeyInfo` into `Self`. 209 /// 210 /// # Errors 211 /// 212 /// Errors iff `der` does not conform. 213 #[expect(single_use_lifetimes, reason = "false positive")] 214 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr>; 215 } 216 impl<'a> SubjectPublicKeyInfo<'a> for MlDsa87PubKey<&'a [u8]> { 217 #[expect(single_use_lifetimes, reason = "false positive")] 218 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 219 /// ```asn 220 /// SubjectPublicKeyInfo ::= SEQUENCE { 221 /// algorithm AlgorithmIdentifier, 222 /// subjectPublicKey BIT STRING 223 /// } 224 /// 225 /// AlgorithmIdentifier ::= SEQUENCE { 226 /// algorithm OBJECT IDENTIFIER, 227 /// parameters ANY DEFINED BY algorithm OPTIONAL 228 /// } 229 /// ``` 230 /// 231 /// [RFC 9882](https://www.rfc-editor.org/rfc/rfc9882.html) requires parameters to not exist 232 /// in `AlgorithmIdentifier`. 233 /// 234 /// RFC 9882 defines the OID as 2.16.840.1.101.3.4.3.19 which is encoded as 96.134.72.1.101.3.4.3.19 235 /// per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 236 /// 237 /// [FIPS 204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf) defines the bitstring as a reinterpretation of the byte string. 238 const HEADER: [u8; MLDSA87_HEADER_LEN] = [ 239 CONSTRUCTED_SEQUENCE, 240 130, 241 10, 242 50, 243 CONSTRUCTED_SEQUENCE, 244 9 + 2, 245 OID, 246 9, 247 96, 248 134, 249 72, 250 1, 251 101, 252 3, 253 4, 254 3, 255 19, 256 BITSTRING, 257 130, 258 10, 259 33, 260 // The number of unused bits. 261 0, 262 ]; 263 der.split_at_checked(HEADER.len()) 264 .ok_or(SubjectPublicKeyInfoErr::MlDsa87Len) 265 .and_then(|(header, rem)| { 266 if header == HEADER { 267 if rem.len() == 2592 { 268 Ok(Self(rem)) 269 } else { 270 Err(SubjectPublicKeyInfoErr::MlDsa87Len) 271 } 272 } else { 273 Err(SubjectPublicKeyInfoErr::MlDsa87Header) 274 } 275 }) 276 } 277 } 278 impl<'a> SubjectPublicKeyInfo<'a> for MlDsa65PubKey<&'a [u8]> { 279 #[expect(single_use_lifetimes, reason = "false positive")] 280 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 281 /// ```asn 282 /// SubjectPublicKeyInfo ::= SEQUENCE { 283 /// algorithm AlgorithmIdentifier, 284 /// subjectPublicKey BIT STRING 285 /// } 286 /// 287 /// AlgorithmIdentifier ::= SEQUENCE { 288 /// algorithm OBJECT IDENTIFIER, 289 /// parameters ANY DEFINED BY algorithm OPTIONAL 290 /// } 291 /// ``` 292 /// 293 /// [RFC 9882](https://www.rfc-editor.org/rfc/rfc9882.html) requires parameters to not exist 294 /// in `AlgorithmIdentifier`. 295 /// 296 /// RFC 9882 defines the OID as 2.16.840.1.101.3.4.3.18 which is encoded as 96.134.72.1.101.3.4.3.18 297 /// per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 298 /// 299 /// [FIPS 204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf) defines the bitstring as a reinterpretation of the byte string. 300 const HEADER: [u8; MLDSA65_HEADER_LEN] = [ 301 CONSTRUCTED_SEQUENCE, 302 130, 303 7, 304 178, 305 CONSTRUCTED_SEQUENCE, 306 9 + 2, 307 OID, 308 9, 309 96, 310 134, 311 72, 312 1, 313 101, 314 3, 315 4, 316 3, 317 18, 318 BITSTRING, 319 130, 320 7, 321 161, 322 // The number of unused bits. 323 0, 324 ]; 325 der.split_at_checked(HEADER.len()) 326 .ok_or(SubjectPublicKeyInfoErr::MlDsa65Len) 327 .and_then(|(header, rem)| { 328 if header == HEADER { 329 if rem.len() == 1952 { 330 Ok(Self(rem)) 331 } else { 332 Err(SubjectPublicKeyInfoErr::MlDsa65Len) 333 } 334 } else { 335 Err(SubjectPublicKeyInfoErr::MlDsa65Header) 336 } 337 }) 338 } 339 } 340 impl<'a> SubjectPublicKeyInfo<'a> for MlDsa44PubKey<&'a [u8]> { 341 #[expect(single_use_lifetimes, reason = "false positive")] 342 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 343 /// ```asn 344 /// SubjectPublicKeyInfo ::= SEQUENCE { 345 /// algorithm AlgorithmIdentifier, 346 /// subjectPublicKey BIT STRING 347 /// } 348 /// 349 /// AlgorithmIdentifier ::= SEQUENCE { 350 /// algorithm OBJECT IDENTIFIER, 351 /// parameters ANY DEFINED BY algorithm OPTIONAL 352 /// } 353 /// ``` 354 /// 355 /// [RFC 9882](https://www.rfc-editor.org/rfc/rfc9882.html) requires parameters to not exist 356 /// in `AlgorithmIdentifier`. 357 /// 358 /// RFC 9882 defines the OID as 2.16.840.1.101.3.4.3.17 which is encoded as 96.134.72.1.101.3.4.3.17 359 /// per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 360 /// 361 /// [FIPS 204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf) defines the bitstring as a reinterpretation of the byte string. 362 const HEADER: [u8; MLDSA44_HEADER_LEN] = [ 363 CONSTRUCTED_SEQUENCE, 364 130, 365 5, 366 50, 367 CONSTRUCTED_SEQUENCE, 368 9 + 2, 369 OID, 370 9, 371 96, 372 134, 373 72, 374 1, 375 101, 376 3, 377 4, 378 3, 379 17, 380 BITSTRING, 381 130, 382 5, 383 33, 384 // The number of unused bits. 385 0, 386 ]; 387 der.split_at_checked(HEADER.len()) 388 .ok_or(SubjectPublicKeyInfoErr::MlDsa44Len) 389 .and_then(|(header, rem)| { 390 if header == HEADER { 391 if rem.len() == 1312 { 392 Ok(Self(rem)) 393 } else { 394 Err(SubjectPublicKeyInfoErr::MlDsa44Len) 395 } 396 } else { 397 Err(SubjectPublicKeyInfoErr::MlDsa44Header) 398 } 399 }) 400 } 401 } 402 impl<'a> SubjectPublicKeyInfo<'a> for Ed25519PubKey<&'a [u8]> { 403 #[expect(single_use_lifetimes, reason = "false positive")] 404 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 405 /// ```asn 406 /// SubjectPublicKeyInfo ::= SEQUENCE { 407 /// algorithm AlgorithmIdentifier, 408 /// subjectPublicKey BIT STRING 409 /// } 410 /// 411 /// AlgorithmIdentifier ::= SEQUENCE { 412 /// algorithm OBJECT IDENTIFIER, 413 /// parameters ANY DEFINED BY algorithm OPTIONAL 414 /// } 415 /// ``` 416 /// 417 /// [RFC 8410](https://www.rfc-editor.org/rfc/rfc8410#section-3) requires parameters to not exist 418 /// in `AlgorithmIdentifier`. 419 /// 420 /// RFC 8410 defines the OID as 1.3.101.112 which is encoded as 43.101.112 421 /// per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 422 /// 423 /// RFC 8410 defines the bitstring as a reinterpretation of the byte string. 424 const HEADER: [u8; ED25519_HEADER_LEN] = [ 425 CONSTRUCTED_SEQUENCE, 426 // `ED25519_LEN_U8` is the length of the entire payload; thus we subtract 427 // the "consumed" length. 428 ED25519_LEN_U8 - 2, 429 CONSTRUCTED_SEQUENCE, 430 // AlgorithmIdentifier only contains the OID; thus it's length is 5. 431 3 + 2, 432 OID, 433 3, 434 43, 435 101, 436 112, 437 BITSTRING, 438 // `ED25519_LEN_U8` is the length of the entire payload; thus we subtract 439 // the "consumed" length. 440 ED25519_LEN_U8 - 11, 441 // The number of unused bits. 442 0, 443 ]; 444 der.split_at_checked(HEADER.len()) 445 .ok_or(SubjectPublicKeyInfoErr::Ed25519Len) 446 .and_then(|(header, rem)| { 447 if header == HEADER { 448 if rem.len() == ed25519_dalek::PUBLIC_KEY_LENGTH { 449 Ok(Self(rem)) 450 } else { 451 Err(SubjectPublicKeyInfoErr::Ed25519Len) 452 } 453 } else { 454 Err(SubjectPublicKeyInfoErr::Ed25519Header) 455 } 456 }) 457 } 458 } 459 impl<'a> SubjectPublicKeyInfo<'a> for UncompressedP256PubKey<'a> { 460 #[expect(single_use_lifetimes, reason = "false positive")] 461 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 462 // ```asn 463 // SubjectPublicKeyInfo ::= SEQUENCE { 464 // algorithm AlgorithmIdentifier, 465 // subjectPublicKey BIT STRING 466 // } 467 // 468 // AlgorithmIdentifier ::= SEQUENCE { 469 // algorithm OBJECT IDENTIFIER, 470 // parameters ANY DEFINED BY algorithm OPTIONAL 471 // } 472 // 473 // ECParameters ::= CHOICE { 474 // namedCurve OBJECT IDENTIFIER 475 // -- implicitCurve NULL 476 // -- specifiedCurve SpecifiedECDomain 477 // } 478 // ``` 479 // [RFC 5480](https://www.rfc-editor.org/rfc/rfc5480#section-2.1.1) requires parameters to exist and 480 // be of the `ECParameters` form with the requirement that `namedCurve` is chosen. 481 // 482 // RFC 5480 defines the OID for id-ecPublicKey as 1.2.840.10045.2.1 and the OID for the namedCurve 483 // 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 484 // 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). 485 // 486 // [RFC 5480](https://www.rfc-editor.org/rfc/rfc5480#section-5) only requires support for the 487 // uncompressed form and only states that the compressed form MAY be supported. In practice this means 488 // DER-encoded payloads almost always are of the uncompressed form for compatibility reasons. This in 489 // conjunction with the fact the COSE key is required to be in the uncompressed form means we only support 490 // DER-encoded payloads containing uncompressed keys. 491 // 492 // [SEC 1](https://secg.org/sec1-v2.pdf) defines the point as an octet string, and the conversion 493 // to a bitstring simply requires reinterpreting the octet string as a bitstring. 494 495 /// Header of the DER-encoded payload before the public key. 496 const HEADER: [u8; P256_HEADER_LEN] = [ 497 CONSTRUCTED_SEQUENCE, 498 // `P256_LEN_U8` is the length of the entire payload; thus we subtract 499 // the "consumed" length. 500 P256_LEN_U8 - 2, 501 CONSTRUCTED_SEQUENCE, 502 7 + 2 + 8 + 2, 503 OID, 504 7, 505 42, 506 134, 507 72, 508 206, 509 61, 510 2, 511 1, 512 OID, 513 8, 514 42, 515 134, 516 72, 517 206, 518 61, 519 3, 520 1, 521 7, 522 BITSTRING, 523 // `P256_LEN_U8` is the length of the entire payload; thus we subtract 524 // the "consumed" length. 525 P256_LEN_U8 - 25, 526 // The number of unused bits. 527 0, 528 // SEC-1 tag for an uncompressed key. 529 4, 530 ]; 531 der.split_at_checked(HEADER.len()) 532 .ok_or(SubjectPublicKeyInfoErr::P256Len) 533 .and_then(|(header, header_rem)| { 534 if header == HEADER { 535 header_rem 536 .split_at_checked(P256_X_LEN) 537 .ok_or(SubjectPublicKeyInfoErr::P256Len) 538 .and_then(|(x, y)| { 539 if y.len() == P256_Y_LEN { 540 Ok(Self(x, y)) 541 } else { 542 Err(SubjectPublicKeyInfoErr::P256Len) 543 } 544 }) 545 } else { 546 Err(SubjectPublicKeyInfoErr::P256Header) 547 } 548 }) 549 } 550 } 551 impl<'a> SubjectPublicKeyInfo<'a> for UncompressedP384PubKey<'a> { 552 #[expect(single_use_lifetimes, reason = "false positive")] 553 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 554 // ```asn 555 // SubjectPublicKeyInfo ::= SEQUENCE { 556 // algorithm AlgorithmIdentifier, 557 // subjectPublicKey BIT STRING 558 // } 559 // 560 // AlgorithmIdentifier ::= SEQUENCE { 561 // algorithm OBJECT IDENTIFIER, 562 // parameters ANY DEFINED BY algorithm OPTIONAL 563 // } 564 // 565 // ECParameters ::= CHOICE { 566 // namedCurve OBJECT IDENTIFIER 567 // -- implicitCurve NULL 568 // -- specifiedCurve SpecifiedECDomain 569 // } 570 // ``` 571 // [RFC 5480](https://www.rfc-editor.org/rfc/rfc5480#section-2.1.1) requires parameters to exist and 572 // be of the `ECParameters` form with the requirement that `namedCurve` is chosen. 573 // 574 // RFC 5480 defines the OID for id-ecPublicKey as 1.2.840.10045.2.1 and the OID for the namedCurve 575 // secp384r1 as 1.3.132.0.34. The former OID is encoded as 42.134.72.206.61.2.1 and the latter 576 // is encoded as 43.129.4.0.34 per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 577 // 578 // [RFC 5480](https://www.rfc-editor.org/rfc/rfc5480#section-5) only requires support for the 579 // uncompressed form and only states that the compressed form MAY be supported. In practice this means 580 // DER-encoded payloads almost always are of the uncompressed form for compatibility reasons. This in 581 // conjunction with the fact the COSE key is required to be in the uncompressed form means we only support 582 // DER-encoded payloads containing uncompressed keys. 583 // 584 // [SEC 1](https://secg.org/sec1-v2.pdf) defines the point as an octet string, and the conversion 585 // to a bitstring simply requires reinterpreting the octet string as a bitstring. 586 587 /// Header of the DER-encoded payload before the public key. 588 const HEADER: [u8; P384_HEADER_LEN] = [ 589 CONSTRUCTED_SEQUENCE, 590 // `P384_LEN_U8` is the length of the entire payload; thus we subtract 591 // the "consumed" length. 592 P384_LEN_U8 - 2, 593 CONSTRUCTED_SEQUENCE, 594 7 + 2 + 5 + 2, 595 OID, 596 7, 597 42, 598 134, 599 72, 600 206, 601 61, 602 2, 603 1, 604 OID, 605 5, 606 43, 607 129, 608 4, 609 0, 610 34, 611 BITSTRING, 612 // `P384_LEN_U8` is the length of the entire payload; thus we subtract 613 // the "consumed" length. 614 P384_LEN_U8 - 22, 615 // The number of unused bits. 616 0, 617 // SEC-1 tag for an uncompressed key. 618 4, 619 ]; 620 der.split_at_checked(HEADER.len()) 621 .ok_or(SubjectPublicKeyInfoErr::P384Len) 622 .and_then(|(header, header_rem)| { 623 if header == HEADER { 624 header_rem 625 .split_at_checked(P384_X_LEN) 626 .ok_or(SubjectPublicKeyInfoErr::P384Len) 627 .and_then(|(x, y)| { 628 if y.len() == P384_Y_LEN { 629 Ok(Self(x, y)) 630 } else { 631 Err(SubjectPublicKeyInfoErr::P384Len) 632 } 633 }) 634 } else { 635 Err(SubjectPublicKeyInfoErr::P384Header) 636 } 637 }) 638 } 639 } 640 impl<'a> SubjectPublicKeyInfo<'a> for RsaPubKey<&'a [u8]> { 641 #[expect(single_use_lifetimes, reason = "false positive")] 642 #[expect( 643 clippy::arithmetic_side_effects, 644 clippy::big_endian_bytes, 645 clippy::indexing_slicing, 646 clippy::missing_asserts_for_indexing, 647 reason = "comments justify their correctness" 648 )] 649 #[expect( 650 clippy::too_many_lines, 651 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." 652 )] 653 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 654 // ```asn 655 // SubjectPublicKeyInfo ::= SEQUENCE { 656 // algorithm AlgorithmIdentifier, 657 // subjectPublicKey BIT STRING 658 // } 659 // 660 // AlgorithmIdentifier ::= SEQUENCE { 661 // algorithm OBJECT IDENTIFIER, 662 // parameters ANY DEFINED BY algorithm OPTIONAL 663 // } 664 // 665 // RSAPublicKey ::= SEQUENCE { 666 // modulus INTEGER, -- n 667 // publicExponent INTEGER --e 668 // } 669 // 670 // pkcs-1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) 671 // rsadsi(113549) pkcs(1) 1 672 // } 673 // 674 // rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1} 675 // ``` 676 // [RFC 3279](https://www.rfc-editor.org/rfc/rfc3279#section-2.3.1) requires parameters to exist and 677 // be null. 678 // 679 // RFC 3279 defines the OID for rsaEncryption as 1.2.840.113549.1.1.1 which is encoded as 680 // 42.134.72.134.247.13.1.1.1 per [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en). 681 // 682 // Note we only allow moduli that are 256 to 2048 bytes in length inclusively. Additionally 683 // we only allow `u32` exponents; consequently all lengths that include the modulus will always be 684 // encoded with two bytes. 685 686 /// `AlgorithmIdentifier` header including the `BITSTRING` and number of bytes the length 687 /// is encoded in of the `BITSTRING` type of `subjectPublicKey` 688 /// (130-128 = 2 bytes to encode the length). 689 const ALG_OID_HEADER: [u8; 17] = [ 690 CONSTRUCTED_SEQUENCE, 691 9 + 2 + 2, 692 OID, 693 9, 694 42, 695 134, 696 72, 697 134, 698 247, 699 13, 700 1, 701 1, 702 1, 703 NULL, 704 0, 705 BITSTRING, 706 130, 707 ]; 708 /// `CONSTRUCTED_SEQUENCE` whose length is encoded in two bytes. 709 const SEQ_LONG: [u8; 2] = [CONSTRUCTED_SEQUENCE, 130]; 710 /// `INTEGER` whose length is encoded in two bytes. 711 const INT_LONG: [u8; 2] = [INTEGER, 130]; 712 der.split_at_checked(SEQ_LONG.len()) 713 .ok_or(SubjectPublicKeyInfoErr::RsaLen) 714 .and_then(|(seq, seq_rem)| { 715 if seq == SEQ_LONG { 716 seq_rem 717 .split_at_checked(2) 718 .ok_or(SubjectPublicKeyInfoErr::RsaLen) 719 .and_then(|(seq_len, seq_len_rem)| { 720 let mut len = [0; 2]; 721 len.copy_from_slice(seq_len); 722 let rem_len = usize::from(u16::from_be_bytes(len)); 723 if rem_len == seq_len_rem.len() { 724 if rem_len > 255 { 725 // We can safely split here since we know `seq_len_rem` is at least 726 // 256 which is greater than `ALG_OID_HEADER.len()`. 727 let (a_oid, a_oid_rem) = seq_len_rem.split_at(ALG_OID_HEADER.len()); 728 if a_oid == ALG_OID_HEADER { 729 // `a_oid_rem.len()` is at least 239, so splitting is fine. 730 let (bit_str_len_enc, bit_str_val) = a_oid_rem.split_at(2); 731 let mut bit_string_len = [0; 2]; 732 bit_string_len.copy_from_slice(bit_str_len_enc); 733 let bit_str_val_len = usize::from(u16::from_be_bytes(bit_string_len)); 734 if bit_str_val_len == bit_str_val.len() { 735 if bit_str_val_len > 255 { 736 // `bit_str_val.len() > 255`, so splitting is fine. 737 let (unused_bits, bits_rem) = bit_str_val.split_at(1); 738 if unused_bits == [0] { 739 // We can safely split here since we know `bits_rem.len()` is at least 740 // 255. 741 let (rsa_seq, rsa_seq_rem) = bits_rem.split_at(SEQ_LONG.len()); 742 if rsa_seq == SEQ_LONG { 743 // `rsa_seq_rem.len()` is at least 253, so splitting is fine. 744 let (rsa_seq_len_enc, rsa_seq_len_enc_rem) = rsa_seq_rem.split_at(2); 745 let mut rsa_seq_len = [0; 2]; 746 rsa_seq_len.copy_from_slice(rsa_seq_len_enc); 747 let rsa_key_info_len = usize::from(u16::from_be_bytes(rsa_seq_len)); 748 if rsa_key_info_len == rsa_seq_len_enc_rem.len() { 749 if rsa_key_info_len > 255 { 750 // We can safely split here since we know `rsa_seq_len_enc_rem.len()` 751 // is at least 256. 752 let (n_meta, n_meta_rem) = rsa_seq_len_enc_rem.split_at(INT_LONG.len()); 753 if n_meta == INT_LONG { 754 // `n_meta_rem.len()` is at least 254, so splitting is fine. 755 let (n_len_enc, n_len_enc_rem) = n_meta_rem.split_at(2); 756 let mut n_len = [0; 2]; 757 n_len.copy_from_slice(n_len_enc); 758 let mod_len = usize::from(u16::from_be_bytes(n_len)); 759 if mod_len > 255 { 760 n_len_enc_rem.split_at_checked(mod_len).ok_or(SubjectPublicKeyInfoErr::RsaLen).and_then(|(mut n, n_rem)| { 761 // `n.len() > 255`, so indexing is fine. 762 let n_first = n[0]; 763 // DER integers are signed; thus the most significant bit must be 0. 764 // DER integers are minimally encoded; thus when a leading 0 exists, 765 // the second byte must be at least 128. 766 // `n.len() > 255`, so indexing is fine. 767 if n_first < 128 && (n_first != 0 || n[1] > 127) { 768 if n_first == 0 { 769 // `n.len() > 255`, so indexing is fine. 770 // We must remove the leading 0. 771 n = &n[1..]; 772 } 773 n_rem.split_first().ok_or(SubjectPublicKeyInfoErr::RsaLen).and_then(|(e_type, e_type_rem)| { 774 if *e_type == INTEGER { 775 e_type_rem.split_first().ok_or(SubjectPublicKeyInfoErr::RsaLen).and_then(|(e_len, e_len_rem)| { 776 let e_len_usize = usize::from(*e_len); 777 if e_len_usize == e_len_rem.len() { 778 e_len_rem.first().ok_or(SubjectPublicKeyInfoErr::RsaLen).and_then(|&e_first| { 779 // DER integers are signed; thus the most significant bit must be 0. 780 if e_first < 128 { 781 // `RsaPubKey` only allows `u32` exponents, which means we only care 782 // about lengths up to 5. 783 match e_len_usize { 784 1 => Ok(u32::from(e_first)), 785 2..=5 => if e_first == 0 { 786 // DER integers are minimally encoded; thus when a leading 787 // 0 exists, the second byte must be at least 128. 788 // We know the length is at least 2; thus this won't `panic`. 789 if e_len_rem[1] > 127 { 790 let mut e = [0; 4]; 791 if e_len_usize == 5 { 792 // We know the length is at least 2; thus this won't `panic`. 793 e.copy_from_slice(&e_len_rem[1..]); 794 } else { 795 // `e.len() == 4` and `e_len_usize` is at most 4; thus underflow 796 // won't occur nor will indexing `panic`. `e` is big-endian, 797 // so we start from the right. 798 e[4 - e_len_usize..].copy_from_slice(e_len_rem); 799 } 800 Ok(u32::from_be_bytes(e)) 801 } else { 802 Err(SubjectPublicKeyInfoErr::RsaEncoding) 803 } 804 } else if e_len_usize == 5 { 805 // 5 bytes are only possible for `INTEGER`s that 806 // are greater than `i32::MAX`, which will be encoded 807 // with a leading 0. 808 Err(SubjectPublicKeyInfoErr::RsaEncoding) 809 } else { 810 let mut e = [0; 4]; 811 // `e.len() == 4` and `e_len_usize` is at most 4; thus underflow 812 // won't occur nor will indexing `panic`. `e` is big-endian, 813 // so we start from the right. 814 e[4 - e_len_usize..].copy_from_slice(e_len_rem); 815 Ok(u32::from_be_bytes(e)) 816 }, 817 _ => Err(SubjectPublicKeyInfoErr::RsaExponentTooLarge), 818 }.and_then(|e| Self::try_from((n, e)).map_err(SubjectPublicKeyInfoErr::RsaPubKey)) 819 } else { 820 Err(SubjectPublicKeyInfoErr::RsaEncoding) 821 } 822 }) 823 } else { 824 Err(SubjectPublicKeyInfoErr::RsaLen) 825 } 826 }) 827 } else { 828 Err(SubjectPublicKeyInfoErr::RsaEncoding) 829 } 830 }) 831 } else { 832 Err(SubjectPublicKeyInfoErr::RsaEncoding) 833 } 834 }) 835 } else { 836 Err(SubjectPublicKeyInfoErr::RsaEncoding) 837 } 838 } else { 839 Err(SubjectPublicKeyInfoErr::RsaEncoding) 840 } 841 } else { 842 Err(SubjectPublicKeyInfoErr::RsaEncoding) 843 } 844 } else { 845 Err(SubjectPublicKeyInfoErr::RsaLen) 846 } 847 } else { 848 Err(SubjectPublicKeyInfoErr::RsaEncoding) 849 } 850 } else { 851 Err(SubjectPublicKeyInfoErr::RsaEncoding) 852 } 853 } else { 854 Err(SubjectPublicKeyInfoErr::RsaEncoding) 855 } 856 } else { 857 Err(SubjectPublicKeyInfoErr::RsaLen) 858 } 859 } else { 860 Err(SubjectPublicKeyInfoErr::RsaEncoding) 861 } 862 } else { 863 Err(SubjectPublicKeyInfoErr::RsaEncoding) 864 } 865 } else { 866 Err(SubjectPublicKeyInfoErr::RsaLen) 867 } 868 }) 869 } else { 870 Err(SubjectPublicKeyInfoErr::RsaEncoding) 871 } 872 }) 873 } 874 } 875 impl<'a> SubjectPublicKeyInfo<'a> for UncompressedPubKey<'a> { 876 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 877 #[expect(single_use_lifetimes, reason = "false positive")] 878 fn from_der<'b: 'a>(der: &'b [u8]) -> Result<Self, SubjectPublicKeyInfoErr> { 879 /// Index in a DER-encoded payload of the ML-DSA-* public key that corresponds 880 /// to the OID length. 881 const ML_DSA_OID_LEN_IDX: usize = 5; 882 /// Length of the OID for the DER-encoded ML-DSA-* public keys. 883 /// 884 /// Note this is _not_ the same as the OID length for an RSA public key (i.e., 13). 885 const ML_DSA_OID_LEN: u8 = 11; 886 // The only lengths that overlap is RSA with ML-DSA-44 and ML-DSA-65. 887 match der.len() { 888 // The minimum modulus we support for RSA is 2048 bits which is 256 bytes; 889 // thus clearly its encoding will be at least 256 which is greater than 890 // all of the non-ML-DSA-* values and less than the ML-DSA-* values. 891 // The maximum modulus we support for RSA is 16K bits which is 2048 bytes and the maximum 892 // exponent we support for RSA is 4 bytes which is hundreds of bytes less than 2614 893 // (i.e., the length of a DER-encoded ML-DSAS-87 public key). 894 ED25519_LEN => Ed25519PubKey::from_der(der).map(Self::Ed25519), 895 P256_LEN => UncompressedP256PubKey::from_der(der).map(Self::P256), 896 P384_LEN => UncompressedP384PubKey::from_der(der).map(Self::P384), 897 MLDSA44_LEN => { 898 // `ML_DSA_OID_LEN_IDX < MLDSA44_LEN = der.len()` so indexing 899 // won't `panic`. 900 if der[ML_DSA_OID_LEN_IDX] == ML_DSA_OID_LEN { 901 MlDsa44PubKey::from_der(der).map(Self::MlDsa44) 902 } else { 903 RsaPubKey::from_der(der).map(Self::Rsa) 904 } 905 } 906 MLDSA65_LEN => { 907 // `ML_DSA_OID_LEN_IDX < MLDSA65_LEN = der.len()` so indexing 908 // won't `panic`. 909 if der[ML_DSA_OID_LEN_IDX] == ML_DSA_OID_LEN { 910 MlDsa65PubKey::from_der(der).map(Self::MlDsa65) 911 } else { 912 RsaPubKey::from_der(der).map(Self::Rsa) 913 } 914 } 915 MLDSA87_LEN => MlDsa87PubKey::from_der(der).map(Self::MlDsa87), 916 _ => RsaPubKey::from_der(der).map(Self::Rsa), 917 } 918 } 919 } 920 } 921 /// Helper type returned from [`AuthenticatorAttestationVisitor::visit_map`]. 922 /// 923 /// The purpose of this type is to hopefully avoid re-parsing the raw attestation object multiple times. In 924 /// particular [`Registration`] and [`super::ser_relaxed::RegistrationRelaxed`] will attempt to validate `id` is the 925 /// same as the [`CredentialId`] within the attestation object. 926 pub(super) struct AuthAttest { 927 /// The data we care about. 928 pub attest: AuthenticatorAttestation, 929 /// [`CredentialId`] information. This is `None` iff `authenticatorData`, `publicKey`, and 930 /// `publicKeyAlgorithm` do not exist and we are performing a `RELAXED` parsing. When `Some`, the first 931 /// `usize` is the starting index of `CredentialId` within the attestation object; and the second `usize` is 932 /// 1 past the last index of `CredentialId`. 933 pub cred_info: Option<(usize, usize)>, 934 } 935 /// Fields in `AuthenticatorAttestationResponseJSON`. 936 enum AttestField<const IGNORE_UNKNOWN: bool> { 937 /// `clientDataJSON`. 938 ClientDataJson, 939 /// `attestationObject`. 940 AttestationObject, 941 /// `authenticatorData`. 942 AuthenticatorData, 943 /// `transports`. 944 Transports, 945 /// `publicKey`. 946 PublicKey, 947 /// `publicKeyAlgorithm`. 948 PublicKeyAlgorithm, 949 /// Unknown fields. 950 Other, 951 } 952 impl<'e, const I: bool> Deserialize<'e> for AttestField<I> { 953 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 954 where 955 D: Deserializer<'e>, 956 { 957 /// `Visitor` for `AttestField`. 958 struct AttestFieldVisitor<const IGNORE_UNKNOWN: bool>; 959 impl<const IG: bool> Visitor<'_> for AttestFieldVisitor<IG> { 960 type Value = AttestField<IG>; 961 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 962 write!( 963 formatter, 964 "'{CLIENT_DATA_JSON}', '{ATTESTATION_OBJECT}', '{AUTHENTICATOR_DATA}', '{TRANSPORTS}', '{PUBLIC_KEY}', or '{PUBLIC_KEY_ALGORITHM}'" 965 ) 966 } 967 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 968 where 969 E: Error, 970 { 971 match v { 972 CLIENT_DATA_JSON => Ok(AttestField::ClientDataJson), 973 ATTESTATION_OBJECT => Ok(AttestField::AttestationObject), 974 AUTHENTICATOR_DATA => Ok(AttestField::AuthenticatorData), 975 TRANSPORTS => Ok(AttestField::Transports), 976 PUBLIC_KEY => Ok(AttestField::PublicKey), 977 PUBLIC_KEY_ALGORITHM => Ok(AttestField::PublicKeyAlgorithm), 978 _ => { 979 if IG { 980 Ok(AttestField::Other) 981 } else { 982 Err(E::unknown_field(v, AUTH_ATTEST_FIELDS)) 983 } 984 } 985 } 986 } 987 } 988 deserializer.deserialize_identifier(AttestFieldVisitor::<I>) 989 } 990 } 991 /// Attestation object. We use this instead of `Base64DecodedVal` since we want to manually 992 /// allocate the `Vec` in order to avoid re-allocation. Internally `AuthenticatorAttestation::new` 993 /// appends the SHA-256 hash to the passed attestation object `Vec` to avoid temporarily allocating 994 /// a `Vec` that contains the attestation object and hash for signature verification. Calling code 995 /// can avoid any reallocation that would occur when the capacity is not large enough by ensuring the 996 /// passed `Vec` has at least 32 bytes of available capacity. 997 pub(super) struct AttObj(pub Vec<u8>); 998 impl<'e> Deserialize<'e> for AttObj { 999 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1000 where 1001 D: Deserializer<'e>, 1002 { 1003 /// `Visitor` for `AttObj`. 1004 struct AttObjVisitor; 1005 impl Visitor<'_> for AttObjVisitor { 1006 type Value = AttObj; 1007 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1008 formatter.write_str("base64url-encoded attestation object") 1009 } 1010 #[expect( 1011 clippy::arithmetic_side_effects, 1012 reason = "comment justifies their correctness" 1013 )] 1014 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 1015 where 1016 E: Error, 1017 { 1018 base64url_nopad::decode_len(v.len()) 1019 .ok_or_else(|| E::invalid_value(Unexpected::Str(v), &"base64url-encoded value")) 1020 .and_then(|len| { 1021 // The decoded length is 3/4 of the encoded length, so overflow could only occur 1022 // if usize::MAX / 4 < 32 => usize::MAX < 128 < u8::MAX; thus overflow is not 1023 // possible. We add 32 since the SHA-256 hash of `clientDataJSON` will be added to 1024 // the raw attestation object by `AuthenticatorAttestation::new`. 1025 let mut att_obj = vec![0; len + Sha256::output_size()]; 1026 att_obj.truncate(len); 1027 base64url_nopad::decode_buffer_exact(v.as_bytes(), &mut att_obj) 1028 .map_err(E::custom) 1029 .map(|()| AttObj(att_obj)) 1030 }) 1031 } 1032 } 1033 deserializer.deserialize_str(AttObjVisitor) 1034 } 1035 } 1036 /// `Visitor` for `AuthenticatorAttestation`. 1037 /// 1038 /// Unknown fields are ignored and only `clientDataJSON` and `attestationObject` are required iff `RELAXED`. 1039 pub(super) struct AuthenticatorAttestationVisitor<const RELAXED: bool>; 1040 impl<'d, const R: bool> Visitor<'d> for AuthenticatorAttestationVisitor<R> { 1041 type Value = AuthAttest; 1042 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1043 formatter.write_str("AuthenticatorAttestation") 1044 } 1045 #[expect(clippy::too_many_lines, reason = "find it easier to reason about")] 1046 #[expect( 1047 clippy::arithmetic_side_effects, 1048 clippy::indexing_slicing, 1049 reason = "comments justify their correctness" 1050 )] 1051 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 1052 where 1053 A: MapAccess<'d>, 1054 { 1055 use spki::SubjectPublicKeyInfo as _; 1056 let mut client_data = None; 1057 let mut attest = None; 1058 let mut auth = None; 1059 let mut pub_key = None; 1060 let mut key_alg = None; 1061 let mut trans = None; 1062 while let Some(key) = map.next_key::<AttestField<R>>()? { 1063 match key { 1064 AttestField::ClientDataJson => { 1065 if client_data.is_some() { 1066 return Err(Error::duplicate_field(CLIENT_DATA_JSON)); 1067 } 1068 client_data = map 1069 .next_value::<Base64DecodedVal>() 1070 .map(|c_data| Some(c_data.0))?; 1071 } 1072 AttestField::AttestationObject => { 1073 if attest.is_some() { 1074 return Err(Error::duplicate_field(ATTESTATION_OBJECT)); 1075 } 1076 attest = map.next_value::<AttObj>().map(|att_obj| Some(att_obj.0))?; 1077 } 1078 AttestField::AuthenticatorData => { 1079 if auth.is_some() { 1080 return Err(Error::duplicate_field(AUTHENTICATOR_DATA)); 1081 } 1082 auth = map.next_value::<Option<Base64DecodedVal>>().map(Some)?; 1083 } 1084 AttestField::Transports => { 1085 if trans.is_some() { 1086 return Err(Error::duplicate_field(TRANSPORTS)); 1087 } 1088 trans = map.next_value::<Option<_>>().map(Some)?; 1089 } 1090 AttestField::PublicKey => { 1091 if pub_key.is_some() { 1092 return Err(Error::duplicate_field(PUBLIC_KEY)); 1093 } 1094 pub_key = map.next_value::<Option<Base64DecodedVal>>().map(Some)?; 1095 } 1096 AttestField::PublicKeyAlgorithm => { 1097 if key_alg.is_some() { 1098 return Err(Error::duplicate_field(PUBLIC_KEY_ALGORITHM)); 1099 } 1100 key_alg = map 1101 .next_value::<Option<CoseAlgorithmIdentifier>>() 1102 .map(Some)?; 1103 } 1104 AttestField::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 1105 } 1106 } 1107 // Note the order of this matters from a performance perspective. In particular `auth` must be evaluated 1108 // before `pub_key` which must be evaluated before `key_alg` as this allows us to parse the attestation 1109 // object at most once and allow us to prioritize parsing `authenticatorData` over the attestation object. 1110 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| { 1111 trans.ok_or(false).and_then(|opt_trans| opt_trans.ok_or(true)).or_else( 1112 |flag| { 1113 if R { 1114 Ok(AuthTransports::new()) 1115 } else if flag { 1116 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{TRANSPORTS} to be a sequence of AuthenticatorTransports").as_str())) 1117 } else { 1118 Err(Error::missing_field(TRANSPORTS)) 1119 } 1120 }, 1121 ).and_then(|transports| { 1122 auth.ok_or(false).and_then(|opt_auth| opt_auth.ok_or(true)).as_ref().map_or_else( 1123 |flag| { 1124 if R { 1125 Ok(None) 1126 } else if *flag { 1127 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{AUTHENTICATOR_DATA} to be a base64url-encoded AuthenticatorData").as_str())) 1128 } else { 1129 Err(Error::missing_field(AUTHENTICATOR_DATA)) 1130 } 1131 }, 1132 |a_data| { 1133 if a_data.0.len() > 37 { 1134 // The last portion of attestation object is always authenticator data. 1135 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| { 1136 // Indexing is fine; otherwise the above check would have returned `None`. 1137 if *a_data.0 == attestation_object[idx..] { 1138 // We know `a_data.len() > 37`; thus indexing is fine. 1139 // We start at 37 since that is the beginning of `attestedCredentialData`. 1140 // Recall the first 32 bytes are `rpIdHash`, then a 1 byte `flags`, then a 1141 // 4-byte big-endian integer `signCount`. 1142 // The starting index of `credentialId` is 18 within `attestedCredentialData`. 1143 // Recall the first 16 bytes are `aaguid`, then a 2-byte big-endian integer 1144 // `credentialIdLength`. Consequently the starting index within 1145 // `attestation_object` is `idx + 37 + 18` = `idx + 55`. Overflow cannot occur 1146 // since we successfully parsed `AttestedCredentialData`. 1147 AttestedCredentialData::from_cbor(&a_data.0[37..]).map_err(Error::custom).map(|success| Some((success.value, idx + 55))) 1148 } else { 1149 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())) 1150 } 1151 }) 1152 } else { 1153 Err(Error::invalid_value(Unexpected::Bytes(a_data.0.as_slice()), &"authenticator data to be long enough to contain attested credential data")) 1154 } 1155 } 1156 ).and_then(|attested_info| { 1157 pub_key.ok_or(false).and_then(|opt_key| opt_key.ok_or(true)).map_or_else( 1158 |flag| { 1159 if R { 1160 attested_info.as_ref().map_or(Ok(None), |&(ref attested_data, cred_id_start)| Ok(Some((match attested_data.credential_public_key { 1161 UncompressedPubKey::MlDsa87(_) => CoseAlgorithmIdentifier::Mldsa87, 1162 UncompressedPubKey::MlDsa65(_) => CoseAlgorithmIdentifier::Mldsa65, 1163 UncompressedPubKey::MlDsa44(_) => CoseAlgorithmIdentifier::Mldsa44, 1164 UncompressedPubKey::Ed25519(_) => CoseAlgorithmIdentifier::Eddsa, 1165 UncompressedPubKey::P256(_) => CoseAlgorithmIdentifier::Es256, 1166 UncompressedPubKey::P384(_) => CoseAlgorithmIdentifier::Es384, 1167 UncompressedPubKey::Rsa(_) => CoseAlgorithmIdentifier::Rs256, 1168 // Overflow won't occur since this is correct as 1169 // `AttestedCredentialData::from_cbor` would have erred if not. 1170 }, cred_id_start, cred_id_start + attested_data.credential_id.0.len())))) 1171 } else { 1172 // `publicKey` is only allowed to not exist when `CoseAlgorithmIdentifier::Eddsa`, 1173 // `CoseAlgorithmIdentifier::Es256`, or `CoseAlgorithmIdentifier::Rs256` is not 1174 // used. 1175 attested_info.as_ref().map_or_else( 1176 || AttestationObject::parse_data(attestation_object.as_slice()).map_err(Error::custom).and_then(|(att_obj, auth_idx)| { 1177 match att_obj.auth_data.attested_credential_data.credential_public_key { 1178 UncompressedPubKey::MlDsa87(_) => { 1179 // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx` 1180 // is the start of the raw authenticator data which itself contains the raw Credential ID. 1181 Ok(Some((CoseAlgorithmIdentifier::Mldsa87, auth_idx, auth_idx + att_obj.auth_data.attested_credential_data.credential_id.0.len()))) 1182 } 1183 UncompressedPubKey::MlDsa65(_) => { 1184 // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx` 1185 // is the start of the raw authenticator data which itself contains the raw Credential ID. 1186 Ok(Some((CoseAlgorithmIdentifier::Mldsa65, auth_idx, auth_idx + att_obj.auth_data.attested_credential_data.credential_id.0.len()))) 1187 } 1188 UncompressedPubKey::MlDsa44(_) => { 1189 // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx` 1190 // is the start of the raw authenticator data which itself contains the raw Credential ID. 1191 Ok(Some((CoseAlgorithmIdentifier::Mldsa44, auth_idx, auth_idx + att_obj.auth_data.attested_credential_data.credential_id.0.len()))) 1192 } 1193 UncompressedPubKey::P384(_) => { 1194 // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx` 1195 // is the start of the raw authenticator data which itself contains the raw Credential ID. 1196 Ok(Some((CoseAlgorithmIdentifier::Es384, auth_idx, auth_idx + att_obj.auth_data.attested_credential_data.credential_id.0.len()))) 1197 } 1198 UncompressedPubKey::Ed25519(_) | UncompressedPubKey::P256(_) | UncompressedPubKey::Rsa(_) => Err(Error::missing_field(PUBLIC_KEY)), 1199 } 1200 }), 1201 |&(ref attested_data, cred_id_start)| { 1202 match attested_data.credential_public_key { 1203 UncompressedPubKey::MlDsa87(_) => { 1204 // Overflow won't occur since this is correct. This is correct since we successfully parsed 1205 // `AttestedCredentialData` and calculated `cred_id_start` from it. 1206 Ok(Some((CoseAlgorithmIdentifier::Mldsa87, cred_id_start, cred_id_start + attested_data.credential_id.0.len()))) 1207 } 1208 UncompressedPubKey::MlDsa65(_) => { 1209 // Overflow won't occur since this is correct. This is correct since we successfully parsed 1210 // `AttestedCredentialData` and calculated `cred_id_start` from it. 1211 Ok(Some((CoseAlgorithmIdentifier::Mldsa65, cred_id_start, cred_id_start + attested_data.credential_id.0.len()))) 1212 } 1213 UncompressedPubKey::MlDsa44(_) => { 1214 // Overflow won't occur since this is correct. This is correct since we successfully parsed 1215 // `AttestedCredentialData` and calculated `cred_id_start` from it. 1216 Ok(Some((CoseAlgorithmIdentifier::Mldsa44, cred_id_start, cred_id_start + attested_data.credential_id.0.len()))) 1217 } 1218 UncompressedPubKey::P384(_) => { 1219 // Overflow won't occur since this is correct. This is correct since we successfully parsed 1220 // `AttestedCredentialData` and calculated `cred_id_start` from it. 1221 Ok(Some((CoseAlgorithmIdentifier::Es384, cred_id_start, cred_id_start + attested_data.credential_id.0.len()))) 1222 } 1223 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)) }, 1224 } 1225 } 1226 ) 1227 } 1228 }, 1229 |der| { 1230 UncompressedPubKey::from_der(der.0.as_slice()).map_err(Error::custom).and_then(|key| { 1231 attested_info.as_ref().map_or_else( 1232 || AttestationObject::parse_data(attestation_object.as_slice()).map_err(Error::custom).and_then(|(att_obj, auth_idx)| { 1233 if key == att_obj.auth_data.attested_credential_data.credential_public_key { 1234 let alg = match att_obj.auth_data.attested_credential_data.credential_public_key { 1235 UncompressedPubKey::MlDsa87(_) => CoseAlgorithmIdentifier::Mldsa87, 1236 UncompressedPubKey::MlDsa65(_) => CoseAlgorithmIdentifier::Mldsa65, 1237 UncompressedPubKey::MlDsa44(_) => CoseAlgorithmIdentifier::Mldsa44, 1238 UncompressedPubKey::Ed25519(_) => CoseAlgorithmIdentifier::Eddsa, 1239 UncompressedPubKey::P256(_) => CoseAlgorithmIdentifier::Es256, 1240 UncompressedPubKey::P384(_) => CoseAlgorithmIdentifier::Es384, 1241 UncompressedPubKey::Rsa(_) => CoseAlgorithmIdentifier::Rs256, 1242 }; 1243 // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx` 1244 // is the start of the raw authenticator data which itself contains the raw Credential ID. 1245 Ok(Some((alg, auth_idx, auth_idx+ att_obj.auth_data.attested_credential_data.credential_id.0.len()))) 1246 } else { 1247 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())) 1248 } 1249 }), 1250 |&(ref attested_data, cred_id_start)| { 1251 if key == attested_data.credential_public_key { 1252 let alg = match attested_data.credential_public_key { 1253 UncompressedPubKey::MlDsa87(_) => CoseAlgorithmIdentifier::Mldsa87, 1254 UncompressedPubKey::MlDsa65(_) => CoseAlgorithmIdentifier::Mldsa65, 1255 UncompressedPubKey::MlDsa44(_) => CoseAlgorithmIdentifier::Mldsa44, 1256 UncompressedPubKey::Ed25519(_) => CoseAlgorithmIdentifier::Eddsa, 1257 UncompressedPubKey::P256(_) => CoseAlgorithmIdentifier::Es256, 1258 UncompressedPubKey::P384(_) => CoseAlgorithmIdentifier::Es384, 1259 UncompressedPubKey::Rsa(_) => CoseAlgorithmIdentifier::Rs256, 1260 }; 1261 // Overflow won't occur since this is correct. This is correct since we successfully parsed 1262 // `AttestedCredentialData` and calculated `cred_id_start` from it. 1263 Ok(Some((alg, cred_id_start, cred_id_start + attested_data.credential_id.0.len()))) 1264 } else { 1265 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())) 1266 } 1267 } 1268 ) 1269 }) 1270 } 1271 ).and_then(|cred_key_alg_cred_info| { 1272 key_alg.ok_or(false).and_then(|opt_alg| opt_alg.ok_or(true)).map_or_else( 1273 |flag| { 1274 if R { 1275 Ok(cred_key_alg_cred_info.map(|info| (info.1, info.2))) 1276 } else if flag { 1277 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{PUBLIC_KEY_ALGORITHM} to be a base64url-encoded DER-encoded SubjectPublicKeyInfo").as_str())) 1278 } else { 1279 Err(Error::missing_field(PUBLIC_KEY_ALGORITHM)) 1280 } 1281 }, 1282 |alg| { 1283 cred_key_alg_cred_info.map_or_else( 1284 || AttestationObject::parse_data(attestation_object.as_slice()).map_err(Error::custom).and_then(|(att_obj, auth_idx)| { 1285 let att_obj_alg = match att_obj.auth_data.attested_credential_data.credential_public_key { 1286 UncompressedPubKey::MlDsa87(_) => CoseAlgorithmIdentifier::Mldsa87, 1287 UncompressedPubKey::MlDsa65(_) => CoseAlgorithmIdentifier::Mldsa65, 1288 UncompressedPubKey::MlDsa44(_) => CoseAlgorithmIdentifier::Mldsa44, 1289 UncompressedPubKey::Ed25519(_) => CoseAlgorithmIdentifier::Eddsa, 1290 UncompressedPubKey::P256(_) => CoseAlgorithmIdentifier::Es256, 1291 UncompressedPubKey::P384(_) => CoseAlgorithmIdentifier::Es384, 1292 UncompressedPubKey::Rsa(_) => CoseAlgorithmIdentifier::Rs256, 1293 }; 1294 if alg == att_obj_alg { 1295 // This won't overflow since `AttestationObject::parse_data` succeeded and `auth_idx` 1296 // is the start of the raw authenticator data which itself contains the raw Credential ID. 1297 Ok(Some((auth_idx, auth_idx + att_obj.auth_data.attested_credential_data.credential_id.0.len()))) 1298 } else { 1299 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())) 1300 } 1301 }), 1302 |(a, start, last)| if alg == a { 1303 Ok(Some((start, last))) 1304 } else { 1305 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())) 1306 }, 1307 ) 1308 } 1309 ).map(|cred_info| AuthAttest{ attest: AuthenticatorAttestation::new(client_data_json, attestation_object, transports), cred_info, }) 1310 }) 1311 }) 1312 }) 1313 })) 1314 } 1315 } 1316 /// `"clientDataJSON"` 1317 const CLIENT_DATA_JSON: &str = "clientDataJSON"; 1318 /// `"attestationObject"` 1319 const ATTESTATION_OBJECT: &str = "attestationObject"; 1320 /// `"authenticatorData"` 1321 const AUTHENTICATOR_DATA: &str = "authenticatorData"; 1322 /// `"transports"` 1323 const TRANSPORTS: &str = "transports"; 1324 /// `"publicKey"` 1325 const PUBLIC_KEY: &str = "publicKey"; 1326 /// `"publicKeyAlgorithm"` 1327 const PUBLIC_KEY_ALGORITHM: &str = "publicKeyAlgorithm"; 1328 /// Fields in `AuthenticatorAttestationResponseJSON`. 1329 pub(super) const AUTH_ATTEST_FIELDS: &[&str; 6] = &[ 1330 CLIENT_DATA_JSON, 1331 ATTESTATION_OBJECT, 1332 AUTHENTICATOR_DATA, 1333 TRANSPORTS, 1334 PUBLIC_KEY, 1335 PUBLIC_KEY_ALGORITHM, 1336 ]; 1337 impl<'de> Deserialize<'de> for AuthAttest { 1338 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1339 where 1340 D: Deserializer<'de>, 1341 { 1342 deserializer.deserialize_struct( 1343 "AuthenticatorAttestation", 1344 AUTH_ATTEST_FIELDS, 1345 AuthenticatorAttestationVisitor::<false>, 1346 ) 1347 } 1348 } 1349 impl<'de> Deserialize<'de> for AuthenticatorAttestation { 1350 /// Deserializes a `struct` based on 1351 /// [`AuthenticatorAttestationResponseJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticatorattestationresponsejson). 1352 /// 1353 /// Note unknown keys and duplicate keys are forbidden; 1354 /// [`clientDataJSON`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-clientdatajson), 1355 /// [`authenticatorData`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-authenticatordata), 1356 /// [`publicKey`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-publickey) 1357 /// and 1358 /// [`attestationObject`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-attestationobject) 1359 /// are base64url-decoded; 1360 /// [`transports`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-transports) 1361 /// is deserialized via [`AuthTransports::deserialize`]; the decoded `publicKey` is parsed according to the 1362 /// applicable DER-encoded ASN.1 `SubjectPublicKeyInfo` schema; 1363 /// [`publicKeyAlgorithm`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-publickeyalgorithm) 1364 /// is deserialized according to 1365 /// [`CoseAlgorithmIdentifier`](https://www.w3.org/TR/webauthn-3/#typedefdef-cosealgorithmidentifier); all `required` 1366 /// fields in the `AuthenticatorAttestationResponseJSON` Web IDL `dictionary` exist (and must not be `null`); `publicKey` 1367 /// exists when Ed25519, P-256 with SHA-256, or RSASSA-PKCS1-v1_5 with SHA-256 is used (and must not be `null`) 1368 /// [per WebAuthn](https://www.w3.org/TR/webauthn-3/#sctn-public-key-easy); the `publicKeyAlgorithm` aligns 1369 /// with 1370 /// [`credentialPublicKey`](https://www.w3.org/TR/webauthn-3/#authdata-attestedcredentialdata-credentialpublickey) 1371 /// within 1372 /// [`attestedCredentialData`](https://www.w3.org/TR/webauthn-3/#authdata-attestedcredentialdata) within the 1373 /// decoded `authenticatorData`; the decoded `publicKey` is the same as `credentialPublicKey` within 1374 /// `attestedCredentialData` within the decoded `authenticatorData`; and the decoded `authenticatorData` is the 1375 /// same as [`authData`](https://www.w3.org/TR/webauthn-3/#attestation-object) within the decoded 1376 /// `attestationObject`. 1377 #[inline] 1378 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1379 where 1380 D: Deserializer<'de>, 1381 { 1382 AuthAttest::deserialize(deserializer).map(|val| val.attest) 1383 } 1384 } 1385 /// `Visitor` for `CredentialPropertiesOutput`. 1386 /// 1387 /// Unknown fields are ignored iff `RELAXED`. 1388 pub(super) struct CredentialPropertiesOutputVisitor<const RELAXED: bool>; 1389 impl<'d, const R: bool> Visitor<'d> for CredentialPropertiesOutputVisitor<R> { 1390 type Value = CredentialPropertiesOutput; 1391 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1392 formatter.write_str("CredentialPropertiesOutput") 1393 } 1394 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 1395 where 1396 A: MapAccess<'d>, 1397 { 1398 /// Allowed fields. 1399 enum Field<const IGNORE_UNKNOWN: bool> { 1400 /// `rk` field. 1401 Rk, 1402 /// Unknown field. 1403 Other, 1404 } 1405 impl<'e, const I: bool> Deserialize<'e> for Field<I> { 1406 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1407 where 1408 D: Deserializer<'e>, 1409 { 1410 /// `Visitor` for `Field`. 1411 /// 1412 /// Unknown fields are ignored iff `IGNORE_UNKNOWN`. 1413 struct FieldVisitor<const IGNORE_UNKNOWN: bool>; 1414 impl<const IG: bool> Visitor<'_> for FieldVisitor<IG> { 1415 type Value = Field<IG>; 1416 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1417 write!(formatter, "'{RK}'") 1418 } 1419 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 1420 where 1421 E: Error, 1422 { 1423 match v { 1424 RK => Ok(Field::Rk), 1425 _ => { 1426 if IG { 1427 Ok(Field::Other) 1428 } else { 1429 Err(E::unknown_field(v, PROPS_FIELDS)) 1430 } 1431 } 1432 } 1433 } 1434 } 1435 deserializer.deserialize_identifier(FieldVisitor) 1436 } 1437 } 1438 let mut rk = None; 1439 while let Some(key) = map.next_key::<Field<R>>()? { 1440 match key { 1441 Field::Rk => { 1442 if rk.is_some() { 1443 return Err(Error::duplicate_field(RK)); 1444 } 1445 rk = map.next_value().map(Some)?; 1446 } 1447 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 1448 } 1449 } 1450 Ok(CredentialPropertiesOutput { rk: rk.flatten() }) 1451 } 1452 } 1453 /// `"rk"` 1454 const RK: &str = "rk"; 1455 /// `CredentialPropertiesOutput` fields. 1456 pub(super) const PROPS_FIELDS: &[&str; 1] = &[RK]; 1457 impl<'de> Deserialize<'de> for CredentialPropertiesOutput { 1458 /// Deserializes a `struct` based on 1459 /// [`CredentialPropertiesOutput`](https://www.w3.org/TR/webauthn-3/#dictdef-credentialpropertiesoutput). 1460 /// 1461 /// Note unknown and duplicate keys are forbidden. 1462 #[inline] 1463 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1464 where 1465 D: Deserializer<'de>, 1466 { 1467 deserializer.deserialize_struct( 1468 "CredentialPropertiesOutput", 1469 PROPS_FIELDS, 1470 CredentialPropertiesOutputVisitor::<false>, 1471 ) 1472 } 1473 } 1474 impl<'de> Deserialize<'de> for AuthenticationExtensionsPrfOutputs { 1475 /// Deserializes a `struct` based on 1476 /// [`AuthenticationExtensionsPRFOutputsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfoutputsjson). 1477 /// 1478 /// Note unknown and duplicate keys are forbidden; 1479 /// [`enabled`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-enabled) 1480 /// must exist (and not be `null`); and 1481 /// [`results`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-results) must not exist, 1482 /// be `null`, or be an 1483 /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues) 1484 /// with no unknown or duplicate keys, 1485 /// [`first`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-first) must exist but be 1486 /// `null`, and 1487 /// [`second`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-second) can exist but 1488 /// must be `null` if so. 1489 #[inline] 1490 #[expect(clippy::unreachable, reason = "we want to crash when there is a bug")] 1491 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1492 where 1493 D: Deserializer<'de>, 1494 { 1495 AuthenticationExtensionsPrfOutputsHelper::<false, true, AuthenticationExtensionsPrfValues>::deserialize(deserializer).map(|val| Self { 1496 enabled: val.0.unwrap_or_else(|| { 1497 unreachable!( 1498 "there is a bug in AuthenticationExtensionsPrfOutputsHelper::deserialize" 1499 ) 1500 }), 1501 }) 1502 } 1503 } 1504 /// `Visitor` for `ClientExtensionsOutputs`. 1505 /// 1506 /// Unknown fields are ignored iff `RELAXED`. 1507 pub(super) struct ClientExtensionsOutputsVisitor<const RELAXED: bool, PROPS, PRF>( 1508 pub PhantomData<fn() -> (PROPS, PRF)>, 1509 ); 1510 impl<'d, const R: bool, C, P> Visitor<'d> for ClientExtensionsOutputsVisitor<R, C, P> 1511 where 1512 C: for<'a> Deserialize<'a> + Into<CredentialPropertiesOutput>, 1513 P: for<'a> Deserialize<'a> + Into<AuthenticationExtensionsPrfOutputs>, 1514 { 1515 type Value = ClientExtensionsOutputs; 1516 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1517 formatter.write_str("ClientExtensionsOutputs") 1518 } 1519 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 1520 where 1521 A: MapAccess<'d>, 1522 { 1523 /// Allowed fields. 1524 enum Field<const IGNORE_UNKNOWN: bool> { 1525 /// `credProps` field. 1526 CredProps, 1527 /// `prf` field. 1528 Prf, 1529 /// Unknown field. 1530 Other, 1531 } 1532 impl<'e, const I: bool> Deserialize<'e> for Field<I> { 1533 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1534 where 1535 D: Deserializer<'e>, 1536 { 1537 /// `Visitor` for `Field`. 1538 /// 1539 /// Unknown fields are ignored iff `IGNORE_UNKNOWN`. 1540 struct FieldVisitor<const IGNORE_UNKNOWN: bool>; 1541 impl<const IG: bool> Visitor<'_> for FieldVisitor<IG> { 1542 type Value = Field<IG>; 1543 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1544 write!(formatter, "'{CRED_PROPS}' or '{PRF}'") 1545 } 1546 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 1547 where 1548 E: Error, 1549 { 1550 match v { 1551 CRED_PROPS => Ok(Field::CredProps), 1552 PRF => Ok(Field::Prf), 1553 _ => { 1554 if IG { 1555 Ok(Field::Other) 1556 } else { 1557 Err(E::unknown_field(v, EXT_FIELDS)) 1558 } 1559 } 1560 } 1561 } 1562 } 1563 deserializer.deserialize_identifier(FieldVisitor) 1564 } 1565 } 1566 let mut cred_props = None; 1567 let mut prf = None; 1568 while let Some(key) = map.next_key::<Field<R>>()? { 1569 match key { 1570 Field::CredProps => { 1571 if cred_props.is_some() { 1572 return Err(Error::duplicate_field(CRED_PROPS)); 1573 } 1574 cred_props = map.next_value::<Option<C>>().map(Some)?; 1575 } 1576 Field::Prf => { 1577 if prf.is_some() { 1578 return Err(Error::duplicate_field(PRF)); 1579 } 1580 prf = map.next_value::<Option<P>>().map(Some)?; 1581 } 1582 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 1583 } 1584 } 1585 Ok(ClientExtensionsOutputs { 1586 cred_props: cred_props.flatten().map(Into::into), 1587 prf: prf.flatten().map(Into::into), 1588 }) 1589 } 1590 } 1591 impl ClientExtensions for ClientExtensionsOutputs { 1592 fn empty() -> Self { 1593 Self { 1594 prf: None, 1595 cred_props: None, 1596 } 1597 } 1598 } 1599 /// `"credProps"` 1600 const CRED_PROPS: &str = "credProps"; 1601 /// `"prf"` 1602 const PRF: &str = "prf"; 1603 /// `AuthenticationExtensionsClientOutputsJSON` fields. 1604 pub(super) const EXT_FIELDS: &[&str; 2] = &[CRED_PROPS, PRF]; 1605 impl<'de> Deserialize<'de> for ClientExtensionsOutputs { 1606 /// Deserializes a `struct` based on 1607 /// [`AuthenticationExtensionsClientOutputsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsclientoutputsjson). 1608 /// 1609 /// Note that unknown and duplicate keys are forbidden; 1610 /// [`credProps`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsclientoutputs-credprops) is 1611 /// `null` or deserialized via [`CredentialPropertiesOutput::deserialize`]; and 1612 /// [`prf`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsclientoutputs-prf) is `null` 1613 /// or deserialized via [`AuthenticationExtensionsPrfOutputs::deserialize`]. 1614 #[inline] 1615 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1616 where 1617 D: Deserializer<'de>, 1618 { 1619 deserializer.deserialize_struct( 1620 "ClientExtensionsOutputs", 1621 EXT_FIELDS, 1622 ClientExtensionsOutputsVisitor::< 1623 false, 1624 CredentialPropertiesOutput, 1625 AuthenticationExtensionsPrfOutputs, 1626 >(PhantomData), 1627 ) 1628 } 1629 } 1630 impl<'de> Deserialize<'de> for Registration { 1631 /// Deserializes a `struct` based on 1632 /// [`RegistrationResponseJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-registrationresponsejson). 1633 /// 1634 /// Note that unknown and duplicate keys are forbidden; 1635 /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-id) and 1636 /// [`rawId`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-rawid) are deserialized 1637 /// via [`CredentialId::deserialize`]; 1638 /// [`response`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-response) is deserialized 1639 /// via [`AuthenticatorAttestation::deserialize`]; 1640 /// [`authenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-authenticatorattachment) 1641 /// is `null` or deserialized via [`AuthenticatorAttachment::deserialize`]; 1642 /// [`clientExtensionResults`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-clientextensionresults) 1643 /// is deserialized via [`ClientExtensionsOutputs::deserialize`]; all `required` fields in the 1644 /// `RegistrationResponseJSON` Web IDL `dictionary` exist (and are not `null`); 1645 /// [`type`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-type) is `"public-key"`; 1646 /// and the decoded `id`, decoded `rawId`, and 1647 /// [`credentialId`](https://www.w3.org/TR/webauthn-3/#authdata-attestedcredentialdata-credentialid) within 1648 /// [`attestedCredentialData`](https://www.w3.org/TR/webauthn-3/#authdata-attestedcredentialdata) within 1649 /// [`authData`](https://www.w3.org/TR/webauthn-3/#attestation-object) within the decoded 1650 /// [`attestationObject`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-attestationobject) 1651 /// are all the same. 1652 #[expect(clippy::unreachable, reason = "when there is a bug, we want to crash")] 1653 #[expect(clippy::indexing_slicing, reason = "comment justifies its correctness")] 1654 #[inline] 1655 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1656 where 1657 D: Deserializer<'de>, 1658 { 1659 PublicKeyCredential::<false, true, AuthAttest, ClientExtensionsOutputs>::deserialize(deserializer).and_then(|cred| { 1660 let id = cred.id.unwrap_or_else(|| unreachable!("there is a bug in PublicKeyCredential::deserialize")); 1661 cred.response.cred_info.map_or_else( 1662 || AttestationObject::try_from(cred.response.attest.attestation_object()).map_err(Error::custom).and_then(|att_obj| { 1663 if id.as_ref() == att_obj.auth_data.attested_credential_data.credential_id.as_ref() { 1664 Ok(()) 1665 } else { 1666 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())) 1667 } 1668 }), 1669 // `start` and `last` were calculated based on `cred.response.attest.attestation_object()` 1670 // and represent the starting and ending index of the `CredentialId`; therefore this is correct 1671 // let alone won't `panic`. 1672 |(start, last)| if *id.0 == cred.response.attest.attestation_object()[start..last] { 1673 Ok(()) 1674 } else { 1675 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())) 1676 }, 1677 ).map(|()| Self { response: cred.response.attest, authenticator_attachment: cred.authenticator_attachment, client_extension_results: cred.client_extension_results, }) 1678 }) 1679 } 1680 } 1681 #[cfg(test)] 1682 mod tests { 1683 use super::{ 1684 super::{ 1685 AKP, ALG, AuthenticatorAttachment, EC2, EDDSA, ES256, ES384, Ed25519PubKey, KTY, 1686 MLDSA44, MLDSA65, MLDSA87, MlDsa44PubKey, MlDsa65PubKey, MlDsa87PubKey, OKP, RSA, 1687 Registration, RsaPubKey, UncompressedP256PubKey, UncompressedP384PubKey, cbor, 1688 }, 1689 CoseAlgorithmIdentifier, 1690 spki::SubjectPublicKeyInfo as _, 1691 }; 1692 use ed25519_dalek::{VerifyingKey, pkcs8::EncodePublicKey as _}; 1693 use ml_dsa::{MlDsa44, MlDsa65, MlDsa87, VerifyingKey as MlDsaVerKey}; 1694 use p256::{ 1695 PublicKey as P256PubKey, Sec1Point as P256Pt, SecretKey as P256Key, 1696 elliptic_curve::sec1::{FromSec1Point as _, ToSec1Point as _}, 1697 }; 1698 use p384::{PublicKey as P384PubKey, Sec1Point as P384Pt, SecretKey as P384Key}; 1699 use rsa::{ 1700 BoxedUint, RsaPrivateKey, 1701 sha2::{Digest as _, Sha256}, 1702 traits::PublicKeyParts as _, 1703 }; 1704 use serde::de::{Error as _, Unexpected}; 1705 use serde_json::Error; 1706 #[expect(clippy::unwrap_used, reason = "OK in tests")] 1707 #[test] 1708 fn mldsa87_spki() { 1709 assert!( 1710 MlDsa87PubKey::from_der( 1711 MlDsaVerKey::<MlDsa87>::decode(&[1; 2592].into()) 1712 .to_public_key_der() 1713 .unwrap() 1714 .as_bytes() 1715 ) 1716 .is_ok_and(|k| k.0 == [1; 2592]) 1717 ); 1718 } 1719 #[expect(clippy::unwrap_used, reason = "OK in tests")] 1720 #[test] 1721 fn mldsa65_spki() { 1722 assert!( 1723 MlDsa65PubKey::from_der( 1724 MlDsaVerKey::<MlDsa65>::decode(&[1; 1952].into()) 1725 .to_public_key_der() 1726 .unwrap() 1727 .as_bytes() 1728 ) 1729 .is_ok_and(|k| k.0 == [1; 1952]) 1730 ); 1731 } 1732 #[expect(clippy::unwrap_used, reason = "OK in tests")] 1733 #[test] 1734 fn mldsa44_spki() { 1735 assert!( 1736 MlDsa44PubKey::from_der( 1737 MlDsaVerKey::<MlDsa44>::decode(&[1; 1312].into()) 1738 .to_public_key_der() 1739 .unwrap() 1740 .as_bytes() 1741 ) 1742 .is_ok_and(|k| k.0 == [1; 1312]) 1743 ); 1744 } 1745 #[expect(clippy::unwrap_used, reason = "OK in tests")] 1746 #[test] 1747 fn ed25519_spki() { 1748 assert!( 1749 Ed25519PubKey::from_der( 1750 VerifyingKey::from_bytes(&[1; 32]) 1751 .unwrap() 1752 .to_public_key_der() 1753 .unwrap() 1754 .as_bytes() 1755 ) 1756 .is_ok_and(|k| k.0 == [1; 32]) 1757 ); 1758 } 1759 #[expect(clippy::unwrap_used, reason = "OK in tests")] 1760 #[test] 1761 fn p256_spki() { 1762 let key = P256Key::from_bytes( 1763 &[ 1764 137, 133, 36, 206, 163, 47, 255, 5, 76, 144, 163, 141, 40, 109, 108, 240, 246, 115, 1765 178, 237, 169, 68, 6, 129, 92, 21, 238, 127, 55, 158, 207, 95, 1766 ] 1767 .into(), 1768 ) 1769 .unwrap() 1770 .public_key(); 1771 let enc_key = key.to_sec1_point(false); 1772 assert!( 1773 UncompressedP256PubKey::from_der(key.to_public_key_der().unwrap().as_bytes()) 1774 .is_ok_and(|k| *k.0 == **enc_key.x().unwrap() && *k.1 == **enc_key.y().unwrap()) 1775 ); 1776 } 1777 #[expect(clippy::unwrap_used, reason = "OK in tests")] 1778 #[test] 1779 fn p384_spki() { 1780 let key = P384Key::from_bytes( 1781 &[ 1782 158, 99, 156, 49, 190, 211, 85, 167, 28, 2, 80, 57, 31, 22, 17, 38, 85, 78, 232, 1783 42, 45, 199, 154, 243, 136, 251, 84, 34, 5, 120, 208, 91, 61, 248, 64, 144, 87, 1, 1784 32, 86, 220, 68, 182, 11, 105, 223, 75, 70, 1785 ] 1786 .into(), 1787 ) 1788 .unwrap() 1789 .public_key(); 1790 let enc_key = key.to_sec1_point(false); 1791 assert!( 1792 UncompressedP384PubKey::from_der(key.to_public_key_der().unwrap().as_bytes()) 1793 .is_ok_and(|k| *k.0 == **enc_key.x().unwrap() && *k.1 == **enc_key.y().unwrap()) 1794 ); 1795 } 1796 #[expect(clippy::unwrap_used, reason = "OK in tests")] 1797 #[test] 1798 fn rsa_spki() { 1799 let n = [ 1800 111, 183, 124, 133, 38, 167, 70, 148, 44, 50, 30, 60, 121, 14, 38, 37, 96, 114, 107, 1801 195, 248, 64, 79, 36, 237, 140, 43, 27, 94, 74, 102, 152, 135, 102, 184, 150, 186, 206, 1802 185, 19, 165, 209, 48, 98, 98, 9, 3, 205, 208, 82, 250, 105, 132, 201, 73, 62, 60, 165, 1803 100, 128, 153, 9, 41, 118, 66, 95, 236, 214, 73, 135, 197, 68, 184, 10, 27, 116, 204, 1804 145, 50, 174, 58, 42, 183, 181, 119, 232, 126, 252, 217, 96, 162, 190, 103, 122, 64, 1805 87, 145, 45, 32, 207, 17, 239, 223, 3, 35, 14, 112, 119, 124, 141, 123, 208, 239, 105, 1806 81, 217, 151, 162, 190, 17, 88, 182, 176, 158, 81, 200, 42, 166, 133, 48, 23, 236, 55, 1807 117, 248, 233, 151, 203, 122, 155, 231, 46, 177, 20, 20, 151, 64, 222, 239, 226, 7, 21, 1808 254, 81, 202, 64, 232, 161, 235, 22, 51, 246, 207, 213, 0, 229, 138, 46, 222, 205, 157, 1809 108, 139, 253, 230, 80, 50, 2, 122, 212, 163, 100, 180, 114, 12, 113, 52, 56, 99, 188, 1810 42, 198, 212, 23, 182, 222, 56, 221, 200, 79, 96, 239, 221, 135, 10, 17, 106, 183, 56, 1811 104, 68, 94, 198, 196, 35, 200, 83, 204, 26, 185, 204, 212, 31, 183, 19, 111, 233, 13, 1812 72, 93, 53, 65, 111, 59, 242, 122, 160, 244, 162, 126, 38, 235, 156, 47, 88, 39, 132, 1813 153, 79, 0, 133, 78, 7, 218, 165, 241, 1814 ]; 1815 let e = 0x0001_0001u32; 1816 let d = [ 1817 145, 79, 21, 97, 233, 3, 192, 194, 177, 68, 181, 80, 120, 197, 23, 44, 185, 74, 144, 0, 1818 132, 149, 139, 11, 16, 224, 4, 112, 236, 94, 238, 97, 121, 124, 213, 145, 24, 253, 168, 1819 35, 190, 205, 132, 115, 33, 201, 38, 253, 246, 180, 66, 155, 165, 46, 3, 254, 68, 108, 1820 154, 247, 246, 45, 187, 0, 204, 96, 185, 157, 249, 174, 158, 38, 62, 244, 183, 76, 102, 1821 6, 219, 92, 212, 138, 59, 147, 163, 219, 111, 39, 105, 21, 236, 196, 38, 255, 114, 247, 1822 82, 104, 113, 204, 29, 152, 209, 219, 48, 239, 74, 129, 19, 247, 33, 239, 119, 166, 1823 216, 152, 94, 138, 238, 164, 242, 129, 50, 150, 57, 20, 53, 224, 56, 241, 138, 97, 111, 1824 215, 107, 212, 195, 146, 108, 143, 0, 229, 181, 171, 73, 152, 105, 146, 25, 243, 242, 1825 140, 252, 248, 162, 247, 63, 168, 180, 20, 153, 120, 10, 248, 211, 1, 71, 127, 212, 1826 249, 237, 203, 202, 48, 26, 216, 226, 228, 186, 13, 204, 70, 255, 240, 89, 255, 59, 83, 1827 31, 253, 55, 43, 158, 90, 248, 83, 32, 159, 105, 57, 134, 34, 96, 18, 255, 245, 153, 1828 162, 60, 91, 99, 220, 51, 44, 85, 114, 67, 125, 202, 65, 217, 245, 40, 8, 81, 165, 142, 1829 24, 245, 127, 122, 247, 152, 212, 75, 45, 59, 90, 184, 234, 31, 147, 36, 8, 212, 45, 1830 50, 23, 3, 25, 253, 87, 227, 79, 119, 161, 1831 ]; 1832 let p = BoxedUint::from_le_slice_vartime( 1833 [ 1834 215, 166, 5, 21, 11, 179, 41, 77, 198, 92, 165, 48, 77, 162, 42, 41, 206, 141, 60, 1835 69, 47, 164, 19, 92, 46, 72, 100, 238, 100, 53, 214, 197, 163, 185, 6, 140, 229, 1836 250, 195, 77, 8, 12, 5, 236, 178, 173, 86, 201, 43, 213, 165, 51, 108, 101, 161, 1837 99, 76, 240, 14, 234, 76, 197, 137, 53, 198, 168, 135, 205, 212, 198, 120, 29, 16, 1838 82, 98, 233, 236, 177, 12, 171, 141, 100, 107, 146, 33, 176, 125, 202, 172, 79, 1839 147, 179, 30, 62, 247, 206, 169, 19, 168, 114, 26, 73, 108, 178, 105, 84, 89, 191, 1840 168, 253, 228, 214, 54, 16, 212, 199, 111, 72, 3, 41, 247, 227, 165, 244, 32, 188, 1841 24, 247, 1842 ] 1843 .as_slice(), 1844 ); 1845 let p_2 = BoxedUint::from_le_slice_vartime( 1846 [ 1847 41, 25, 198, 240, 134, 206, 121, 57, 11, 5, 134, 192, 212, 77, 229, 197, 14, 78, 1848 85, 212, 190, 114, 179, 188, 21, 171, 174, 12, 104, 74, 15, 164, 136, 173, 62, 177, 1849 141, 213, 93, 102, 147, 83, 59, 124, 146, 59, 175, 213, 55, 27, 25, 248, 154, 29, 1850 39, 85, 50, 235, 134, 60, 203, 106, 186, 195, 190, 185, 71, 169, 142, 236, 92, 11, 1851 250, 187, 198, 8, 201, 184, 120, 178, 227, 87, 63, 243, 89, 227, 234, 184, 28, 252, 1852 112, 211, 193, 69, 23, 92, 5, 72, 93, 53, 69, 159, 73, 160, 105, 244, 249, 94, 214, 1853 173, 9, 236, 4, 255, 129, 11, 224, 140, 252, 168, 57, 143, 176, 241, 60, 219, 90, 1854 250, 1855 ] 1856 .as_slice(), 1857 ); 1858 let key = RsaPrivateKey::from_components( 1859 BoxedUint::from_le_slice_vartime(n.as_slice()), 1860 e.into(), 1861 BoxedUint::from_le_slice_vartime(d.as_slice()), 1862 vec![p, p_2], 1863 ) 1864 .unwrap() 1865 .to_public_key(); 1866 assert!( 1867 RsaPubKey::from_der(key.to_public_key_der().unwrap().as_bytes()) 1868 .is_ok_and(|k| *k.0 == *key.n().to_be_bytes() && BoxedUint::from(k.1) == *key.e()) 1869 ); 1870 } 1871 #[expect(clippy::unwrap_used, reason = "OK in tests")] 1872 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 1873 #[expect( 1874 clippy::cognitive_complexity, 1875 clippy::too_many_lines, 1876 reason = "a lot to test" 1877 )] 1878 #[test] 1879 fn eddsa_registration_deserialize_data_mismatch() { 1880 let c_data_json = serde_json::json!({}).to_string(); 1881 let att_obj: [u8; 143] = [ 1882 cbor::MAP_3, 1883 cbor::TEXT_3, 1884 b'f', 1885 b'm', 1886 b't', 1887 cbor::TEXT_4, 1888 b'n', 1889 b'o', 1890 b'n', 1891 b'e', 1892 cbor::TEXT_7, 1893 b'a', 1894 b't', 1895 b't', 1896 b'S', 1897 b't', 1898 b'm', 1899 b't', 1900 cbor::MAP_0, 1901 cbor::TEXT_8, 1902 b'a', 1903 b'u', 1904 b't', 1905 b'h', 1906 b'D', 1907 b'a', 1908 b't', 1909 b'a', 1910 cbor::BYTES_INFO_24, 1911 115, 1912 // `rpIdHash`. 1913 0, 1914 0, 1915 0, 1916 0, 1917 0, 1918 0, 1919 0, 1920 0, 1921 0, 1922 0, 1923 0, 1924 0, 1925 0, 1926 0, 1927 0, 1928 0, 1929 0, 1930 0, 1931 0, 1932 0, 1933 0, 1934 0, 1935 0, 1936 0, 1937 0, 1938 0, 1939 0, 1940 0, 1941 0, 1942 0, 1943 0, 1944 0, 1945 // `flags`. 1946 0b0100_0101, 1947 // `signCount`. 1948 0, 1949 0, 1950 0, 1951 0, 1952 // `aaguid`. 1953 0, 1954 0, 1955 0, 1956 0, 1957 0, 1958 0, 1959 0, 1960 0, 1961 0, 1962 0, 1963 0, 1964 0, 1965 0, 1966 0, 1967 0, 1968 0, 1969 // `credentialIdLength`. 1970 0, 1971 16, 1972 // `credentialId`. 1973 0, 1974 0, 1975 0, 1976 0, 1977 0, 1978 0, 1979 0, 1980 0, 1981 0, 1982 0, 1983 0, 1984 0, 1985 0, 1986 0, 1987 0, 1988 0, 1989 // Ed25519 COSE key. 1990 cbor::MAP_4, 1991 KTY, 1992 OKP, 1993 ALG, 1994 EDDSA, 1995 // `crv`. 1996 cbor::NEG_ONE, 1997 // `Ed25519`. 1998 cbor::SIX, 1999 // `x`. 2000 cbor::NEG_TWO, 2001 cbor::BYTES_INFO_24, 2002 32, 2003 // Compressed y-coordinate. 2004 1, 2005 1, 2006 1, 2007 1, 2008 1, 2009 1, 2010 1, 2011 1, 2012 1, 2013 1, 2014 1, 2015 1, 2016 1, 2017 1, 2018 1, 2019 1, 2020 1, 2021 1, 2022 1, 2023 1, 2024 1, 2025 1, 2026 1, 2027 1, 2028 1, 2029 1, 2030 1, 2031 1, 2032 1, 2033 1, 2034 1, 2035 1, 2036 ]; 2037 let pub_key = VerifyingKey::from_bytes(&[1; 32]) 2038 .unwrap() 2039 .to_public_key_der() 2040 .unwrap(); 2041 let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes()); 2042 let att_obj_len = att_obj.len(); 2043 let auth_data_start = att_obj_len - 113; 2044 let b64_adata = base64url_nopad::encode(&att_obj[auth_data_start..]); 2045 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 2046 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 2047 // Base case is valid. 2048 assert!( 2049 serde_json::from_str::<Registration>( 2050 serde_json::json!({ 2051 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2052 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2053 "response": { 2054 "clientDataJSON": b64_cdata_json, 2055 "authenticatorData": b64_adata, 2056 "transports": ["ble", "usb", "hybrid", "internal", "nfc", "smart-card"], 2057 "publicKey": b64_key, 2058 "publicKeyAlgorithm": -8i8, 2059 "attestationObject": b64_aobj, 2060 }, 2061 "authenticatorAttachment": "cross-platform", 2062 "clientExtensionResults": {}, 2063 "type": "public-key" 2064 }) 2065 .to_string() 2066 .as_str() 2067 ) 2068 .is_ok_and( 2069 |reg| reg.response.client_data_json == c_data_json.as_bytes() 2070 && reg.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 2071 && reg.response.attestation_object_and_c_data_hash[att_obj_len..] 2072 == *Sha256::digest(c_data_json.as_bytes()) 2073 && reg.response.transports.count() == 6 2074 && matches!( 2075 reg.authenticator_attachment, 2076 AuthenticatorAttachment::CrossPlatform 2077 ) 2078 && reg.client_extension_results.cred_props.is_none() 2079 && reg.client_extension_results.prf.is_none() 2080 ) 2081 ); 2082 // `id` and `rawId` mismatch. 2083 let mut err = Error::invalid_value( 2084 Unexpected::Bytes( 2085 base64url_nopad::decode(b"ABABABABABABABABABABAA") 2086 .unwrap() 2087 .as_slice(), 2088 ), 2089 &format!("id and rawId to match: CredentialId({:?})", [0u8; 16]).as_str(), 2090 ) 2091 .to_string() 2092 .into_bytes(); 2093 assert_eq!( 2094 serde_json::from_str::<Registration>( 2095 serde_json::json!({ 2096 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2097 "rawId": "ABABABABABABABABABABAA", 2098 "response": { 2099 "clientDataJSON": b64_cdata_json, 2100 "authenticatorData": b64_adata, 2101 "transports": [], 2102 "publicKey": b64_key, 2103 "publicKeyAlgorithm": -8i8, 2104 "attestationObject": b64_aobj, 2105 }, 2106 "clientExtensionResults": {}, 2107 "type": "public-key" 2108 }) 2109 .to_string() 2110 .as_str() 2111 ) 2112 .unwrap_err() 2113 .to_string() 2114 .into_bytes() 2115 .get(..err.len()), 2116 Some(err.as_slice()) 2117 ); 2118 // missing `id`. 2119 err = Error::missing_field("id").to_string().into_bytes(); 2120 assert_eq!( 2121 serde_json::from_str::<Registration>( 2122 serde_json::json!({ 2123 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2124 "response": { 2125 "clientDataJSON": b64_cdata_json, 2126 "authenticatorData": b64_adata, 2127 "transports": [], 2128 "publicKey": b64_key, 2129 "publicKeyAlgorithm": -8i8, 2130 "attestationObject": b64_aobj, 2131 }, 2132 "clientExtensionResults": {}, 2133 "type": "public-key" 2134 }) 2135 .to_string() 2136 .as_str() 2137 ) 2138 .unwrap_err() 2139 .to_string() 2140 .into_bytes() 2141 .get(..err.len()), 2142 Some(err.as_slice()) 2143 ); 2144 // `null` `id`. 2145 err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId") 2146 .to_string() 2147 .into_bytes(); 2148 assert_eq!( 2149 serde_json::from_str::<Registration>( 2150 serde_json::json!({ 2151 "id": null, 2152 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2153 "response": { 2154 "clientDataJSON": b64_cdata_json, 2155 "authenticatorData": b64_adata, 2156 "transports": [], 2157 "publicKey": b64_key, 2158 "publicKeyAlgorithm": -8i8, 2159 "attestationObject": b64_aobj, 2160 }, 2161 "clientExtensionResults": {}, 2162 "type": "public-key" 2163 }) 2164 .to_string() 2165 .as_str() 2166 ) 2167 .unwrap_err() 2168 .to_string() 2169 .into_bytes() 2170 .get(..err.len()), 2171 Some(err.as_slice()) 2172 ); 2173 // missing `rawId`. 2174 err = Error::missing_field("rawId").to_string().into_bytes(); 2175 assert_eq!( 2176 serde_json::from_str::<Registration>( 2177 serde_json::json!({ 2178 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2179 "response": { 2180 "clientDataJSON": b64_cdata_json, 2181 "authenticatorData": b64_adata, 2182 "transports": [], 2183 "publicKey": b64_key, 2184 "publicKeyAlgorithm": -8i8, 2185 "attestationObject": b64_aobj, 2186 }, 2187 "clientExtensionResults": {}, 2188 "type": "public-key" 2189 }) 2190 .to_string() 2191 .as_str() 2192 ) 2193 .unwrap_err() 2194 .to_string() 2195 .into_bytes() 2196 .get(..err.len()), 2197 Some(err.as_slice()) 2198 ); 2199 // `null` `rawId`. 2200 err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId") 2201 .to_string() 2202 .into_bytes(); 2203 assert_eq!( 2204 serde_json::from_str::<Registration>( 2205 serde_json::json!({ 2206 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2207 "rawId": null, 2208 "response": { 2209 "clientDataJSON": b64_cdata_json, 2210 "authenticatorData": b64_adata, 2211 "transports": [], 2212 "publicKey": b64_key, 2213 "publicKeyAlgorithm": -8i8, 2214 "attestationObject": b64_aobj, 2215 }, 2216 "clientExtensionResults": {}, 2217 "type": "public-key" 2218 }) 2219 .to_string() 2220 .as_str() 2221 ) 2222 .unwrap_err() 2223 .to_string() 2224 .into_bytes() 2225 .get(..err.len()), 2226 Some(err.as_slice()) 2227 ); 2228 // `id` and the credential id in authenticator data mismatch. 2229 err = Error::invalid_value( 2230 Unexpected::Bytes( 2231 base64url_nopad 2232 ::decode(b"ABABABABABABABABABABAA") 2233 .unwrap() 2234 .as_slice(), 2235 ), 2236 &format!("id, rawId, and the credential id in the attested credential data to all match: {:?}", [0u8; 16]).as_str(), 2237 ) 2238 .to_string().into_bytes(); 2239 assert_eq!( 2240 serde_json::from_str::<Registration>( 2241 serde_json::json!({ 2242 "id": "ABABABABABABABABABABAA", 2243 "rawId": "ABABABABABABABABABABAA", 2244 "response": { 2245 "clientDataJSON": b64_cdata_json, 2246 "authenticatorData": b64_adata, 2247 "transports": [], 2248 "publicKey": b64_key, 2249 "publicKeyAlgorithm": -8i8, 2250 "attestationObject": b64_aobj, 2251 }, 2252 "clientExtensionResults": {}, 2253 "type": "public-key" 2254 }) 2255 .to_string() 2256 .as_str() 2257 ) 2258 .unwrap_err() 2259 .to_string() 2260 .into_bytes() 2261 .get(..err.len()), 2262 Some(err.as_slice()) 2263 ); 2264 // `authenticatorData` mismatches `authData` in attestation object. 2265 let mut bad_auth = [0; 113]; 2266 bad_auth.copy_from_slice(&att_obj[auth_data_start..]); 2267 bad_auth[113 - 32..].copy_from_slice([0; 32].as_slice()); 2268 err = Error::invalid_value( 2269 Unexpected::Bytes(bad_auth.as_slice()), 2270 &format!("authenticator data to match the authenticator data portion of attestation object: {:?}", &att_obj[att_obj_len - bad_auth.len()..]).as_str(), 2271 ) 2272 .to_string().into_bytes(); 2273 assert_eq!( 2274 serde_json::from_str::<Registration>( 2275 serde_json::json!({ 2276 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2277 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2278 "response": { 2279 "clientDataJSON": b64_cdata_json, 2280 "authenticatorData": base64url_nopad::encode(bad_auth.as_slice()), 2281 "transports": [], 2282 "publicKey": b64_key, 2283 "publicKeyAlgorithm": -8i8, 2284 "attestationObject": b64_aobj, 2285 }, 2286 "clientExtensionResults": {}, 2287 "type": "public-key" 2288 }) 2289 .to_string() 2290 .as_str() 2291 ) 2292 .unwrap_err() 2293 .to_string() 2294 .into_bytes() 2295 .get(..err.len()), 2296 Some(err.as_slice()) 2297 ); 2298 // Missing `authenticatorData`. 2299 err = Error::missing_field("authenticatorData") 2300 .to_string() 2301 .into_bytes(); 2302 assert_eq!( 2303 serde_json::from_str::<Registration>( 2304 serde_json::json!({ 2305 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2306 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2307 "response": { 2308 "clientDataJSON": b64_cdata_json, 2309 "transports": [], 2310 "publicKey": b64_key, 2311 "publicKeyAlgorithm": -8i8, 2312 "attestationObject": b64_aobj, 2313 }, 2314 "clientExtensionResults": {}, 2315 "type": "public-key" 2316 }) 2317 .to_string() 2318 .as_str() 2319 ) 2320 .unwrap_err() 2321 .to_string() 2322 .into_bytes() 2323 .get(..err.len()), 2324 Some(err.as_slice()) 2325 ); 2326 // `null` `authenticatorData`. 2327 err = Error::invalid_type(Unexpected::Other("null"), &"authenticatorData") 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 "transports": [], 2338 "authenticatorData": null, 2339 "publicKey": b64_key, 2340 "publicKeyAlgorithm": -8i8, 2341 "attestationObject": b64_aobj, 2342 }, 2343 "clientExtensionResults": {}, 2344 "type": "public-key" 2345 }) 2346 .to_string() 2347 .as_str() 2348 ) 2349 .unwrap_err() 2350 .to_string() 2351 .into_bytes() 2352 .get(..err.len()), 2353 Some(err.as_slice()) 2354 ); 2355 // `publicKeyAlgorithm` mismatch. 2356 err = Error::invalid_value( 2357 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 2358 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Eddsa).as_str() 2359 ) 2360 .to_string().into_bytes(); 2361 assert_eq!( 2362 serde_json::from_str::<Registration>( 2363 serde_json::json!({ 2364 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2365 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2366 "response": { 2367 "clientDataJSON": b64_cdata_json, 2368 "authenticatorData": b64_adata, 2369 "transports": [], 2370 "publicKey": b64_key, 2371 "publicKeyAlgorithm": -7i8, 2372 "attestationObject": b64_aobj, 2373 }, 2374 "clientExtensionResults": {}, 2375 "type": "public-key" 2376 }) 2377 .to_string() 2378 .as_str() 2379 ) 2380 .unwrap_err() 2381 .to_string() 2382 .into_bytes() 2383 .get(..err.len()), 2384 Some(err.as_slice()) 2385 ); 2386 // Missing `publicKeyAlgorithm`. 2387 err = Error::missing_field("publicKeyAlgorithm") 2388 .to_string() 2389 .into_bytes(); 2390 assert_eq!( 2391 serde_json::from_str::<Registration>( 2392 serde_json::json!({ 2393 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2394 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2395 "response": { 2396 "clientDataJSON": b64_cdata_json, 2397 "authenticatorData": b64_adata, 2398 "transports": [], 2399 "publicKey": b64_key, 2400 "attestationObject": b64_aobj, 2401 }, 2402 "clientExtensionResults": {}, 2403 "type": "public-key" 2404 }) 2405 .to_string() 2406 .as_str() 2407 ) 2408 .unwrap_err() 2409 .to_string() 2410 .into_bytes() 2411 .get(..err.len()), 2412 Some(err.as_slice()) 2413 ); 2414 // `null` `publicKeyAlgorithm`. 2415 err = Error::invalid_type(Unexpected::Other("null"), &"publicKeyAlgorithm") 2416 .to_string() 2417 .into_bytes(); 2418 assert_eq!( 2419 serde_json::from_str::<Registration>( 2420 serde_json::json!({ 2421 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2422 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2423 "response": { 2424 "clientDataJSON": b64_cdata_json, 2425 "authenticatorData": b64_adata, 2426 "transports": [], 2427 "publicKey": b64_key, 2428 "publicKeyAlgorithm": null, 2429 "attestationObject": b64_aobj, 2430 }, 2431 "clientExtensionResults": {}, 2432 "type": "public-key" 2433 }) 2434 .to_string() 2435 .as_str() 2436 ) 2437 .unwrap_err() 2438 .to_string() 2439 .into_bytes() 2440 .get(..err.len()), 2441 Some(err.as_slice()) 2442 ); 2443 // `publicKey` mismatch. 2444 err = Error::invalid_value( 2445 Unexpected::Bytes([0; 32].as_slice()), 2446 &format!( 2447 "DER-encoded public key to match the public key within the attestation object: Ed25519(Ed25519PubKey({:?}))", 2448 &att_obj[att_obj.len() - 32..], 2449 ) 2450 .as_str(), 2451 ) 2452 .to_string().into_bytes(); 2453 assert_eq!(serde_json::from_str::<Registration>( 2454 serde_json::json!({ 2455 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2456 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2457 "response": { 2458 "clientDataJSON": b64_cdata_json, 2459 "authenticatorData": b64_adata, 2460 "transports": [], 2461 "publicKey": base64url_nopad::encode(VerifyingKey::from_bytes(&[0; 32]).unwrap().to_public_key_der().unwrap().as_bytes()), 2462 "publicKeyAlgorithm": -8i8, 2463 "attestationObject": b64_aobj, 2464 }, 2465 "clientExtensionResults": {}, 2466 "type": "public-key" 2467 }) 2468 .to_string() 2469 .as_str() 2470 ) 2471 .unwrap_err().to_string().into_bytes().get(..err.len()), 2472 Some(err.as_slice()) 2473 ); 2474 // Missing `publicKey` when using EdDSA, ES256, or RS256. 2475 err = Error::missing_field("publicKey").to_string().into_bytes(); 2476 assert_eq!( 2477 serde_json::from_str::<Registration>( 2478 serde_json::json!({ 2479 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2480 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2481 "response": { 2482 "clientDataJSON": b64_cdata_json, 2483 "authenticatorData": b64_adata, 2484 "transports": [], 2485 "publicKeyAlgorithm": -8i8, 2486 "attestationObject": b64_aobj, 2487 }, 2488 "clientExtensionResults": {}, 2489 "type": "public-key" 2490 }) 2491 .to_string() 2492 .as_str() 2493 ) 2494 .unwrap_err() 2495 .to_string() 2496 .into_bytes() 2497 .get(..err.len()), 2498 Some(err.as_slice()) 2499 ); 2500 // `null` `publicKey` when using EdDSA, ES256, or RS256. 2501 err = Error::invalid_type(Unexpected::Other("null"), &"publicKey") 2502 .to_string() 2503 .into_bytes(); 2504 assert_eq!( 2505 serde_json::from_str::<Registration>( 2506 serde_json::json!({ 2507 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2508 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2509 "response": { 2510 "clientDataJSON": b64_cdata_json, 2511 "authenticatorData": b64_adata, 2512 "transports": [], 2513 "publicKey": null, 2514 "publicKeyAlgorithm": -8i8, 2515 "attestationObject": b64_aobj, 2516 }, 2517 "clientExtensionResults": {}, 2518 "type": "public-key" 2519 }) 2520 .to_string() 2521 .as_str() 2522 ) 2523 .unwrap_err() 2524 .to_string() 2525 .into_bytes() 2526 .get(..err.len()), 2527 Some(err.as_slice()) 2528 ); 2529 // Missing `transports`. 2530 err = Error::missing_field("transports").to_string().into_bytes(); 2531 assert_eq!( 2532 serde_json::from_str::<Registration>( 2533 serde_json::json!({ 2534 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2535 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2536 "response": { 2537 "clientDataJSON": b64_cdata_json, 2538 "authenticatorData": b64_adata, 2539 "publicKey": b64_key, 2540 "publicKeyAlgorithm": -8i8, 2541 "attestationObject": b64_aobj, 2542 }, 2543 "clientExtensionResults": {}, 2544 "type": "public-key" 2545 }) 2546 .to_string() 2547 .as_str() 2548 ) 2549 .unwrap_err() 2550 .to_string() 2551 .into_bytes() 2552 .get(..err.len()), 2553 Some(err.as_slice()) 2554 ); 2555 // Duplicate `transports` are allowed. 2556 assert!( 2557 serde_json::from_str::<Registration>( 2558 serde_json::json!({ 2559 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2560 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2561 "response": { 2562 "clientDataJSON": b64_cdata_json, 2563 "authenticatorData": b64_adata, 2564 "transports": ["usb", "usb"], 2565 "publicKey": b64_key, 2566 "publicKeyAlgorithm": -8i8, 2567 "attestationObject": b64_aobj, 2568 }, 2569 "clientExtensionResults": {}, 2570 "type": "public-key" 2571 }) 2572 .to_string() 2573 .as_str() 2574 ) 2575 .is_ok_and(|reg| reg.response.transports.count() == 1) 2576 ); 2577 // `null` `transports`. 2578 err = Error::invalid_type(Unexpected::Other("null"), &"transports") 2579 .to_string() 2580 .into_bytes(); 2581 assert_eq!( 2582 serde_json::from_str::<Registration>( 2583 serde_json::json!({ 2584 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2585 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2586 "response": { 2587 "clientDataJSON": b64_cdata_json, 2588 "authenticatorData": b64_adata, 2589 "transports": null, 2590 "publicKey": b64_key, 2591 "publicKeyAlgorithm": -8i8, 2592 "attestationObject": b64_aobj, 2593 }, 2594 "clientExtensionResults": {}, 2595 "type": "public-key" 2596 }) 2597 .to_string() 2598 .as_str() 2599 ) 2600 .unwrap_err() 2601 .to_string() 2602 .into_bytes() 2603 .get(..err.len()), 2604 Some(err.as_slice()) 2605 ); 2606 // Unknown `transports`. 2607 err = Error::invalid_value( 2608 Unexpected::Str("Usb"), 2609 &"'ble', 'cable', 'hybrid', 'internal', 'nfc', 'smart-card', or 'usb'", 2610 ) 2611 .to_string() 2612 .into_bytes(); 2613 assert_eq!( 2614 serde_json::from_str::<Registration>( 2615 serde_json::json!({ 2616 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2617 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2618 "response": { 2619 "clientDataJSON": b64_cdata_json, 2620 "authenticatorData": b64_adata, 2621 "transports": ["Usb"], 2622 "publicKey": b64_key, 2623 "publicKeyAlgorithm": -8i8, 2624 "attestationObject": b64_aobj, 2625 }, 2626 "clientExtensionResults": {}, 2627 "type": "public-key" 2628 }) 2629 .to_string() 2630 .as_str() 2631 ) 2632 .unwrap_err() 2633 .to_string() 2634 .into_bytes() 2635 .get(..err.len()), 2636 Some(err.as_slice()) 2637 ); 2638 // `null` `authenticatorAttachment`. 2639 assert!( 2640 serde_json::from_str::<Registration>( 2641 serde_json::json!({ 2642 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2643 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2644 "response": { 2645 "clientDataJSON": b64_cdata_json, 2646 "authenticatorData": b64_adata, 2647 "transports": [], 2648 "publicKey": b64_key, 2649 "publicKeyAlgorithm": -8i8, 2650 "attestationObject": b64_aobj, 2651 }, 2652 "authenticatorAttachment": null, 2653 "clientExtensionResults": {}, 2654 "type": "public-key" 2655 }) 2656 .to_string() 2657 .as_str() 2658 ) 2659 .is_ok_and(|reg| matches!(reg.authenticator_attachment, AuthenticatorAttachment::None)) 2660 ); 2661 // Unknown `authenticatorAttachment`. 2662 err = Error::invalid_value( 2663 Unexpected::Str("Platform"), 2664 &"'platform' or 'cross-platform'", 2665 ) 2666 .to_string() 2667 .into_bytes(); 2668 assert_eq!( 2669 serde_json::from_str::<Registration>( 2670 serde_json::json!({ 2671 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2672 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2673 "response": { 2674 "clientDataJSON": b64_cdata_json, 2675 "authenticatorData": b64_adata, 2676 "transports": [], 2677 "publicKey": b64_key, 2678 "publicKeyAlgorithm": -8i8, 2679 "attestationObject": b64_aobj, 2680 }, 2681 "authenticatorAttachment": "Platform", 2682 "clientExtensionResults": {}, 2683 "type": "public-key" 2684 }) 2685 .to_string() 2686 .as_str() 2687 ) 2688 .unwrap_err() 2689 .to_string() 2690 .into_bytes() 2691 .get(..err.len()), 2692 Some(err.as_slice()) 2693 ); 2694 // Missing `clientDataJSON`. 2695 err = Error::missing_field("clientDataJSON") 2696 .to_string() 2697 .into_bytes(); 2698 assert_eq!( 2699 serde_json::from_str::<Registration>( 2700 serde_json::json!({ 2701 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2702 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2703 "response": { 2704 "authenticatorData": b64_adata, 2705 "transports": [], 2706 "publicKey": b64_key, 2707 "publicKeyAlgorithm": -8i8, 2708 "attestationObject": b64_aobj, 2709 }, 2710 "clientExtensionResults": {}, 2711 "type": "public-key" 2712 }) 2713 .to_string() 2714 .as_str() 2715 ) 2716 .unwrap_err() 2717 .to_string() 2718 .into_bytes() 2719 .get(..err.len()), 2720 Some(err.as_slice()) 2721 ); 2722 // `null` `clientDataJSON`. 2723 err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data") 2724 .to_string() 2725 .into_bytes(); 2726 assert_eq!( 2727 serde_json::from_str::<Registration>( 2728 serde_json::json!({ 2729 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2730 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2731 "response": { 2732 "clientDataJSON": null, 2733 "authenticatorData": b64_adata, 2734 "transports": [], 2735 "publicKey": b64_key, 2736 "publicKeyAlgorithm": -8i8, 2737 "attestationObject": b64_aobj, 2738 }, 2739 "clientExtensionResults": {}, 2740 "type": "public-key" 2741 }) 2742 .to_string() 2743 .as_str() 2744 ) 2745 .unwrap_err() 2746 .to_string() 2747 .into_bytes() 2748 .get(..err.len()), 2749 Some(err.as_slice()) 2750 ); 2751 // Missing `attestationObject`. 2752 err = Error::missing_field("attestationObject") 2753 .to_string() 2754 .into_bytes(); 2755 assert_eq!( 2756 serde_json::from_str::<Registration>( 2757 serde_json::json!({ 2758 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2759 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2760 "response": { 2761 "clientDataJSON": b64_cdata_json, 2762 "authenticatorData": b64_adata, 2763 "transports": [], 2764 "publicKey": b64_key, 2765 "publicKeyAlgorithm": -8i8, 2766 }, 2767 "clientExtensionResults": {}, 2768 "type": "public-key" 2769 }) 2770 .to_string() 2771 .as_str() 2772 ) 2773 .unwrap_err() 2774 .to_string() 2775 .into_bytes() 2776 .get(..err.len()), 2777 Some(err.as_slice()) 2778 ); 2779 // `null` `attestationObject`. 2780 err = Error::invalid_type( 2781 Unexpected::Other("null"), 2782 &"base64url-encoded attestation object", 2783 ) 2784 .to_string() 2785 .into_bytes(); 2786 assert_eq!( 2787 serde_json::from_str::<Registration>( 2788 serde_json::json!({ 2789 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2790 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2791 "response": { 2792 "clientDataJSON": b64_cdata_json, 2793 "authenticatorData": b64_adata, 2794 "transports": [], 2795 "publicKey": b64_key, 2796 "publicKeyAlgorithm": -8i8, 2797 "attestationObject": null, 2798 }, 2799 "clientExtensionResults": {}, 2800 "type": "public-key" 2801 }) 2802 .to_string() 2803 .as_str() 2804 ) 2805 .unwrap_err() 2806 .to_string() 2807 .into_bytes() 2808 .get(..err.len()), 2809 Some(err.as_slice()) 2810 ); 2811 // Missing `response`. 2812 err = Error::missing_field("response").to_string().into_bytes(); 2813 assert_eq!( 2814 serde_json::from_str::<Registration>( 2815 serde_json::json!({ 2816 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2817 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2818 "clientExtensionResults": {}, 2819 "type": "public-key" 2820 }) 2821 .to_string() 2822 .as_str() 2823 ) 2824 .unwrap_err() 2825 .to_string() 2826 .into_bytes() 2827 .get(..err.len()), 2828 Some(err.as_slice()) 2829 ); 2830 // `null` `response`. 2831 err = Error::invalid_type(Unexpected::Other("null"), &"AuthenticatorAttestation") 2832 .to_string() 2833 .into_bytes(); 2834 assert_eq!( 2835 serde_json::from_str::<Registration>( 2836 serde_json::json!({ 2837 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2838 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2839 "response": null, 2840 "clientExtensionResults": {}, 2841 "type": "public-key" 2842 }) 2843 .to_string() 2844 .as_str() 2845 ) 2846 .unwrap_err() 2847 .to_string() 2848 .into_bytes() 2849 .get(..err.len()), 2850 Some(err.as_slice()) 2851 ); 2852 // Empty `response`. 2853 err = Error::missing_field("clientDataJSON") 2854 .to_string() 2855 .into_bytes(); 2856 assert_eq!( 2857 serde_json::from_str::<Registration>( 2858 serde_json::json!({ 2859 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2860 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2861 "response": {}, 2862 "clientExtensionResults": {}, 2863 "type": "public-key" 2864 }) 2865 .to_string() 2866 .as_str() 2867 ) 2868 .unwrap_err() 2869 .to_string() 2870 .into_bytes() 2871 .get(..err.len()), 2872 Some(err.as_slice()) 2873 ); 2874 // Missing `clientExtensionResults`. 2875 err = Error::missing_field("clientExtensionResults") 2876 .to_string() 2877 .into_bytes(); 2878 assert_eq!( 2879 serde_json::from_str::<Registration>( 2880 serde_json::json!({ 2881 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2882 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2883 "response": { 2884 "clientDataJSON": b64_cdata_json, 2885 "authenticatorData": b64_adata, 2886 "transports": [], 2887 "publicKey": b64_key, 2888 "publicKeyAlgorithm": -8i8, 2889 "attestationObject": b64_aobj, 2890 }, 2891 "type": "public-key" 2892 }) 2893 .to_string() 2894 .as_str() 2895 ) 2896 .unwrap_err() 2897 .to_string() 2898 .into_bytes() 2899 .get(..err.len()), 2900 Some(err.as_slice()) 2901 ); 2902 // `null` `clientExtensionResults`. 2903 err = Error::invalid_type( 2904 Unexpected::Other("null"), 2905 &"clientExtensionResults to be a map of allowed client extensions", 2906 ) 2907 .to_string() 2908 .into_bytes(); 2909 assert_eq!( 2910 serde_json::from_str::<Registration>( 2911 serde_json::json!({ 2912 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2913 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2914 "response": { 2915 "clientDataJSON": b64_cdata_json, 2916 "authenticatorData": b64_adata, 2917 "transports": [], 2918 "publicKey": b64_key, 2919 "publicKeyAlgorithm": -8i8, 2920 "attestationObject": b64_aobj, 2921 }, 2922 "clientExtensionResults": null, 2923 "type": "public-key" 2924 }) 2925 .to_string() 2926 .as_str() 2927 ) 2928 .unwrap_err() 2929 .to_string() 2930 .into_bytes() 2931 .get(..err.len()), 2932 Some(err.as_slice()) 2933 ); 2934 // Missing `type`. 2935 err = Error::missing_field("type").to_string().into_bytes(); 2936 assert_eq!( 2937 serde_json::from_str::<Registration>( 2938 serde_json::json!({ 2939 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2940 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2941 "response": { 2942 "clientDataJSON": b64_cdata_json, 2943 "authenticatorData": b64_adata, 2944 "transports": [], 2945 "publicKey": b64_key, 2946 "publicKeyAlgorithm": -8i8, 2947 "attestationObject": b64_aobj, 2948 }, 2949 "clientExtensionResults": {}, 2950 }) 2951 .to_string() 2952 .as_str() 2953 ) 2954 .unwrap_err() 2955 .to_string() 2956 .into_bytes() 2957 .get(..err.len()), 2958 Some(err.as_slice()) 2959 ); 2960 // `null` `type`. 2961 err = Error::invalid_type(Unexpected::Other("null"), &"public-key") 2962 .to_string() 2963 .into_bytes(); 2964 assert_eq!( 2965 serde_json::from_str::<Registration>( 2966 serde_json::json!({ 2967 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2968 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2969 "response": { 2970 "clientDataJSON": b64_cdata_json, 2971 "authenticatorData": b64_adata, 2972 "transports": [], 2973 "publicKey": b64_key, 2974 "publicKeyAlgorithm": -8i8, 2975 "attestationObject": b64_aobj, 2976 }, 2977 "clientExtensionResults": {}, 2978 "type": null 2979 }) 2980 .to_string() 2981 .as_str() 2982 ) 2983 .unwrap_err() 2984 .to_string() 2985 .into_bytes() 2986 .get(..err.len()), 2987 Some(err.as_slice()) 2988 ); 2989 // Not exactly `public-type` `type`. 2990 err = Error::invalid_value(Unexpected::Str("Public-key"), &"public-key") 2991 .to_string() 2992 .into_bytes(); 2993 assert_eq!( 2994 serde_json::from_str::<Registration>( 2995 serde_json::json!({ 2996 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2997 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2998 "response": { 2999 "clientDataJSON": b64_cdata_json, 3000 "authenticatorData": b64_adata, 3001 "transports": [], 3002 "publicKey": b64_key, 3003 "publicKeyAlgorithm": -8i8, 3004 "attestationObject": b64_aobj, 3005 }, 3006 "clientExtensionResults": {}, 3007 "type": "Public-key" 3008 }) 3009 .to_string() 3010 .as_str() 3011 ) 3012 .unwrap_err() 3013 .to_string() 3014 .into_bytes() 3015 .get(..err.len()), 3016 Some(err.as_slice()) 3017 ); 3018 // `null`. 3019 err = Error::invalid_type(Unexpected::Other("null"), &"PublicKeyCredential") 3020 .to_string() 3021 .into_bytes(); 3022 assert_eq!( 3023 serde_json::from_str::<Registration>(serde_json::json!(null).to_string().as_str()) 3024 .unwrap_err() 3025 .to_string() 3026 .into_bytes() 3027 .get(..err.len()), 3028 Some(err.as_slice()) 3029 ); 3030 // Empty. 3031 err = Error::missing_field("response").to_string().into_bytes(); 3032 assert_eq!( 3033 serde_json::from_str::<Registration>(serde_json::json!({}).to_string().as_str()) 3034 .unwrap_err() 3035 .to_string() 3036 .into_bytes() 3037 .get(..err.len()), 3038 Some(err.as_slice()) 3039 ); 3040 // Unknown field in `response`. 3041 err = Error::unknown_field( 3042 "foo", 3043 [ 3044 "clientDataJSON", 3045 "attestationObject", 3046 "authenticatorData", 3047 "transports", 3048 "publicKey", 3049 "publicKeyAlgorithm", 3050 ] 3051 .as_slice(), 3052 ) 3053 .to_string() 3054 .into_bytes(); 3055 assert_eq!( 3056 serde_json::from_str::<Registration>( 3057 serde_json::json!({ 3058 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3059 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3060 "response": { 3061 "clientDataJSON": b64_cdata_json, 3062 "authenticatorData": b64_adata, 3063 "transports": [], 3064 "publicKey": b64_key, 3065 "publicKeyAlgorithm": -8i8, 3066 "attestationObject": b64_aobj, 3067 "foo": true, 3068 }, 3069 "clientExtensionResults": {}, 3070 "type": "public-key" 3071 }) 3072 .to_string() 3073 .as_str() 3074 ) 3075 .unwrap_err() 3076 .to_string() 3077 .into_bytes() 3078 .get(..err.len()), 3079 Some(err.as_slice()) 3080 ); 3081 // Duplicate field in `response`. 3082 err = Error::duplicate_field("transports") 3083 .to_string() 3084 .into_bytes(); 3085 assert_eq!( 3086 serde_json::from_str::<Registration>( 3087 format!( 3088 "{{ 3089 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3090 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3091 \"response\": {{ 3092 \"clientDataJSON\": \"{b64_cdata_json}\", 3093 \"authenticatorData\": \"{b64_adata}\", 3094 \"transports\": [], 3095 \"publicKey\": \"{b64_key}\", 3096 \"publicKeyAlgorithm\": -8, 3097 \"attestationObject\": \"{b64_aobj}\", 3098 \"transports\": [] 3099 }}, 3100 \"clientExtensionResults\": {{}}, 3101 \"type\": \"public-key\" 3102 3103 }}" 3104 ) 3105 .as_str() 3106 ) 3107 .unwrap_err() 3108 .to_string() 3109 .into_bytes() 3110 .get(..err.len()), 3111 Some(err.as_slice()) 3112 ); 3113 // Unknown field in `PublicKeyCredential`. 3114 err = Error::unknown_field( 3115 "foo", 3116 [ 3117 "id", 3118 "type", 3119 "rawId", 3120 "response", 3121 "authenticatorAttachment", 3122 "clientExtensionResults", 3123 ] 3124 .as_slice(), 3125 ) 3126 .to_string() 3127 .into_bytes(); 3128 assert_eq!( 3129 serde_json::from_str::<Registration>( 3130 serde_json::json!({ 3131 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3132 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3133 "response": { 3134 "clientDataJSON": b64_cdata_json, 3135 "authenticatorData": b64_adata, 3136 "transports": [], 3137 "publicKey": b64_key, 3138 "publicKeyAlgorithm": -8i8, 3139 "attestationObject": b64_aobj 3140 }, 3141 "clientExtensionResults": {}, 3142 "type": "public-key", 3143 "foo": true, 3144 }) 3145 .to_string() 3146 .as_str() 3147 ) 3148 .unwrap_err() 3149 .to_string() 3150 .into_bytes() 3151 .get(..err.len()), 3152 Some(err.as_slice()) 3153 ); 3154 // Duplicate field in `PublicKeyCredential`. 3155 err = Error::duplicate_field("id").to_string().into_bytes(); 3156 assert_eq!( 3157 serde_json::from_str::<Registration>( 3158 format!( 3159 "{{ 3160 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3161 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3162 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3163 \"response\": {{ 3164 \"clientDataJSON\": \"{b64_cdata_json}\", 3165 \"authenticatorData\": \"{b64_adata}\", 3166 \"transports\": [], 3167 \"publicKey\": \"{b64_key}\", 3168 \"publicKeyAlgorithm\": -8, 3169 \"attestationObject\": \"{b64_aobj}\" 3170 }}, 3171 \"clientExtensionResults\": {{}}, 3172 \"type\": \"public-key\" 3173 3174 }}" 3175 ) 3176 .as_str() 3177 ) 3178 .unwrap_err() 3179 .to_string() 3180 .into_bytes() 3181 .get(..err.len()), 3182 Some(err.as_slice()) 3183 ); 3184 } 3185 #[expect(clippy::unwrap_used, reason = "OK in tests")] 3186 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 3187 #[expect( 3188 clippy::cognitive_complexity, 3189 clippy::too_many_lines, 3190 reason = "a lot to test" 3191 )] 3192 #[test] 3193 fn client_extensions() { 3194 let c_data_json = serde_json::json!({}).to_string(); 3195 let att_obj: [u8; 143] = [ 3196 cbor::MAP_3, 3197 cbor::TEXT_3, 3198 b'f', 3199 b'm', 3200 b't', 3201 cbor::TEXT_4, 3202 b'n', 3203 b'o', 3204 b'n', 3205 b'e', 3206 cbor::TEXT_7, 3207 b'a', 3208 b't', 3209 b't', 3210 b'S', 3211 b't', 3212 b'm', 3213 b't', 3214 cbor::MAP_0, 3215 cbor::TEXT_8, 3216 b'a', 3217 b'u', 3218 b't', 3219 b'h', 3220 b'D', 3221 b'a', 3222 b't', 3223 b'a', 3224 cbor::BYTES_INFO_24, 3225 113, 3226 // `rpIdHash`. 3227 0, 3228 0, 3229 0, 3230 0, 3231 0, 3232 0, 3233 0, 3234 0, 3235 0, 3236 0, 3237 0, 3238 0, 3239 0, 3240 0, 3241 0, 3242 0, 3243 0, 3244 0, 3245 0, 3246 0, 3247 0, 3248 0, 3249 0, 3250 0, 3251 0, 3252 0, 3253 0, 3254 0, 3255 0, 3256 0, 3257 0, 3258 0, 3259 // `flags`. 3260 0b0100_0101, 3261 // `signCount`. 3262 0, 3263 0, 3264 0, 3265 0, 3266 // `aaguid`. 3267 0, 3268 0, 3269 0, 3270 0, 3271 0, 3272 0, 3273 0, 3274 0, 3275 0, 3276 0, 3277 0, 3278 0, 3279 0, 3280 0, 3281 0, 3282 0, 3283 // `credentialIdLength`. 3284 0, 3285 16, 3286 // `credentialId`. 3287 0, 3288 0, 3289 0, 3290 0, 3291 0, 3292 0, 3293 0, 3294 0, 3295 0, 3296 0, 3297 0, 3298 0, 3299 0, 3300 0, 3301 0, 3302 0, 3303 // Ed25519 COSE key. 3304 cbor::MAP_4, 3305 KTY, 3306 OKP, 3307 ALG, 3308 EDDSA, 3309 // `crv`. 3310 cbor::NEG_ONE, 3311 // `Ed25519`. 3312 cbor::SIX, 3313 // `x`. 3314 cbor::NEG_TWO, 3315 cbor::BYTES_INFO_24, 3316 32, 3317 // Compressed y-coordinate. 3318 1, 3319 1, 3320 1, 3321 1, 3322 1, 3323 1, 3324 1, 3325 1, 3326 1, 3327 1, 3328 1, 3329 1, 3330 1, 3331 1, 3332 1, 3333 1, 3334 1, 3335 1, 3336 1, 3337 1, 3338 1, 3339 1, 3340 1, 3341 1, 3342 1, 3343 1, 3344 1, 3345 1, 3346 1, 3347 1, 3348 1, 3349 1, 3350 ]; 3351 let pub_key = VerifyingKey::from_bytes(&[1; 32]) 3352 .unwrap() 3353 .to_public_key_der() 3354 .unwrap(); 3355 let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes()); 3356 let b64_adata = base64url_nopad::encode(&att_obj[att_obj.len() - 113..]); 3357 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 3358 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 3359 // Base case is valid. 3360 assert!( 3361 serde_json::from_str::<Registration>( 3362 serde_json::json!({ 3363 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3364 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3365 "response": { 3366 "clientDataJSON": b64_cdata_json, 3367 "authenticatorData": b64_adata, 3368 "transports": [], 3369 "publicKey": b64_key, 3370 "publicKeyAlgorithm": -8i8, 3371 "attestationObject": b64_aobj, 3372 }, 3373 "clientExtensionResults": {}, 3374 "type": "public-key" 3375 }) 3376 .to_string() 3377 .as_str() 3378 ) 3379 .is_ok_and( 3380 |reg| reg.response.client_data_json == c_data_json.as_bytes() 3381 && reg.response.attestation_object_and_c_data_hash[..att_obj.len()] == att_obj 3382 && reg.response.attestation_object_and_c_data_hash[att_obj.len()..] 3383 == *Sha256::digest(c_data_json.as_bytes()) 3384 && reg.response.transports.is_empty() 3385 && matches!(reg.authenticator_attachment, AuthenticatorAttachment::None) 3386 && reg.client_extension_results.cred_props.is_none() 3387 && reg.client_extension_results.prf.is_none() 3388 ) 3389 ); 3390 // `null` `credProps`. 3391 assert!( 3392 serde_json::from_str::<Registration>( 3393 serde_json::json!({ 3394 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3395 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3396 "response": { 3397 "clientDataJSON": b64_cdata_json, 3398 "authenticatorData": b64_adata, 3399 "transports": [], 3400 "publicKey": b64_key, 3401 "publicKeyAlgorithm": -8i8, 3402 "attestationObject": b64_aobj, 3403 }, 3404 "clientExtensionResults": { 3405 "credProps": null 3406 }, 3407 "type": "public-key" 3408 }) 3409 .to_string() 3410 .as_str() 3411 ) 3412 .is_ok_and(|reg| reg.client_extension_results.cred_props.is_none() 3413 && reg.client_extension_results.prf.is_none()) 3414 ); 3415 // `null` `prf`. 3416 assert!( 3417 serde_json::from_str::<Registration>( 3418 serde_json::json!({ 3419 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3420 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3421 "response": { 3422 "clientDataJSON": b64_cdata_json, 3423 "authenticatorData": b64_adata, 3424 "transports": [], 3425 "publicKey": b64_key, 3426 "publicKeyAlgorithm": -8i8, 3427 "attestationObject": b64_aobj, 3428 }, 3429 "clientExtensionResults": { 3430 "prf": null 3431 }, 3432 "type": "public-key" 3433 }) 3434 .to_string() 3435 .as_str() 3436 ) 3437 .is_ok_and(|reg| reg.client_extension_results.cred_props.is_none() 3438 && reg.client_extension_results.prf.is_none()) 3439 ); 3440 // Unknown `clientExtensionResults`. 3441 let mut err = Error::unknown_field("CredProps", ["credProps", "prf"].as_slice()) 3442 .to_string() 3443 .into_bytes(); 3444 assert_eq!( 3445 serde_json::from_str::<Registration>( 3446 serde_json::json!({ 3447 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3448 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3449 "response": { 3450 "clientDataJSON": b64_cdata_json, 3451 "authenticatorData": b64_adata, 3452 "transports": [], 3453 "publicKey": b64_key, 3454 "publicKeyAlgorithm": -8i8, 3455 "attestationObject": b64_aobj, 3456 }, 3457 "clientExtensionResults": { 3458 "CredProps": { 3459 "rk": true 3460 } 3461 }, 3462 "type": "public-key" 3463 }) 3464 .to_string() 3465 .as_str() 3466 ) 3467 .unwrap_err() 3468 .to_string() 3469 .into_bytes() 3470 .get(..err.len()), 3471 Some(err.as_slice()) 3472 ); 3473 // Duplicate field. 3474 err = Error::duplicate_field("credProps").to_string().into_bytes(); 3475 assert_eq!( 3476 serde_json::from_str::<Registration>( 3477 format!( 3478 "{{ 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\": -8, 3487 \"attestationObject\": \"{b64_aobj}\" 3488 }}, 3489 \"clientExtensionResults\": {{ 3490 \"credProps\": null, 3491 \"credProps\": null 3492 }}, 3493 \"type\": \"public-key\" 3494 }}" 3495 ) 3496 .as_str() 3497 ) 3498 .unwrap_err() 3499 .to_string() 3500 .into_bytes() 3501 .get(..err.len()), 3502 Some(err.as_slice()) 3503 ); 3504 // `null` `rk`. 3505 assert!( 3506 serde_json::from_str::<Registration>( 3507 serde_json::json!({ 3508 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3509 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3510 "response": { 3511 "clientDataJSON": b64_cdata_json, 3512 "authenticatorData": b64_adata, 3513 "transports": [], 3514 "publicKey": b64_key, 3515 "publicKeyAlgorithm": -8i8, 3516 "attestationObject": b64_aobj, 3517 }, 3518 "clientExtensionResults": { 3519 "credProps": { 3520 "rk": null 3521 } 3522 }, 3523 "type": "public-key" 3524 }) 3525 .to_string() 3526 .as_str() 3527 ) 3528 .is_ok_and(|reg| reg 3529 .client_extension_results 3530 .cred_props 3531 .is_some_and(|props| props.rk.is_none()) 3532 && reg.client_extension_results.prf.is_none()) 3533 ); 3534 // Missing `rk`. 3535 assert!( 3536 serde_json::from_str::<Registration>( 3537 serde_json::json!({ 3538 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3539 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3540 "response": { 3541 "clientDataJSON": b64_cdata_json, 3542 "authenticatorData": b64_adata, 3543 "transports": [], 3544 "publicKey": b64_key, 3545 "publicKeyAlgorithm": -8i8, 3546 "attestationObject": b64_aobj, 3547 }, 3548 "clientExtensionResults": { 3549 "credProps": {} 3550 }, 3551 "type": "public-key" 3552 }) 3553 .to_string() 3554 .as_str() 3555 ) 3556 .is_ok_and(|reg| reg 3557 .client_extension_results 3558 .cred_props 3559 .is_some_and(|props| props.rk.is_none()) 3560 && reg.client_extension_results.prf.is_none()) 3561 ); 3562 // `true` rk`. 3563 assert!( 3564 serde_json::from_str::<Registration>( 3565 serde_json::json!({ 3566 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3567 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3568 "response": { 3569 "clientDataJSON": b64_cdata_json, 3570 "authenticatorData": b64_adata, 3571 "transports": [], 3572 "publicKey": b64_key, 3573 "publicKeyAlgorithm": -8i8, 3574 "attestationObject": b64_aobj, 3575 }, 3576 "clientExtensionResults": { 3577 "credProps": { 3578 "rk": true 3579 } 3580 }, 3581 "type": "public-key" 3582 }) 3583 .to_string() 3584 .as_str() 3585 ) 3586 .is_ok_and(|reg| reg 3587 .client_extension_results 3588 .cred_props 3589 .is_some_and(|props| props.rk.unwrap_or_default()) 3590 && reg.client_extension_results.prf.is_none()) 3591 ); 3592 // `false` rk`. 3593 assert!( 3594 serde_json::from_str::<Registration>( 3595 serde_json::json!({ 3596 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3597 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3598 "response": { 3599 "clientDataJSON": b64_cdata_json, 3600 "authenticatorData": b64_adata, 3601 "transports": [], 3602 "publicKey": b64_key, 3603 "publicKeyAlgorithm": -8i8, 3604 "attestationObject": b64_aobj, 3605 }, 3606 "clientExtensionResults": { 3607 "credProps": { 3608 "rk": false 3609 } 3610 }, 3611 "type": "public-key" 3612 }) 3613 .to_string() 3614 .as_str() 3615 ) 3616 .is_ok_and(|reg| reg 3617 .client_extension_results 3618 .cred_props 3619 .is_some_and(|props| props.rk.is_some_and(|rk| !rk)) 3620 && reg.client_extension_results.prf.is_none()) 3621 ); 3622 // Invalid `rk`. 3623 err = Error::invalid_type(Unexpected::Unsigned(3), &"a boolean") 3624 .to_string() 3625 .into_bytes(); 3626 assert_eq!( 3627 serde_json::from_str::<Registration>( 3628 serde_json::json!({ 3629 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3630 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3631 "response": { 3632 "clientDataJSON": b64_cdata_json, 3633 "authenticatorData": b64_adata, 3634 "transports": [], 3635 "publicKey": b64_key, 3636 "publicKeyAlgorithm": -8i8, 3637 "attestationObject": b64_aobj, 3638 }, 3639 "clientExtensionResults": { 3640 "credProps": { 3641 "rk": 3u8 3642 } 3643 }, 3644 "type": "public-key" 3645 }) 3646 .to_string() 3647 .as_str() 3648 ) 3649 .unwrap_err() 3650 .to_string() 3651 .into_bytes() 3652 .get(..err.len()), 3653 Some(err.as_slice()) 3654 ); 3655 // Unknown `credProps` field. 3656 err = Error::unknown_field("Rk", ["rk"].as_slice()) 3657 .to_string() 3658 .into_bytes(); 3659 assert_eq!( 3660 serde_json::from_str::<Registration>( 3661 serde_json::json!({ 3662 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3663 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3664 "response": { 3665 "clientDataJSON": b64_cdata_json, 3666 "authenticatorData": b64_adata, 3667 "transports": [], 3668 "publicKey": b64_key, 3669 "publicKeyAlgorithm": -8i8, 3670 "attestationObject": b64_aobj, 3671 }, 3672 "clientExtensionResults": { 3673 "credProps": { 3674 "Rk": true, 3675 } 3676 }, 3677 "type": "public-key" 3678 }) 3679 .to_string() 3680 .as_str() 3681 ) 3682 .unwrap_err() 3683 .to_string() 3684 .into_bytes() 3685 .get(..err.len()), 3686 Some(err.as_slice()) 3687 ); 3688 // Duplicate field in `credProps`. 3689 err = Error::duplicate_field("rk").to_string().into_bytes(); 3690 assert_eq!( 3691 serde_json::from_str::<Registration>( 3692 format!( 3693 "{{ 3694 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3695 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3696 \"response\": {{ 3697 \"clientDataJSON\": \"{b64_cdata_json}\", 3698 \"authenticatorData\": \"{b64_adata}\", 3699 \"transports\": [], 3700 \"publicKey\": \"{b64_key}\", 3701 \"publicKeyAlgorithm\": -8, 3702 \"attestationObject\": \"{b64_aobj}\" 3703 }}, 3704 \"clientExtensionResults\": {{ 3705 \"credProps\": {{ 3706 \"rk\": true, 3707 \"rk\": true 3708 }} 3709 }}, 3710 \"type\": \"public-key\" 3711 }}" 3712 ) 3713 .as_str() 3714 ) 3715 .unwrap_err() 3716 .to_string() 3717 .into_bytes() 3718 .get(..err.len()), 3719 Some(err.as_slice()) 3720 ); 3721 // `null` `enabled`. 3722 err = Error::invalid_type(Unexpected::Other("null"), &"a boolean") 3723 .to_string() 3724 .into_bytes(); 3725 assert_eq!( 3726 serde_json::from_str::<Registration>( 3727 serde_json::json!({ 3728 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3729 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3730 "response": { 3731 "clientDataJSON": b64_cdata_json, 3732 "authenticatorData": b64_adata, 3733 "transports": [], 3734 "publicKey": b64_key, 3735 "publicKeyAlgorithm": -8i8, 3736 "attestationObject": b64_aobj, 3737 }, 3738 "clientExtensionResults": { 3739 "prf": { 3740 "enabled": null 3741 } 3742 }, 3743 "type": "public-key" 3744 }) 3745 .to_string() 3746 .as_str() 3747 ) 3748 .unwrap_err() 3749 .to_string() 3750 .into_bytes() 3751 .get(..err.len()), 3752 Some(err.as_slice()) 3753 ); 3754 // Missing `enabled`. 3755 err = Error::missing_field("enabled").to_string().into_bytes(); 3756 assert_eq!( 3757 serde_json::from_str::<Registration>( 3758 serde_json::json!({ 3759 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3760 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3761 "response": { 3762 "clientDataJSON": b64_cdata_json, 3763 "authenticatorData": b64_adata, 3764 "transports": [], 3765 "publicKey": b64_key, 3766 "publicKeyAlgorithm": -8i8, 3767 "attestationObject": b64_aobj, 3768 }, 3769 "clientExtensionResults": { 3770 "prf": {} 3771 }, 3772 "type": "public-key" 3773 }) 3774 .to_string() 3775 .as_str() 3776 ) 3777 .unwrap_err() 3778 .to_string() 3779 .into_bytes() 3780 .get(..err.len()), 3781 Some(err.as_slice()) 3782 ); 3783 // `true` `enabled`. 3784 assert!( 3785 serde_json::from_str::<Registration>( 3786 serde_json::json!({ 3787 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3788 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3789 "response": { 3790 "clientDataJSON": b64_cdata_json, 3791 "authenticatorData": b64_adata, 3792 "transports": [], 3793 "publicKey": b64_key, 3794 "publicKeyAlgorithm": -8i8, 3795 "attestationObject": b64_aobj, 3796 }, 3797 "clientExtensionResults": { 3798 "prf": { 3799 "enabled": true 3800 } 3801 }, 3802 "type": "public-key" 3803 }) 3804 .to_string() 3805 .as_str() 3806 ) 3807 .is_ok_and(|reg| reg.client_extension_results.cred_props.is_none() 3808 && reg 3809 .client_extension_results 3810 .prf 3811 .is_some_and(|prf| prf.enabled)) 3812 ); 3813 // `false` `enabled`. 3814 assert!( 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": false, 3830 } 3831 }, 3832 "type": "public-key" 3833 }) 3834 .to_string() 3835 .as_str() 3836 ) 3837 .is_ok_and(|reg| reg.client_extension_results.cred_props.is_none() 3838 && reg 3839 .client_extension_results 3840 .prf 3841 .is_some_and(|prf| !prf.enabled)) 3842 ); 3843 // Invalid `enabled`. 3844 err = Error::invalid_type(Unexpected::Unsigned(3), &"a boolean") 3845 .to_string() 3846 .into_bytes(); 3847 assert_eq!( 3848 serde_json::from_str::<Registration>( 3849 serde_json::json!({ 3850 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3851 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3852 "response": { 3853 "clientDataJSON": b64_cdata_json, 3854 "authenticatorData": b64_adata, 3855 "transports": [], 3856 "publicKey": b64_key, 3857 "publicKeyAlgorithm": -8i8, 3858 "attestationObject": b64_aobj, 3859 }, 3860 "clientExtensionResults": { 3861 "prf": { 3862 "enabled": 3u8 3863 } 3864 }, 3865 "type": "public-key" 3866 }) 3867 .to_string() 3868 .as_str() 3869 ) 3870 .unwrap_err() 3871 .to_string() 3872 .into_bytes() 3873 .get(..err.len()), 3874 Some(err.as_slice()) 3875 ); 3876 // `null` `results` with `enabled` `true`. 3877 assert!( 3878 serde_json::from_str::<Registration>( 3879 serde_json::json!({ 3880 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3881 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3882 "response": { 3883 "clientDataJSON": b64_cdata_json, 3884 "authenticatorData": b64_adata, 3885 "transports": [], 3886 "publicKey": b64_key, 3887 "publicKeyAlgorithm": -8i8, 3888 "attestationObject": b64_aobj, 3889 }, 3890 "clientExtensionResults": { 3891 "prf": { 3892 "enabled": true, 3893 "results": null, 3894 } 3895 }, 3896 "type": "public-key" 3897 }) 3898 .to_string() 3899 .as_str() 3900 ) 3901 .is_ok_and(|reg| reg.client_extension_results.cred_props.is_none() 3902 && reg 3903 .client_extension_results 3904 .prf 3905 .is_some_and(|prf| prf.enabled)) 3906 ); 3907 // `null` `results` with `enabled` `false`. 3908 err = Error::custom( 3909 "prf must not have 'results', including a null 'results', if 'enabled' is false", 3910 ) 3911 .to_string() 3912 .into_bytes(); 3913 assert_eq!( 3914 serde_json::from_str::<Registration>( 3915 serde_json::json!({ 3916 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3917 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3918 "response": { 3919 "clientDataJSON": b64_cdata_json, 3920 "authenticatorData": b64_adata, 3921 "transports": [], 3922 "publicKey": b64_key, 3923 "publicKeyAlgorithm": -8i8, 3924 "attestationObject": b64_aobj, 3925 }, 3926 "clientExtensionResults": { 3927 "prf": { 3928 "enabled": false, 3929 "results": null 3930 } 3931 }, 3932 "type": "public-key" 3933 }) 3934 .to_string() 3935 .as_str() 3936 ) 3937 .unwrap_err() 3938 .to_string() 3939 .into_bytes() 3940 .get(..err.len()), 3941 Some(err.as_slice()) 3942 ); 3943 // Duplicate field in `prf`. 3944 err = Error::duplicate_field("enabled").to_string().into_bytes(); 3945 assert_eq!( 3946 serde_json::from_str::<Registration>( 3947 format!( 3948 "{{ 3949 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3950 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 3951 \"response\": {{ 3952 \"clientDataJSON\": \"{b64_cdata_json}\", 3953 \"authenticatorData\": \"{b64_adata}\", 3954 \"transports\": [], 3955 \"publicKey\": \"{b64_key}\", 3956 \"publicKeyAlgorithm\": -8, 3957 \"attestationObject\": \"{b64_aobj}\" 3958 }}, 3959 \"clientExtensionResults\": {{ 3960 \"prf\": {{ 3961 \"enabled\": true, 3962 \"enabled\": true 3963 }} 3964 }}, 3965 \"type\": \"public-key\" 3966 }}" 3967 ) 3968 .as_str() 3969 ) 3970 .unwrap_err() 3971 .to_string() 3972 .into_bytes() 3973 .get(..err.len()), 3974 Some(err.as_slice()) 3975 ); 3976 // Missing `first`. 3977 err = Error::missing_field("first").to_string().into_bytes(); 3978 assert_eq!( 3979 serde_json::from_str::<Registration>( 3980 serde_json::json!({ 3981 "id": "AAAAAAAAAAAAAAAAAAAAAA", 3982 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 3983 "response": { 3984 "clientDataJSON": b64_cdata_json, 3985 "authenticatorData": b64_adata, 3986 "transports": [], 3987 "publicKey": b64_key, 3988 "publicKeyAlgorithm": -8i8, 3989 "attestationObject": b64_aobj, 3990 }, 3991 "clientExtensionResults": { 3992 "prf": { 3993 "enabled": true, 3994 "results": {}, 3995 } 3996 }, 3997 "type": "public-key" 3998 }) 3999 .to_string() 4000 .as_str() 4001 ) 4002 .unwrap_err() 4003 .to_string() 4004 .into_bytes() 4005 .get(..err.len()), 4006 Some(err.as_slice()) 4007 ); 4008 // `null` `first`. 4009 assert!( 4010 serde_json::from_str::<Registration>( 4011 serde_json::json!({ 4012 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4013 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4014 "response": { 4015 "clientDataJSON": b64_cdata_json, 4016 "authenticatorData": b64_adata, 4017 "transports": [], 4018 "publicKey": b64_key, 4019 "publicKeyAlgorithm": -8i8, 4020 "attestationObject": b64_aobj, 4021 }, 4022 "clientExtensionResults": { 4023 "prf": { 4024 "enabled": true, 4025 "results": { 4026 "first": null 4027 }, 4028 } 4029 }, 4030 "type": "public-key" 4031 }) 4032 .to_string() 4033 .as_str() 4034 ) 4035 .is_ok_and(|reg| reg.client_extension_results.cred_props.is_none() 4036 && reg 4037 .client_extension_results 4038 .prf 4039 .is_some_and(|prf| prf.enabled)) 4040 ); 4041 // `null` `second`. 4042 assert!( 4043 serde_json::from_str::<Registration>( 4044 serde_json::json!({ 4045 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4046 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4047 "response": { 4048 "clientDataJSON": b64_cdata_json, 4049 "authenticatorData": b64_adata, 4050 "transports": [], 4051 "publicKey": b64_key, 4052 "publicKeyAlgorithm": -8i8, 4053 "attestationObject": b64_aobj, 4054 }, 4055 "clientExtensionResults": { 4056 "prf": { 4057 "enabled": true, 4058 "results": { 4059 "first": null, 4060 "second": null 4061 }, 4062 } 4063 }, 4064 "type": "public-key" 4065 }) 4066 .to_string() 4067 .as_str() 4068 ) 4069 .is_ok_and(|reg| reg.client_extension_results.cred_props.is_none() 4070 && reg 4071 .client_extension_results 4072 .prf 4073 .is_some_and(|prf| prf.enabled)) 4074 ); 4075 // Non-`null` `first`. 4076 err = Error::invalid_type(Unexpected::Option, &"null") 4077 .to_string() 4078 .into_bytes(); 4079 assert_eq!( 4080 serde_json::from_str::<Registration>( 4081 serde_json::json!({ 4082 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4083 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4084 "response": { 4085 "clientDataJSON": b64_cdata_json, 4086 "authenticatorData": b64_adata, 4087 "transports": [], 4088 "publicKey": b64_key, 4089 "publicKeyAlgorithm": -8i8, 4090 "attestationObject": b64_aobj, 4091 }, 4092 "clientExtensionResults": { 4093 "prf": { 4094 "enabled": true, 4095 "results": { 4096 "first": "" 4097 }, 4098 } 4099 }, 4100 "type": "public-key" 4101 }) 4102 .to_string() 4103 .as_str() 4104 ) 4105 .unwrap_err() 4106 .to_string() 4107 .into_bytes() 4108 .get(..err.len()), 4109 Some(err.as_slice()) 4110 ); 4111 // Non-`null` `second`. 4112 err = Error::invalid_type(Unexpected::Option, &"null") 4113 .to_string() 4114 .into_bytes(); 4115 assert_eq!( 4116 serde_json::from_str::<Registration>( 4117 serde_json::json!({ 4118 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4119 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4120 "response": { 4121 "clientDataJSON": b64_cdata_json, 4122 "authenticatorData": b64_adata, 4123 "transports": [], 4124 "publicKey": b64_key, 4125 "publicKeyAlgorithm": -8i8, 4126 "attestationObject": b64_aobj, 4127 }, 4128 "clientExtensionResults": { 4129 "prf": { 4130 "enabled": true, 4131 "results": { 4132 "first": null, 4133 "second": "" 4134 }, 4135 } 4136 }, 4137 "type": "public-key" 4138 }) 4139 .to_string() 4140 .as_str() 4141 ) 4142 .unwrap_err() 4143 .to_string() 4144 .into_bytes() 4145 .get(..err.len()), 4146 Some(err.as_slice()) 4147 ); 4148 // Unknown `prf` field. 4149 err = Error::unknown_field("Results", ["enabled", "results"].as_slice()) 4150 .to_string() 4151 .into_bytes(); 4152 assert_eq!( 4153 serde_json::from_str::<Registration>( 4154 serde_json::json!({ 4155 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4156 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4157 "response": { 4158 "clientDataJSON": b64_cdata_json, 4159 "authenticatorData": b64_adata, 4160 "transports": [], 4161 "publicKey": b64_key, 4162 "publicKeyAlgorithm": -8i8, 4163 "attestationObject": b64_aobj, 4164 }, 4165 "clientExtensionResults": { 4166 "prf": { 4167 "enabled": true, 4168 "Results": null 4169 } 4170 }, 4171 "type": "public-key" 4172 }) 4173 .to_string() 4174 .as_str() 4175 ) 4176 .unwrap_err() 4177 .to_string() 4178 .into_bytes() 4179 .get(..err.len()), 4180 Some(err.as_slice()) 4181 ); 4182 // Unknown `results` field. 4183 err = Error::unknown_field("Second", ["first", "second"].as_slice()) 4184 .to_string() 4185 .into_bytes(); 4186 assert_eq!( 4187 serde_json::from_str::<Registration>( 4188 serde_json::json!({ 4189 "id": "AAAAAAAAAAAAAAAAAAAAAA", 4190 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 4191 "response": { 4192 "clientDataJSON": b64_cdata_json, 4193 "authenticatorData": b64_adata, 4194 "transports": [], 4195 "publicKey": b64_key, 4196 "publicKeyAlgorithm": -8i8, 4197 "attestationObject": b64_aobj, 4198 }, 4199 "clientExtensionResults": { 4200 "prf": { 4201 "enabled": true, 4202 "results": { 4203 "first": null, 4204 "Second": null 4205 } 4206 } 4207 }, 4208 "type": "public-key" 4209 }) 4210 .to_string() 4211 .as_str() 4212 ) 4213 .unwrap_err() 4214 .to_string() 4215 .into_bytes() 4216 .get(..err.len()), 4217 Some(err.as_slice()) 4218 ); 4219 // Duplicate field in `results`. 4220 err = Error::duplicate_field("first").to_string().into_bytes(); 4221 assert_eq!( 4222 serde_json::from_str::<Registration>( 4223 format!( 4224 "{{ 4225 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 4226 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 4227 \"response\": {{ 4228 \"clientDataJSON\": \"{b64_cdata_json}\", 4229 \"authenticatorData\": \"{b64_adata}\", 4230 \"transports\": [], 4231 \"publicKey\": \"{b64_key}\", 4232 \"publicKeyAlgorithm\": -8, 4233 \"attestationObject\": \"{b64_aobj}\" 4234 }}, 4235 \"clientExtensionResults\": {{ 4236 \"prf\": {{ 4237 \"enabled\": true, 4238 \"results\": {{ 4239 \"first\": null, 4240 \"first\": null 4241 }} 4242 }} 4243 }}, 4244 \"type\": \"public-key\" 4245 }}" 4246 ) 4247 .as_str() 4248 ) 4249 .unwrap_err() 4250 .to_string() 4251 .into_bytes() 4252 .get(..err.len()), 4253 Some(err.as_slice()) 4254 ); 4255 } 4256 #[expect( 4257 clippy::assertions_on_result_states, 4258 clippy::unwrap_used, 4259 reason = "OK in tests" 4260 )] 4261 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 4262 #[expect(clippy::too_many_lines, reason = "a lot to test")] 4263 #[test] 4264 fn mldsa87_registration_deserialize_data_mismatch() { 4265 let c_data_json = serde_json::json!({}).to_string(); 4266 let att_obj: [u8; 2704] = [ 4267 cbor::MAP_3, 4268 cbor::TEXT_3, 4269 b'f', 4270 b'm', 4271 b't', 4272 cbor::TEXT_4, 4273 b'n', 4274 b'o', 4275 b'n', 4276 b'e', 4277 cbor::TEXT_7, 4278 b'a', 4279 b't', 4280 b't', 4281 b'S', 4282 b't', 4283 b'm', 4284 b't', 4285 cbor::MAP_0, 4286 cbor::TEXT_8, 4287 b'a', 4288 b'u', 4289 b't', 4290 b'h', 4291 b'D', 4292 b'a', 4293 b't', 4294 b'a', 4295 cbor::BYTES_INFO_25, 4296 10, 4297 113, 4298 // `rpIdHash`. 4299 0, 4300 0, 4301 0, 4302 0, 4303 0, 4304 0, 4305 0, 4306 0, 4307 0, 4308 0, 4309 0, 4310 0, 4311 0, 4312 0, 4313 0, 4314 0, 4315 0, 4316 0, 4317 0, 4318 0, 4319 0, 4320 0, 4321 0, 4322 0, 4323 0, 4324 0, 4325 0, 4326 0, 4327 0, 4328 0, 4329 0, 4330 0, 4331 // `flags`. 4332 0b0100_0101, 4333 // `signCount`. 4334 0, 4335 0, 4336 0, 4337 0, 4338 // `aaguid`. 4339 0, 4340 0, 4341 0, 4342 0, 4343 0, 4344 0, 4345 0, 4346 0, 4347 0, 4348 0, 4349 0, 4350 0, 4351 0, 4352 0, 4353 0, 4354 0, 4355 // `credentialIdLength`. 4356 0, 4357 16, 4358 // `credentialId`. 4359 0, 4360 0, 4361 0, 4362 0, 4363 0, 4364 0, 4365 0, 4366 0, 4367 0, 4368 0, 4369 0, 4370 0, 4371 0, 4372 0, 4373 0, 4374 0, 4375 // ML-DSA-87 COSE key. 4376 cbor::MAP_3, 4377 KTY, 4378 AKP, 4379 ALG, 4380 cbor::NEG_INFO_24, 4381 MLDSA87, 4382 // `pub`. 4383 cbor::NEG_ONE, 4384 cbor::BYTES_INFO_25, 4385 10, 4386 32, 4387 // Encoded key. 4388 1, 4389 1, 4390 1, 4391 1, 4392 1, 4393 1, 4394 1, 4395 1, 4396 1, 4397 1, 4398 1, 4399 1, 4400 1, 4401 1, 4402 1, 4403 1, 4404 1, 4405 1, 4406 1, 4407 1, 4408 1, 4409 1, 4410 1, 4411 1, 4412 1, 4413 1, 4414 1, 4415 1, 4416 1, 4417 1, 4418 1, 4419 1, 4420 1, 4421 1, 4422 1, 4423 1, 4424 1, 4425 1, 4426 1, 4427 1, 4428 1, 4429 1, 4430 1, 4431 1, 4432 1, 4433 1, 4434 1, 4435 1, 4436 1, 4437 1, 4438 1, 4439 1, 4440 1, 4441 1, 4442 1, 4443 1, 4444 1, 4445 1, 4446 1, 4447 1, 4448 1, 4449 1, 4450 1, 4451 1, 4452 1, 4453 1, 4454 1, 4455 1, 4456 1, 4457 1, 4458 1, 4459 1, 4460 1, 4461 1, 4462 1, 4463 1, 4464 1, 4465 1, 4466 1, 4467 1, 4468 1, 4469 1, 4470 1, 4471 1, 4472 1, 4473 1, 4474 1, 4475 1, 4476 1, 4477 1, 4478 1, 4479 1, 4480 1, 4481 1, 4482 1, 4483 1, 4484 1, 4485 1, 4486 1, 4487 1, 4488 1, 4489 1, 4490 1, 4491 1, 4492 1, 4493 1, 4494 1, 4495 1, 4496 1, 4497 1, 4498 1, 4499 1, 4500 1, 4501 1, 4502 1, 4503 1, 4504 1, 4505 1, 4506 1, 4507 1, 4508 1, 4509 1, 4510 1, 4511 1, 4512 1, 4513 1, 4514 1, 4515 1, 4516 1, 4517 1, 4518 1, 4519 1, 4520 1, 4521 1, 4522 1, 4523 1, 4524 1, 4525 1, 4526 1, 4527 1, 4528 1, 4529 1, 4530 1, 4531 1, 4532 1, 4533 1, 4534 1, 4535 1, 4536 1, 4537 1, 4538 1, 4539 1, 4540 1, 4541 1, 4542 1, 4543 1, 4544 1, 4545 1, 4546 1, 4547 1, 4548 1, 4549 1, 4550 1, 4551 1, 4552 1, 4553 1, 4554 1, 4555 1, 4556 1, 4557 1, 4558 1, 4559 1, 4560 1, 4561 1, 4562 1, 4563 1, 4564 1, 4565 1, 4566 1, 4567 1, 4568 1, 4569 1, 4570 1, 4571 1, 4572 1, 4573 1, 4574 1, 4575 1, 4576 1, 4577 1, 4578 1, 4579 1, 4580 1, 4581 1, 4582 1, 4583 1, 4584 1, 4585 1, 4586 1, 4587 1, 4588 1, 4589 1, 4590 1, 4591 1, 4592 1, 4593 1, 4594 1, 4595 1, 4596 1, 4597 1, 4598 1, 4599 1, 4600 1, 4601 1, 4602 1, 4603 1, 4604 1, 4605 1, 4606 1, 4607 1, 4608 1, 4609 1, 4610 1, 4611 1, 4612 1, 4613 1, 4614 1, 4615 1, 4616 1, 4617 1, 4618 1, 4619 1, 4620 1, 4621 1, 4622 1, 4623 1, 4624 1, 4625 1, 4626 1, 4627 1, 4628 1, 4629 1, 4630 1, 4631 1, 4632 1, 4633 1, 4634 1, 4635 1, 4636 1, 4637 1, 4638 1, 4639 1, 4640 1, 4641 1, 4642 1, 4643 1, 4644 1, 4645 1, 4646 1, 4647 1, 4648 1, 4649 1, 4650 1, 4651 1, 4652 1, 4653 1, 4654 1, 4655 1, 4656 1, 4657 1, 4658 1, 4659 1, 4660 1, 4661 1, 4662 1, 4663 1, 4664 1, 4665 1, 4666 1, 4667 1, 4668 1, 4669 1, 4670 1, 4671 1, 4672 1, 4673 1, 4674 1, 4675 1, 4676 1, 4677 1, 4678 1, 4679 1, 4680 1, 4681 1, 4682 1, 4683 1, 4684 1, 4685 1, 4686 1, 4687 1, 4688 1, 4689 1, 4690 1, 4691 1, 4692 1, 4693 1, 4694 1, 4695 1, 4696 1, 4697 1, 4698 1, 4699 1, 4700 1, 4701 1, 4702 1, 4703 1, 4704 1, 4705 1, 4706 1, 4707 1, 4708 1, 4709 1, 4710 1, 4711 1, 4712 1, 4713 1, 4714 1, 4715 1, 4716 1, 4717 1, 4718 1, 4719 1, 4720 1, 4721 1, 4722 1, 4723 1, 4724 1, 4725 1, 4726 1, 4727 1, 4728 1, 4729 1, 4730 1, 4731 1, 4732 1, 4733 1, 4734 1, 4735 1, 4736 1, 4737 1, 4738 1, 4739 1, 4740 1, 4741 1, 4742 1, 4743 1, 4744 1, 4745 1, 4746 1, 4747 1, 4748 1, 4749 1, 4750 1, 4751 1, 4752 1, 4753 1, 4754 1, 4755 1, 4756 1, 4757 1, 4758 1, 4759 1, 4760 1, 4761 1, 4762 1, 4763 1, 4764 1, 4765 1, 4766 1, 4767 1, 4768 1, 4769 1, 4770 1, 4771 1, 4772 1, 4773 1, 4774 1, 4775 1, 4776 1, 4777 1, 4778 1, 4779 1, 4780 1, 4781 1, 4782 1, 4783 1, 4784 1, 4785 1, 4786 1, 4787 1, 4788 1, 4789 1, 4790 1, 4791 1, 4792 1, 4793 1, 4794 1, 4795 1, 4796 1, 4797 1, 4798 1, 4799 1, 4800 1, 4801 1, 4802 1, 4803 1, 4804 1, 4805 1, 4806 1, 4807 1, 4808 1, 4809 1, 4810 1, 4811 1, 4812 1, 4813 1, 4814 1, 4815 1, 4816 1, 4817 1, 4818 1, 4819 1, 4820 1, 4821 1, 4822 1, 4823 1, 4824 1, 4825 1, 4826 1, 4827 1, 4828 1, 4829 1, 4830 1, 4831 1, 4832 1, 4833 1, 4834 1, 4835 1, 4836 1, 4837 1, 4838 1, 4839 1, 4840 1, 4841 1, 4842 1, 4843 1, 4844 1, 4845 1, 4846 1, 4847 1, 4848 1, 4849 1, 4850 1, 4851 1, 4852 1, 4853 1, 4854 1, 4855 1, 4856 1, 4857 1, 4858 1, 4859 1, 4860 1, 4861 1, 4862 1, 4863 1, 4864 1, 4865 1, 4866 1, 4867 1, 4868 1, 4869 1, 4870 1, 4871 1, 4872 1, 4873 1, 4874 1, 4875 1, 4876 1, 4877 1, 4878 1, 4879 1, 4880 1, 4881 1, 4882 1, 4883 1, 4884 1, 4885 1, 4886 1, 4887 1, 4888 1, 4889 1, 4890 1, 4891 1, 4892 1, 4893 1, 4894 1, 4895 1, 4896 1, 4897 1, 4898 1, 4899 1, 4900 1, 4901 1, 4902 1, 4903 1, 4904 1, 4905 1, 4906 1, 4907 1, 4908 1, 4909 1, 4910 1, 4911 1, 4912 1, 4913 1, 4914 1, 4915 1, 4916 1, 4917 1, 4918 1, 4919 1, 4920 1, 4921 1, 4922 1, 4923 1, 4924 1, 4925 1, 4926 1, 4927 1, 4928 1, 4929 1, 4930 1, 4931 1, 4932 1, 4933 1, 4934 1, 4935 1, 4936 1, 4937 1, 4938 1, 4939 1, 4940 1, 4941 1, 4942 1, 4943 1, 4944 1, 4945 1, 4946 1, 4947 1, 4948 1, 4949 1, 4950 1, 4951 1, 4952 1, 4953 1, 4954 1, 4955 1, 4956 1, 4957 1, 4958 1, 4959 1, 4960 1, 4961 1, 4962 1, 4963 1, 4964 1, 4965 1, 4966 1, 4967 1, 4968 1, 4969 1, 4970 1, 4971 1, 4972 1, 4973 1, 4974 1, 4975 1, 4976 1, 4977 1, 4978 1, 4979 1, 4980 1, 4981 1, 4982 1, 4983 1, 4984 1, 4985 1, 4986 1, 4987 1, 4988 1, 4989 1, 4990 1, 4991 1, 4992 1, 4993 1, 4994 1, 4995 1, 4996 1, 4997 1, 4998 1, 4999 1, 5000 1, 5001 1, 5002 1, 5003 1, 5004 1, 5005 1, 5006 1, 5007 1, 5008 1, 5009 1, 5010 1, 5011 1, 5012 1, 5013 1, 5014 1, 5015 1, 5016 1, 5017 1, 5018 1, 5019 1, 5020 1, 5021 1, 5022 1, 5023 1, 5024 1, 5025 1, 5026 1, 5027 1, 5028 1, 5029 1, 5030 1, 5031 1, 5032 1, 5033 1, 5034 1, 5035 1, 5036 1, 5037 1, 5038 1, 5039 1, 5040 1, 5041 1, 5042 1, 5043 1, 5044 1, 5045 1, 5046 1, 5047 1, 5048 1, 5049 1, 5050 1, 5051 1, 5052 1, 5053 1, 5054 1, 5055 1, 5056 1, 5057 1, 5058 1, 5059 1, 5060 1, 5061 1, 5062 1, 5063 1, 5064 1, 5065 1, 5066 1, 5067 1, 5068 1, 5069 1, 5070 1, 5071 1, 5072 1, 5073 1, 5074 1, 5075 1, 5076 1, 5077 1, 5078 1, 5079 1, 5080 1, 5081 1, 5082 1, 5083 1, 5084 1, 5085 1, 5086 1, 5087 1, 5088 1, 5089 1, 5090 1, 5091 1, 5092 1, 5093 1, 5094 1, 5095 1, 5096 1, 5097 1, 5098 1, 5099 1, 5100 1, 5101 1, 5102 1, 5103 1, 5104 1, 5105 1, 5106 1, 5107 1, 5108 1, 5109 1, 5110 1, 5111 1, 5112 1, 5113 1, 5114 1, 5115 1, 5116 1, 5117 1, 5118 1, 5119 1, 5120 1, 5121 1, 5122 1, 5123 1, 5124 1, 5125 1, 5126 1, 5127 1, 5128 1, 5129 1, 5130 1, 5131 1, 5132 1, 5133 1, 5134 1, 5135 1, 5136 1, 5137 1, 5138 1, 5139 1, 5140 1, 5141 1, 5142 1, 5143 1, 5144 1, 5145 1, 5146 1, 5147 1, 5148 1, 5149 1, 5150 1, 5151 1, 5152 1, 5153 1, 5154 1, 5155 1, 5156 1, 5157 1, 5158 1, 5159 1, 5160 1, 5161 1, 5162 1, 5163 1, 5164 1, 5165 1, 5166 1, 5167 1, 5168 1, 5169 1, 5170 1, 5171 1, 5172 1, 5173 1, 5174 1, 5175 1, 5176 1, 5177 1, 5178 1, 5179 1, 5180 1, 5181 1, 5182 1, 5183 1, 5184 1, 5185 1, 5186 1, 5187 1, 5188 1, 5189 1, 5190 1, 5191 1, 5192 1, 5193 1, 5194 1, 5195 1, 5196 1, 5197 1, 5198 1, 5199 1, 5200 1, 5201 1, 5202 1, 5203 1, 5204 1, 5205 1, 5206 1, 5207 1, 5208 1, 5209 1, 5210 1, 5211 1, 5212 1, 5213 1, 5214 1, 5215 1, 5216 1, 5217 1, 5218 1, 5219 1, 5220 1, 5221 1, 5222 1, 5223 1, 5224 1, 5225 1, 5226 1, 5227 1, 5228 1, 5229 1, 5230 1, 5231 1, 5232 1, 5233 1, 5234 1, 5235 1, 5236 1, 5237 1, 5238 1, 5239 1, 5240 1, 5241 1, 5242 1, 5243 1, 5244 1, 5245 1, 5246 1, 5247 1, 5248 1, 5249 1, 5250 1, 5251 1, 5252 1, 5253 1, 5254 1, 5255 1, 5256 1, 5257 1, 5258 1, 5259 1, 5260 1, 5261 1, 5262 1, 5263 1, 5264 1, 5265 1, 5266 1, 5267 1, 5268 1, 5269 1, 5270 1, 5271 1, 5272 1, 5273 1, 5274 1, 5275 1, 5276 1, 5277 1, 5278 1, 5279 1, 5280 1, 5281 1, 5282 1, 5283 1, 5284 1, 5285 1, 5286 1, 5287 1, 5288 1, 5289 1, 5290 1, 5291 1, 5292 1, 5293 1, 5294 1, 5295 1, 5296 1, 5297 1, 5298 1, 5299 1, 5300 1, 5301 1, 5302 1, 5303 1, 5304 1, 5305 1, 5306 1, 5307 1, 5308 1, 5309 1, 5310 1, 5311 1, 5312 1, 5313 1, 5314 1, 5315 1, 5316 1, 5317 1, 5318 1, 5319 1, 5320 1, 5321 1, 5322 1, 5323 1, 5324 1, 5325 1, 5326 1, 5327 1, 5328 1, 5329 1, 5330 1, 5331 1, 5332 1, 5333 1, 5334 1, 5335 1, 5336 1, 5337 1, 5338 1, 5339 1, 5340 1, 5341 1, 5342 1, 5343 1, 5344 1, 5345 1, 5346 1, 5347 1, 5348 1, 5349 1, 5350 1, 5351 1, 5352 1, 5353 1, 5354 1, 5355 1, 5356 1, 5357 1, 5358 1, 5359 1, 5360 1, 5361 1, 5362 1, 5363 1, 5364 1, 5365 1, 5366 1, 5367 1, 5368 1, 5369 1, 5370 1, 5371 1, 5372 1, 5373 1, 5374 1, 5375 1, 5376 1, 5377 1, 5378 1, 5379 1, 5380 1, 5381 1, 5382 1, 5383 1, 5384 1, 5385 1, 5386 1, 5387 1, 5388 1, 5389 1, 5390 1, 5391 1, 5392 1, 5393 1, 5394 1, 5395 1, 5396 1, 5397 1, 5398 1, 5399 1, 5400 1, 5401 1, 5402 1, 5403 1, 5404 1, 5405 1, 5406 1, 5407 1, 5408 1, 5409 1, 5410 1, 5411 1, 5412 1, 5413 1, 5414 1, 5415 1, 5416 1, 5417 1, 5418 1, 5419 1, 5420 1, 5421 1, 5422 1, 5423 1, 5424 1, 5425 1, 5426 1, 5427 1, 5428 1, 5429 1, 5430 1, 5431 1, 5432 1, 5433 1, 5434 1, 5435 1, 5436 1, 5437 1, 5438 1, 5439 1, 5440 1, 5441 1, 5442 1, 5443 1, 5444 1, 5445 1, 5446 1, 5447 1, 5448 1, 5449 1, 5450 1, 5451 1, 5452 1, 5453 1, 5454 1, 5455 1, 5456 1, 5457 1, 5458 1, 5459 1, 5460 1, 5461 1, 5462 1, 5463 1, 5464 1, 5465 1, 5466 1, 5467 1, 5468 1, 5469 1, 5470 1, 5471 1, 5472 1, 5473 1, 5474 1, 5475 1, 5476 1, 5477 1, 5478 1, 5479 1, 5480 1, 5481 1, 5482 1, 5483 1, 5484 1, 5485 1, 5486 1, 5487 1, 5488 1, 5489 1, 5490 1, 5491 1, 5492 1, 5493 1, 5494 1, 5495 1, 5496 1, 5497 1, 5498 1, 5499 1, 5500 1, 5501 1, 5502 1, 5503 1, 5504 1, 5505 1, 5506 1, 5507 1, 5508 1, 5509 1, 5510 1, 5511 1, 5512 1, 5513 1, 5514 1, 5515 1, 5516 1, 5517 1, 5518 1, 5519 1, 5520 1, 5521 1, 5522 1, 5523 1, 5524 1, 5525 1, 5526 1, 5527 1, 5528 1, 5529 1, 5530 1, 5531 1, 5532 1, 5533 1, 5534 1, 5535 1, 5536 1, 5537 1, 5538 1, 5539 1, 5540 1, 5541 1, 5542 1, 5543 1, 5544 1, 5545 1, 5546 1, 5547 1, 5548 1, 5549 1, 5550 1, 5551 1, 5552 1, 5553 1, 5554 1, 5555 1, 5556 1, 5557 1, 5558 1, 5559 1, 5560 1, 5561 1, 5562 1, 5563 1, 5564 1, 5565 1, 5566 1, 5567 1, 5568 1, 5569 1, 5570 1, 5571 1, 5572 1, 5573 1, 5574 1, 5575 1, 5576 1, 5577 1, 5578 1, 5579 1, 5580 1, 5581 1, 5582 1, 5583 1, 5584 1, 5585 1, 5586 1, 5587 1, 5588 1, 5589 1, 5590 1, 5591 1, 5592 1, 5593 1, 5594 1, 5595 1, 5596 1, 5597 1, 5598 1, 5599 1, 5600 1, 5601 1, 5602 1, 5603 1, 5604 1, 5605 1, 5606 1, 5607 1, 5608 1, 5609 1, 5610 1, 5611 1, 5612 1, 5613 1, 5614 1, 5615 1, 5616 1, 5617 1, 5618 1, 5619 1, 5620 1, 5621 1, 5622 1, 5623 1, 5624 1, 5625 1, 5626 1, 5627 1, 5628 1, 5629 1, 5630 1, 5631 1, 5632 1, 5633 1, 5634 1, 5635 1, 5636 1, 5637 1, 5638 1, 5639 1, 5640 1, 5641 1, 5642 1, 5643 1, 5644 1, 5645 1, 5646 1, 5647 1, 5648 1, 5649 1, 5650 1, 5651 1, 5652 1, 5653 1, 5654 1, 5655 1, 5656 1, 5657 1, 5658 1, 5659 1, 5660 1, 5661 1, 5662 1, 5663 1, 5664 1, 5665 1, 5666 1, 5667 1, 5668 1, 5669 1, 5670 1, 5671 1, 5672 1, 5673 1, 5674 1, 5675 1, 5676 1, 5677 1, 5678 1, 5679 1, 5680 1, 5681 1, 5682 1, 5683 1, 5684 1, 5685 1, 5686 1, 5687 1, 5688 1, 5689 1, 5690 1, 5691 1, 5692 1, 5693 1, 5694 1, 5695 1, 5696 1, 5697 1, 5698 1, 5699 1, 5700 1, 5701 1, 5702 1, 5703 1, 5704 1, 5705 1, 5706 1, 5707 1, 5708 1, 5709 1, 5710 1, 5711 1, 5712 1, 5713 1, 5714 1, 5715 1, 5716 1, 5717 1, 5718 1, 5719 1, 5720 1, 5721 1, 5722 1, 5723 1, 5724 1, 5725 1, 5726 1, 5727 1, 5728 1, 5729 1, 5730 1, 5731 1, 5732 1, 5733 1, 5734 1, 5735 1, 5736 1, 5737 1, 5738 1, 5739 1, 5740 1, 5741 1, 5742 1, 5743 1, 5744 1, 5745 1, 5746 1, 5747 1, 5748 1, 5749 1, 5750 1, 5751 1, 5752 1, 5753 1, 5754 1, 5755 1, 5756 1, 5757 1, 5758 1, 5759 1, 5760 1, 5761 1, 5762 1, 5763 1, 5764 1, 5765 1, 5766 1, 5767 1, 5768 1, 5769 1, 5770 1, 5771 1, 5772 1, 5773 1, 5774 1, 5775 1, 5776 1, 5777 1, 5778 1, 5779 1, 5780 1, 5781 1, 5782 1, 5783 1, 5784 1, 5785 1, 5786 1, 5787 1, 5788 1, 5789 1, 5790 1, 5791 1, 5792 1, 5793 1, 5794 1, 5795 1, 5796 1, 5797 1, 5798 1, 5799 1, 5800 1, 5801 1, 5802 1, 5803 1, 5804 1, 5805 1, 5806 1, 5807 1, 5808 1, 5809 1, 5810 1, 5811 1, 5812 1, 5813 1, 5814 1, 5815 1, 5816 1, 5817 1, 5818 1, 5819 1, 5820 1, 5821 1, 5822 1, 5823 1, 5824 1, 5825 1, 5826 1, 5827 1, 5828 1, 5829 1, 5830 1, 5831 1, 5832 1, 5833 1, 5834 1, 5835 1, 5836 1, 5837 1, 5838 1, 5839 1, 5840 1, 5841 1, 5842 1, 5843 1, 5844 1, 5845 1, 5846 1, 5847 1, 5848 1, 5849 1, 5850 1, 5851 1, 5852 1, 5853 1, 5854 1, 5855 1, 5856 1, 5857 1, 5858 1, 5859 1, 5860 1, 5861 1, 5862 1, 5863 1, 5864 1, 5865 1, 5866 1, 5867 1, 5868 1, 5869 1, 5870 1, 5871 1, 5872 1, 5873 1, 5874 1, 5875 1, 5876 1, 5877 1, 5878 1, 5879 1, 5880 1, 5881 1, 5882 1, 5883 1, 5884 1, 5885 1, 5886 1, 5887 1, 5888 1, 5889 1, 5890 1, 5891 1, 5892 1, 5893 1, 5894 1, 5895 1, 5896 1, 5897 1, 5898 1, 5899 1, 5900 1, 5901 1, 5902 1, 5903 1, 5904 1, 5905 1, 5906 1, 5907 1, 5908 1, 5909 1, 5910 1, 5911 1, 5912 1, 5913 1, 5914 1, 5915 1, 5916 1, 5917 1, 5918 1, 5919 1, 5920 1, 5921 1, 5922 1, 5923 1, 5924 1, 5925 1, 5926 1, 5927 1, 5928 1, 5929 1, 5930 1, 5931 1, 5932 1, 5933 1, 5934 1, 5935 1, 5936 1, 5937 1, 5938 1, 5939 1, 5940 1, 5941 1, 5942 1, 5943 1, 5944 1, 5945 1, 5946 1, 5947 1, 5948 1, 5949 1, 5950 1, 5951 1, 5952 1, 5953 1, 5954 1, 5955 1, 5956 1, 5957 1, 5958 1, 5959 1, 5960 1, 5961 1, 5962 1, 5963 1, 5964 1, 5965 1, 5966 1, 5967 1, 5968 1, 5969 1, 5970 1, 5971 1, 5972 1, 5973 1, 5974 1, 5975 1, 5976 1, 5977 1, 5978 1, 5979 1, 5980 1, 5981 1, 5982 1, 5983 1, 5984 1, 5985 1, 5986 1, 5987 1, 5988 1, 5989 1, 5990 1, 5991 1, 5992 1, 5993 1, 5994 1, 5995 1, 5996 1, 5997 1, 5998 1, 5999 1, 6000 1, 6001 1, 6002 1, 6003 1, 6004 1, 6005 1, 6006 1, 6007 1, 6008 1, 6009 1, 6010 1, 6011 1, 6012 1, 6013 1, 6014 1, 6015 1, 6016 1, 6017 1, 6018 1, 6019 1, 6020 1, 6021 1, 6022 1, 6023 1, 6024 1, 6025 1, 6026 1, 6027 1, 6028 1, 6029 1, 6030 1, 6031 1, 6032 1, 6033 1, 6034 1, 6035 1, 6036 1, 6037 1, 6038 1, 6039 1, 6040 1, 6041 1, 6042 1, 6043 1, 6044 1, 6045 1, 6046 1, 6047 1, 6048 1, 6049 1, 6050 1, 6051 1, 6052 1, 6053 1, 6054 1, 6055 1, 6056 1, 6057 1, 6058 1, 6059 1, 6060 1, 6061 1, 6062 1, 6063 1, 6064 1, 6065 1, 6066 1, 6067 1, 6068 1, 6069 1, 6070 1, 6071 1, 6072 1, 6073 1, 6074 1, 6075 1, 6076 1, 6077 1, 6078 1, 6079 1, 6080 1, 6081 1, 6082 1, 6083 1, 6084 1, 6085 1, 6086 1, 6087 1, 6088 1, 6089 1, 6090 1, 6091 1, 6092 1, 6093 1, 6094 1, 6095 1, 6096 1, 6097 1, 6098 1, 6099 1, 6100 1, 6101 1, 6102 1, 6103 1, 6104 1, 6105 1, 6106 1, 6107 1, 6108 1, 6109 1, 6110 1, 6111 1, 6112 1, 6113 1, 6114 1, 6115 1, 6116 1, 6117 1, 6118 1, 6119 1, 6120 1, 6121 1, 6122 1, 6123 1, 6124 1, 6125 1, 6126 1, 6127 1, 6128 1, 6129 1, 6130 1, 6131 1, 6132 1, 6133 1, 6134 1, 6135 1, 6136 1, 6137 1, 6138 1, 6139 1, 6140 1, 6141 1, 6142 1, 6143 1, 6144 1, 6145 1, 6146 1, 6147 1, 6148 1, 6149 1, 6150 1, 6151 1, 6152 1, 6153 1, 6154 1, 6155 1, 6156 1, 6157 1, 6158 1, 6159 1, 6160 1, 6161 1, 6162 1, 6163 1, 6164 1, 6165 1, 6166 1, 6167 1, 6168 1, 6169 1, 6170 1, 6171 1, 6172 1, 6173 1, 6174 1, 6175 1, 6176 1, 6177 1, 6178 1, 6179 1, 6180 1, 6181 1, 6182 1, 6183 1, 6184 1, 6185 1, 6186 1, 6187 1, 6188 1, 6189 1, 6190 1, 6191 1, 6192 1, 6193 1, 6194 1, 6195 1, 6196 1, 6197 1, 6198 1, 6199 1, 6200 1, 6201 1, 6202 1, 6203 1, 6204 1, 6205 1, 6206 1, 6207 1, 6208 1, 6209 1, 6210 1, 6211 1, 6212 1, 6213 1, 6214 1, 6215 1, 6216 1, 6217 1, 6218 1, 6219 1, 6220 1, 6221 1, 6222 1, 6223 1, 6224 1, 6225 1, 6226 1, 6227 1, 6228 1, 6229 1, 6230 1, 6231 1, 6232 1, 6233 1, 6234 1, 6235 1, 6236 1, 6237 1, 6238 1, 6239 1, 6240 1, 6241 1, 6242 1, 6243 1, 6244 1, 6245 1, 6246 1, 6247 1, 6248 1, 6249 1, 6250 1, 6251 1, 6252 1, 6253 1, 6254 1, 6255 1, 6256 1, 6257 1, 6258 1, 6259 1, 6260 1, 6261 1, 6262 1, 6263 1, 6264 1, 6265 1, 6266 1, 6267 1, 6268 1, 6269 1, 6270 1, 6271 1, 6272 1, 6273 1, 6274 1, 6275 1, 6276 1, 6277 1, 6278 1, 6279 1, 6280 1, 6281 1, 6282 1, 6283 1, 6284 1, 6285 1, 6286 1, 6287 1, 6288 1, 6289 1, 6290 1, 6291 1, 6292 1, 6293 1, 6294 1, 6295 1, 6296 1, 6297 1, 6298 1, 6299 1, 6300 1, 6301 1, 6302 1, 6303 1, 6304 1, 6305 1, 6306 1, 6307 1, 6308 1, 6309 1, 6310 1, 6311 1, 6312 1, 6313 1, 6314 1, 6315 1, 6316 1, 6317 1, 6318 1, 6319 1, 6320 1, 6321 1, 6322 1, 6323 1, 6324 1, 6325 1, 6326 1, 6327 1, 6328 1, 6329 1, 6330 1, 6331 1, 6332 1, 6333 1, 6334 1, 6335 1, 6336 1, 6337 1, 6338 1, 6339 1, 6340 1, 6341 1, 6342 1, 6343 1, 6344 1, 6345 1, 6346 1, 6347 1, 6348 1, 6349 1, 6350 1, 6351 1, 6352 1, 6353 1, 6354 1, 6355 1, 6356 1, 6357 1, 6358 1, 6359 1, 6360 1, 6361 1, 6362 1, 6363 1, 6364 1, 6365 1, 6366 1, 6367 1, 6368 1, 6369 1, 6370 1, 6371 1, 6372 1, 6373 1, 6374 1, 6375 1, 6376 1, 6377 1, 6378 1, 6379 1, 6380 1, 6381 1, 6382 1, 6383 1, 6384 1, 6385 1, 6386 1, 6387 1, 6388 1, 6389 1, 6390 1, 6391 1, 6392 1, 6393 1, 6394 1, 6395 1, 6396 1, 6397 1, 6398 1, 6399 1, 6400 1, 6401 1, 6402 1, 6403 1, 6404 1, 6405 1, 6406 1, 6407 1, 6408 1, 6409 1, 6410 1, 6411 1, 6412 1, 6413 1, 6414 1, 6415 1, 6416 1, 6417 1, 6418 1, 6419 1, 6420 1, 6421 1, 6422 1, 6423 1, 6424 1, 6425 1, 6426 1, 6427 1, 6428 1, 6429 1, 6430 1, 6431 1, 6432 1, 6433 1, 6434 1, 6435 1, 6436 1, 6437 1, 6438 1, 6439 1, 6440 1, 6441 1, 6442 1, 6443 1, 6444 1, 6445 1, 6446 1, 6447 1, 6448 1, 6449 1, 6450 1, 6451 1, 6452 1, 6453 1, 6454 1, 6455 1, 6456 1, 6457 1, 6458 1, 6459 1, 6460 1, 6461 1, 6462 1, 6463 1, 6464 1, 6465 1, 6466 1, 6467 1, 6468 1, 6469 1, 6470 1, 6471 1, 6472 1, 6473 1, 6474 1, 6475 1, 6476 1, 6477 1, 6478 1, 6479 1, 6480 1, 6481 1, 6482 1, 6483 1, 6484 1, 6485 1, 6486 1, 6487 1, 6488 1, 6489 1, 6490 1, 6491 1, 6492 1, 6493 1, 6494 1, 6495 1, 6496 1, 6497 1, 6498 1, 6499 1, 6500 1, 6501 1, 6502 1, 6503 1, 6504 1, 6505 1, 6506 1, 6507 1, 6508 1, 6509 1, 6510 1, 6511 1, 6512 1, 6513 1, 6514 1, 6515 1, 6516 1, 6517 1, 6518 1, 6519 1, 6520 1, 6521 1, 6522 1, 6523 1, 6524 1, 6525 1, 6526 1, 6527 1, 6528 1, 6529 1, 6530 1, 6531 1, 6532 1, 6533 1, 6534 1, 6535 1, 6536 1, 6537 1, 6538 1, 6539 1, 6540 1, 6541 1, 6542 1, 6543 1, 6544 1, 6545 1, 6546 1, 6547 1, 6548 1, 6549 1, 6550 1, 6551 1, 6552 1, 6553 1, 6554 1, 6555 1, 6556 1, 6557 1, 6558 1, 6559 1, 6560 1, 6561 1, 6562 1, 6563 1, 6564 1, 6565 1, 6566 1, 6567 1, 6568 1, 6569 1, 6570 1, 6571 1, 6572 1, 6573 1, 6574 1, 6575 1, 6576 1, 6577 1, 6578 1, 6579 1, 6580 1, 6581 1, 6582 1, 6583 1, 6584 1, 6585 1, 6586 1, 6587 1, 6588 1, 6589 1, 6590 1, 6591 1, 6592 1, 6593 1, 6594 1, 6595 1, 6596 1, 6597 1, 6598 1, 6599 1, 6600 1, 6601 1, 6602 1, 6603 1, 6604 1, 6605 1, 6606 1, 6607 1, 6608 1, 6609 1, 6610 1, 6611 1, 6612 1, 6613 1, 6614 1, 6615 1, 6616 1, 6617 1, 6618 1, 6619 1, 6620 1, 6621 1, 6622 1, 6623 1, 6624 1, 6625 1, 6626 1, 6627 1, 6628 1, 6629 1, 6630 1, 6631 1, 6632 1, 6633 1, 6634 1, 6635 1, 6636 1, 6637 1, 6638 1, 6639 1, 6640 1, 6641 1, 6642 1, 6643 1, 6644 1, 6645 1, 6646 1, 6647 1, 6648 1, 6649 1, 6650 1, 6651 1, 6652 1, 6653 1, 6654 1, 6655 1, 6656 1, 6657 1, 6658 1, 6659 1, 6660 1, 6661 1, 6662 1, 6663 1, 6664 1, 6665 1, 6666 1, 6667 1, 6668 1, 6669 1, 6670 1, 6671 1, 6672 1, 6673 1, 6674 1, 6675 1, 6676 1, 6677 1, 6678 1, 6679 1, 6680 1, 6681 1, 6682 1, 6683 1, 6684 1, 6685 1, 6686 1, 6687 1, 6688 1, 6689 1, 6690 1, 6691 1, 6692 1, 6693 1, 6694 1, 6695 1, 6696 1, 6697 1, 6698 1, 6699 1, 6700 1, 6701 1, 6702 1, 6703 1, 6704 1, 6705 1, 6706 1, 6707 1, 6708 1, 6709 1, 6710 1, 6711 1, 6712 1, 6713 1, 6714 1, 6715 1, 6716 1, 6717 1, 6718 1, 6719 1, 6720 1, 6721 1, 6722 1, 6723 1, 6724 1, 6725 1, 6726 1, 6727 1, 6728 1, 6729 1, 6730 1, 6731 1, 6732 1, 6733 1, 6734 1, 6735 1, 6736 1, 6737 1, 6738 1, 6739 1, 6740 1, 6741 1, 6742 1, 6743 1, 6744 1, 6745 1, 6746 1, 6747 1, 6748 1, 6749 1, 6750 1, 6751 1, 6752 1, 6753 1, 6754 1, 6755 1, 6756 1, 6757 1, 6758 1, 6759 1, 6760 1, 6761 1, 6762 1, 6763 1, 6764 1, 6765 1, 6766 1, 6767 1, 6768 1, 6769 1, 6770 1, 6771 1, 6772 1, 6773 1, 6774 1, 6775 1, 6776 1, 6777 1, 6778 1, 6779 1, 6780 1, 6781 1, 6782 1, 6783 1, 6784 1, 6785 1, 6786 1, 6787 1, 6788 1, 6789 1, 6790 1, 6791 1, 6792 1, 6793 1, 6794 1, 6795 1, 6796 1, 6797 1, 6798 1, 6799 1, 6800 1, 6801 1, 6802 1, 6803 1, 6804 1, 6805 1, 6806 1, 6807 1, 6808 1, 6809 1, 6810 1, 6811 1, 6812 1, 6813 1, 6814 1, 6815 1, 6816 1, 6817 1, 6818 1, 6819 1, 6820 1, 6821 1, 6822 1, 6823 1, 6824 1, 6825 1, 6826 1, 6827 1, 6828 1, 6829 1, 6830 1, 6831 1, 6832 1, 6833 1, 6834 1, 6835 1, 6836 1, 6837 1, 6838 1, 6839 1, 6840 1, 6841 1, 6842 1, 6843 1, 6844 1, 6845 1, 6846 1, 6847 1, 6848 1, 6849 1, 6850 1, 6851 1, 6852 1, 6853 1, 6854 1, 6855 1, 6856 1, 6857 1, 6858 1, 6859 1, 6860 1, 6861 1, 6862 1, 6863 1, 6864 1, 6865 1, 6866 1, 6867 1, 6868 1, 6869 1, 6870 1, 6871 1, 6872 1, 6873 1, 6874 1, 6875 1, 6876 1, 6877 1, 6878 1, 6879 1, 6880 1, 6881 1, 6882 1, 6883 1, 6884 1, 6885 1, 6886 1, 6887 1, 6888 1, 6889 1, 6890 1, 6891 1, 6892 1, 6893 1, 6894 1, 6895 1, 6896 1, 6897 1, 6898 1, 6899 1, 6900 1, 6901 1, 6902 1, 6903 1, 6904 1, 6905 1, 6906 1, 6907 1, 6908 1, 6909 1, 6910 1, 6911 1, 6912 1, 6913 1, 6914 1, 6915 1, 6916 1, 6917 1, 6918 1, 6919 1, 6920 1, 6921 1, 6922 1, 6923 1, 6924 1, 6925 1, 6926 1, 6927 1, 6928 1, 6929 1, 6930 1, 6931 1, 6932 1, 6933 1, 6934 1, 6935 1, 6936 1, 6937 1, 6938 1, 6939 1, 6940 1, 6941 1, 6942 1, 6943 1, 6944 1, 6945 1, 6946 1, 6947 1, 6948 1, 6949 1, 6950 1, 6951 1, 6952 1, 6953 1, 6954 1, 6955 1, 6956 1, 6957 1, 6958 1, 6959 1, 6960 1, 6961 1, 6962 1, 6963 1, 6964 1, 6965 1, 6966 1, 6967 1, 6968 1, 6969 1, 6970 1, 6971 1, 6972 1, 6973 1, 6974 1, 6975 1, 6976 1, 6977 1, 6978 1, 6979 1, 6980 ]; 6981 let pub_key = MlDsaVerKey::<MlDsa87>::decode(&[1u8; 2592].into()) 6982 .to_public_key_der() 6983 .unwrap(); 6984 let att_obj_len = att_obj.len(); 6985 let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes()); 6986 let b64_adata = base64url_nopad::encode(&att_obj[att_obj_len - 2673..]); 6987 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 6988 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 6989 // Base case is valid. 6990 assert!( 6991 serde_json::from_str::<Registration>( 6992 serde_json::json!({ 6993 "id": "AAAAAAAAAAAAAAAAAAAAAA", 6994 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 6995 "response": { 6996 "clientDataJSON": b64_cdata_json, 6997 "authenticatorData": b64_adata, 6998 "transports": [], 6999 "publicKey": b64_key, 7000 "publicKeyAlgorithm": -50i8, 7001 "attestationObject": b64_aobj, 7002 }, 7003 "clientExtensionResults": {}, 7004 "type": "public-key" 7005 }) 7006 .to_string() 7007 .as_str() 7008 ) 7009 .is_ok_and( 7010 |reg| reg.response.client_data_json == c_data_json.as_bytes() 7011 && reg.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 7012 && reg.response.attestation_object_and_c_data_hash[att_obj_len..] 7013 == *Sha256::digest(c_data_json.as_bytes()) 7014 && reg.response.transports.is_empty() 7015 && matches!(reg.authenticator_attachment, AuthenticatorAttachment::None) 7016 && reg.client_extension_results.cred_props.is_none() 7017 && reg.client_extension_results.prf.is_none() 7018 ) 7019 ); 7020 // `publicKeyAlgorithm` mismatch. 7021 let mut err = Error::invalid_value( 7022 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()), 7023 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa87).as_str() 7024 ) 7025 .to_string().into_bytes(); 7026 assert_eq!( 7027 serde_json::from_str::<Registration>( 7028 serde_json::json!({ 7029 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7030 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7031 "response": { 7032 "clientDataJSON": b64_cdata_json, 7033 "authenticatorData": b64_adata, 7034 "transports": [], 7035 "publicKey": b64_key, 7036 "publicKeyAlgorithm": -8i8, 7037 "attestationObject": b64_aobj, 7038 }, 7039 "clientExtensionResults": {}, 7040 "type": "public-key" 7041 }) 7042 .to_string() 7043 .as_str() 7044 ) 7045 .unwrap_err() 7046 .to_string() 7047 .into_bytes() 7048 .get(..err.len()), 7049 Some(err.as_slice()) 7050 ); 7051 // Missing `publicKeyAlgorithm`. 7052 err = Error::missing_field("publicKeyAlgorithm") 7053 .to_string() 7054 .into_bytes(); 7055 assert_eq!( 7056 serde_json::from_str::<Registration>( 7057 serde_json::json!({ 7058 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7059 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7060 "response": { 7061 "clientDataJSON": b64_cdata_json, 7062 "authenticatorData": b64_adata, 7063 "transports": [], 7064 "publicKey": b64_key, 7065 "attestationObject": b64_aobj, 7066 }, 7067 "clientExtensionResults": {}, 7068 "type": "public-key" 7069 }) 7070 .to_string() 7071 .as_str() 7072 ) 7073 .unwrap_err() 7074 .to_string() 7075 .into_bytes() 7076 .get(..err.len()), 7077 Some(err.as_slice()) 7078 ); 7079 // `null` `publicKeyAlgorithm`. 7080 err = Error::invalid_type(Unexpected::Other("null"), &"publicKeyAlgorithm") 7081 .to_string() 7082 .into_bytes(); 7083 assert_eq!( 7084 serde_json::from_str::<Registration>( 7085 serde_json::json!({ 7086 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7087 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7088 "response": { 7089 "clientDataJSON": b64_cdata_json, 7090 "authenticatorData": b64_adata, 7091 "transports": [], 7092 "publicKey": b64_key, 7093 "publicKeyAlgorithm": null, 7094 "attestationObject": b64_aobj, 7095 }, 7096 "clientExtensionResults": {}, 7097 "type": "public-key" 7098 }) 7099 .to_string() 7100 .as_str() 7101 ) 7102 .unwrap_err() 7103 .to_string() 7104 .into_bytes() 7105 .get(..err.len()), 7106 Some(err.as_slice()) 7107 ); 7108 // `publicKey` mismatch. 7109 let bad_pub_key = MlDsaVerKey::<MlDsa87>::decode(&[2; 2592].into()); 7110 err = Error::invalid_value( 7111 Unexpected::Bytes([0; 32].as_slice()), 7112 &format!( 7113 "DER-encoded public key to match the public key within the attestation object: MlDsa87(MlDsa87PubKey({:?}))", 7114 &[1u8; 2592] 7115 ) 7116 .as_str(), 7117 ) 7118 .to_string().into_bytes(); 7119 assert_eq!(serde_json::from_str::<Registration>( 7120 serde_json::json!({ 7121 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7122 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7123 "response": { 7124 "clientDataJSON": b64_cdata_json, 7125 "authenticatorData": b64_adata, 7126 "transports": [], 7127 "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()), 7128 "publicKeyAlgorithm": -50i8, 7129 "attestationObject": b64_aobj, 7130 }, 7131 "clientExtensionResults": {}, 7132 "type": "public-key" 7133 }) 7134 .to_string() 7135 .as_str() 7136 ) 7137 .unwrap_err().to_string().into_bytes().get(..err.len()), 7138 Some(err.as_slice()) 7139 ); 7140 // Missing `publicKey` is allowed when not using EdDSA, ES256, or RS256. 7141 assert!( 7142 serde_json::from_str::<Registration>( 7143 serde_json::json!({ 7144 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7145 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7146 "response": { 7147 "clientDataJSON": b64_cdata_json, 7148 "authenticatorData": b64_adata, 7149 "transports": [], 7150 "publicKeyAlgorithm": -50i8, 7151 "attestationObject": b64_aobj, 7152 }, 7153 "clientExtensionResults": {}, 7154 "type": "public-key" 7155 }) 7156 .to_string() 7157 .as_str() 7158 ) 7159 .is_ok() 7160 ); 7161 // `publicKeyAlgorithm` mismatch when `publicKey` does not exist. 7162 err = Error::invalid_value( 7163 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 7164 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa87).as_str() 7165 ) 7166 .to_string().into_bytes(); 7167 assert_eq!( 7168 serde_json::from_str::<Registration>( 7169 serde_json::json!({ 7170 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7171 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7172 "response": { 7173 "clientDataJSON": b64_cdata_json, 7174 "authenticatorData": b64_adata, 7175 "transports": [], 7176 "publicKeyAlgorithm": -7i8, 7177 "attestationObject": b64_aobj, 7178 }, 7179 "clientExtensionResults": {}, 7180 "type": "public-key" 7181 }) 7182 .to_string() 7183 .as_str() 7184 ) 7185 .unwrap_err() 7186 .to_string() 7187 .into_bytes() 7188 .get(..err.len()), 7189 Some(err.as_slice()) 7190 ); 7191 // `null` `publicKey` is allowed when not using EdDSA, ES256, or RS256. 7192 assert!( 7193 serde_json::from_str::<Registration>( 7194 serde_json::json!({ 7195 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7196 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7197 "response": { 7198 "clientDataJSON": b64_cdata_json, 7199 "authenticatorData": b64_adata, 7200 "transports": [], 7201 "publicKey": null, 7202 "publicKeyAlgorithm": -50i8, 7203 "attestationObject": b64_aobj, 7204 }, 7205 "clientExtensionResults": {}, 7206 "type": "public-key" 7207 }) 7208 .to_string() 7209 .as_str() 7210 ) 7211 .is_ok() 7212 ); 7213 // `publicKeyAlgorithm` mismatch when `publicKey` is null. 7214 err = Error::invalid_value( 7215 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 7216 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa87).as_str() 7217 ) 7218 .to_string().into_bytes(); 7219 assert_eq!( 7220 serde_json::from_str::<Registration>( 7221 serde_json::json!({ 7222 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7223 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7224 "response": { 7225 "clientDataJSON": b64_cdata_json, 7226 "authenticatorData": b64_adata, 7227 "transports": [], 7228 "publicKey": null, 7229 "publicKeyAlgorithm": -7i8, 7230 "attestationObject": b64_aobj, 7231 }, 7232 "clientExtensionResults": {}, 7233 "type": "public-key" 7234 }) 7235 .to_string() 7236 .as_str() 7237 ) 7238 .unwrap_err() 7239 .to_string() 7240 .into_bytes() 7241 .get(..err.len()), 7242 Some(err.as_slice()) 7243 ); 7244 } 7245 #[expect( 7246 clippy::assertions_on_result_states, 7247 clippy::unwrap_used, 7248 reason = "OK in tests" 7249 )] 7250 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 7251 #[expect(clippy::too_many_lines, reason = "a lot to test")] 7252 #[test] 7253 fn mldsa65_registration_deserialize_data_mismatch() { 7254 let c_data_json = serde_json::json!({}).to_string(); 7255 let att_obj: [u8; 2064] = [ 7256 cbor::MAP_3, 7257 cbor::TEXT_3, 7258 b'f', 7259 b'm', 7260 b't', 7261 cbor::TEXT_4, 7262 b'n', 7263 b'o', 7264 b'n', 7265 b'e', 7266 cbor::TEXT_7, 7267 b'a', 7268 b't', 7269 b't', 7270 b'S', 7271 b't', 7272 b'm', 7273 b't', 7274 cbor::MAP_0, 7275 cbor::TEXT_8, 7276 b'a', 7277 b'u', 7278 b't', 7279 b'h', 7280 b'D', 7281 b'a', 7282 b't', 7283 b'a', 7284 cbor::BYTES_INFO_25, 7285 7, 7286 241, 7287 // `rpIdHash`. 7288 0, 7289 0, 7290 0, 7291 0, 7292 0, 7293 0, 7294 0, 7295 0, 7296 0, 7297 0, 7298 0, 7299 0, 7300 0, 7301 0, 7302 0, 7303 0, 7304 0, 7305 0, 7306 0, 7307 0, 7308 0, 7309 0, 7310 0, 7311 0, 7312 0, 7313 0, 7314 0, 7315 0, 7316 0, 7317 0, 7318 0, 7319 0, 7320 // `flags`. 7321 0b0100_0101, 7322 // `signCount`. 7323 0, 7324 0, 7325 0, 7326 0, 7327 // `aaguid`. 7328 0, 7329 0, 7330 0, 7331 0, 7332 0, 7333 0, 7334 0, 7335 0, 7336 0, 7337 0, 7338 0, 7339 0, 7340 0, 7341 0, 7342 0, 7343 0, 7344 // `credentialIdLength`. 7345 0, 7346 16, 7347 // `credentialId`. 7348 0, 7349 0, 7350 0, 7351 0, 7352 0, 7353 0, 7354 0, 7355 0, 7356 0, 7357 0, 7358 0, 7359 0, 7360 0, 7361 0, 7362 0, 7363 0, 7364 // ML-DSA-65 COSE key. 7365 cbor::MAP_3, 7366 KTY, 7367 AKP, 7368 ALG, 7369 cbor::NEG_INFO_24, 7370 MLDSA65, 7371 // `pub`. 7372 cbor::NEG_ONE, 7373 cbor::BYTES_INFO_25, 7374 7, 7375 160, 7376 // Encoded key. 7377 1, 7378 1, 7379 1, 7380 1, 7381 1, 7382 1, 7383 1, 7384 1, 7385 1, 7386 1, 7387 1, 7388 1, 7389 1, 7390 1, 7391 1, 7392 1, 7393 1, 7394 1, 7395 1, 7396 1, 7397 1, 7398 1, 7399 1, 7400 1, 7401 1, 7402 1, 7403 1, 7404 1, 7405 1, 7406 1, 7407 1, 7408 1, 7409 1, 7410 1, 7411 1, 7412 1, 7413 1, 7414 1, 7415 1, 7416 1, 7417 1, 7418 1, 7419 1, 7420 1, 7421 1, 7422 1, 7423 1, 7424 1, 7425 1, 7426 1, 7427 1, 7428 1, 7429 1, 7430 1, 7431 1, 7432 1, 7433 1, 7434 1, 7435 1, 7436 1, 7437 1, 7438 1, 7439 1, 7440 1, 7441 1, 7442 1, 7443 1, 7444 1, 7445 1, 7446 1, 7447 1, 7448 1, 7449 1, 7450 1, 7451 1, 7452 1, 7453 1, 7454 1, 7455 1, 7456 1, 7457 1, 7458 1, 7459 1, 7460 1, 7461 1, 7462 1, 7463 1, 7464 1, 7465 1, 7466 1, 7467 1, 7468 1, 7469 1, 7470 1, 7471 1, 7472 1, 7473 1, 7474 1, 7475 1, 7476 1, 7477 1, 7478 1, 7479 1, 7480 1, 7481 1, 7482 1, 7483 1, 7484 1, 7485 1, 7486 1, 7487 1, 7488 1, 7489 1, 7490 1, 7491 1, 7492 1, 7493 1, 7494 1, 7495 1, 7496 1, 7497 1, 7498 1, 7499 1, 7500 1, 7501 1, 7502 1, 7503 1, 7504 1, 7505 1, 7506 1, 7507 1, 7508 1, 7509 1, 7510 1, 7511 1, 7512 1, 7513 1, 7514 1, 7515 1, 7516 1, 7517 1, 7518 1, 7519 1, 7520 1, 7521 1, 7522 1, 7523 1, 7524 1, 7525 1, 7526 1, 7527 1, 7528 1, 7529 1, 7530 1, 7531 1, 7532 1, 7533 1, 7534 1, 7535 1, 7536 1, 7537 1, 7538 1, 7539 1, 7540 1, 7541 1, 7542 1, 7543 1, 7544 1, 7545 1, 7546 1, 7547 1, 7548 1, 7549 1, 7550 1, 7551 1, 7552 1, 7553 1, 7554 1, 7555 1, 7556 1, 7557 1, 7558 1, 7559 1, 7560 1, 7561 1, 7562 1, 7563 1, 7564 1, 7565 1, 7566 1, 7567 1, 7568 1, 7569 1, 7570 1, 7571 1, 7572 1, 7573 1, 7574 1, 7575 1, 7576 1, 7577 1, 7578 1, 7579 1, 7580 1, 7581 1, 7582 1, 7583 1, 7584 1, 7585 1, 7586 1, 7587 1, 7588 1, 7589 1, 7590 1, 7591 1, 7592 1, 7593 1, 7594 1, 7595 1, 7596 1, 7597 1, 7598 1, 7599 1, 7600 1, 7601 1, 7602 1, 7603 1, 7604 1, 7605 1, 7606 1, 7607 1, 7608 1, 7609 1, 7610 1, 7611 1, 7612 1, 7613 1, 7614 1, 7615 1, 7616 1, 7617 1, 7618 1, 7619 1, 7620 1, 7621 1, 7622 1, 7623 1, 7624 1, 7625 1, 7626 1, 7627 1, 7628 1, 7629 1, 7630 1, 7631 1, 7632 1, 7633 1, 7634 1, 7635 1, 7636 1, 7637 1, 7638 1, 7639 1, 7640 1, 7641 1, 7642 1, 7643 1, 7644 1, 7645 1, 7646 1, 7647 1, 7648 1, 7649 1, 7650 1, 7651 1, 7652 1, 7653 1, 7654 1, 7655 1, 7656 1, 7657 1, 7658 1, 7659 1, 7660 1, 7661 1, 7662 1, 7663 1, 7664 1, 7665 1, 7666 1, 7667 1, 7668 1, 7669 1, 7670 1, 7671 1, 7672 1, 7673 1, 7674 1, 7675 1, 7676 1, 7677 1, 7678 1, 7679 1, 7680 1, 7681 1, 7682 1, 7683 1, 7684 1, 7685 1, 7686 1, 7687 1, 7688 1, 7689 1, 7690 1, 7691 1, 7692 1, 7693 1, 7694 1, 7695 1, 7696 1, 7697 1, 7698 1, 7699 1, 7700 1, 7701 1, 7702 1, 7703 1, 7704 1, 7705 1, 7706 1, 7707 1, 7708 1, 7709 1, 7710 1, 7711 1, 7712 1, 7713 1, 7714 1, 7715 1, 7716 1, 7717 1, 7718 1, 7719 1, 7720 1, 7721 1, 7722 1, 7723 1, 7724 1, 7725 1, 7726 1, 7727 1, 7728 1, 7729 1, 7730 1, 7731 1, 7732 1, 7733 1, 7734 1, 7735 1, 7736 1, 7737 1, 7738 1, 7739 1, 7740 1, 7741 1, 7742 1, 7743 1, 7744 1, 7745 1, 7746 1, 7747 1, 7748 1, 7749 1, 7750 1, 7751 1, 7752 1, 7753 1, 7754 1, 7755 1, 7756 1, 7757 1, 7758 1, 7759 1, 7760 1, 7761 1, 7762 1, 7763 1, 7764 1, 7765 1, 7766 1, 7767 1, 7768 1, 7769 1, 7770 1, 7771 1, 7772 1, 7773 1, 7774 1, 7775 1, 7776 1, 7777 1, 7778 1, 7779 1, 7780 1, 7781 1, 7782 1, 7783 1, 7784 1, 7785 1, 7786 1, 7787 1, 7788 1, 7789 1, 7790 1, 7791 1, 7792 1, 7793 1, 7794 1, 7795 1, 7796 1, 7797 1, 7798 1, 7799 1, 7800 1, 7801 1, 7802 1, 7803 1, 7804 1, 7805 1, 7806 1, 7807 1, 7808 1, 7809 1, 7810 1, 7811 1, 7812 1, 7813 1, 7814 1, 7815 1, 7816 1, 7817 1, 7818 1, 7819 1, 7820 1, 7821 1, 7822 1, 7823 1, 7824 1, 7825 1, 7826 1, 7827 1, 7828 1, 7829 1, 7830 1, 7831 1, 7832 1, 7833 1, 7834 1, 7835 1, 7836 1, 7837 1, 7838 1, 7839 1, 7840 1, 7841 1, 7842 1, 7843 1, 7844 1, 7845 1, 7846 1, 7847 1, 7848 1, 7849 1, 7850 1, 7851 1, 7852 1, 7853 1, 7854 1, 7855 1, 7856 1, 7857 1, 7858 1, 7859 1, 7860 1, 7861 1, 7862 1, 7863 1, 7864 1, 7865 1, 7866 1, 7867 1, 7868 1, 7869 1, 7870 1, 7871 1, 7872 1, 7873 1, 7874 1, 7875 1, 7876 1, 7877 1, 7878 1, 7879 1, 7880 1, 7881 1, 7882 1, 7883 1, 7884 1, 7885 1, 7886 1, 7887 1, 7888 1, 7889 1, 7890 1, 7891 1, 7892 1, 7893 1, 7894 1, 7895 1, 7896 1, 7897 1, 7898 1, 7899 1, 7900 1, 7901 1, 7902 1, 7903 1, 7904 1, 7905 1, 7906 1, 7907 1, 7908 1, 7909 1, 7910 1, 7911 1, 7912 1, 7913 1, 7914 1, 7915 1, 7916 1, 7917 1, 7918 1, 7919 1, 7920 1, 7921 1, 7922 1, 7923 1, 7924 1, 7925 1, 7926 1, 7927 1, 7928 1, 7929 1, 7930 1, 7931 1, 7932 1, 7933 1, 7934 1, 7935 1, 7936 1, 7937 1, 7938 1, 7939 1, 7940 1, 7941 1, 7942 1, 7943 1, 7944 1, 7945 1, 7946 1, 7947 1, 7948 1, 7949 1, 7950 1, 7951 1, 7952 1, 7953 1, 7954 1, 7955 1, 7956 1, 7957 1, 7958 1, 7959 1, 7960 1, 7961 1, 7962 1, 7963 1, 7964 1, 7965 1, 7966 1, 7967 1, 7968 1, 7969 1, 7970 1, 7971 1, 7972 1, 7973 1, 7974 1, 7975 1, 7976 1, 7977 1, 7978 1, 7979 1, 7980 1, 7981 1, 7982 1, 7983 1, 7984 1, 7985 1, 7986 1, 7987 1, 7988 1, 7989 1, 7990 1, 7991 1, 7992 1, 7993 1, 7994 1, 7995 1, 7996 1, 7997 1, 7998 1, 7999 1, 8000 1, 8001 1, 8002 1, 8003 1, 8004 1, 8005 1, 8006 1, 8007 1, 8008 1, 8009 1, 8010 1, 8011 1, 8012 1, 8013 1, 8014 1, 8015 1, 8016 1, 8017 1, 8018 1, 8019 1, 8020 1, 8021 1, 8022 1, 8023 1, 8024 1, 8025 1, 8026 1, 8027 1, 8028 1, 8029 1, 8030 1, 8031 1, 8032 1, 8033 1, 8034 1, 8035 1, 8036 1, 8037 1, 8038 1, 8039 1, 8040 1, 8041 1, 8042 1, 8043 1, 8044 1, 8045 1, 8046 1, 8047 1, 8048 1, 8049 1, 8050 1, 8051 1, 8052 1, 8053 1, 8054 1, 8055 1, 8056 1, 8057 1, 8058 1, 8059 1, 8060 1, 8061 1, 8062 1, 8063 1, 8064 1, 8065 1, 8066 1, 8067 1, 8068 1, 8069 1, 8070 1, 8071 1, 8072 1, 8073 1, 8074 1, 8075 1, 8076 1, 8077 1, 8078 1, 8079 1, 8080 1, 8081 1, 8082 1, 8083 1, 8084 1, 8085 1, 8086 1, 8087 1, 8088 1, 8089 1, 8090 1, 8091 1, 8092 1, 8093 1, 8094 1, 8095 1, 8096 1, 8097 1, 8098 1, 8099 1, 8100 1, 8101 1, 8102 1, 8103 1, 8104 1, 8105 1, 8106 1, 8107 1, 8108 1, 8109 1, 8110 1, 8111 1, 8112 1, 8113 1, 8114 1, 8115 1, 8116 1, 8117 1, 8118 1, 8119 1, 8120 1, 8121 1, 8122 1, 8123 1, 8124 1, 8125 1, 8126 1, 8127 1, 8128 1, 8129 1, 8130 1, 8131 1, 8132 1, 8133 1, 8134 1, 8135 1, 8136 1, 8137 1, 8138 1, 8139 1, 8140 1, 8141 1, 8142 1, 8143 1, 8144 1, 8145 1, 8146 1, 8147 1, 8148 1, 8149 1, 8150 1, 8151 1, 8152 1, 8153 1, 8154 1, 8155 1, 8156 1, 8157 1, 8158 1, 8159 1, 8160 1, 8161 1, 8162 1, 8163 1, 8164 1, 8165 1, 8166 1, 8167 1, 8168 1, 8169 1, 8170 1, 8171 1, 8172 1, 8173 1, 8174 1, 8175 1, 8176 1, 8177 1, 8178 1, 8179 1, 8180 1, 8181 1, 8182 1, 8183 1, 8184 1, 8185 1, 8186 1, 8187 1, 8188 1, 8189 1, 8190 1, 8191 1, 8192 1, 8193 1, 8194 1, 8195 1, 8196 1, 8197 1, 8198 1, 8199 1, 8200 1, 8201 1, 8202 1, 8203 1, 8204 1, 8205 1, 8206 1, 8207 1, 8208 1, 8209 1, 8210 1, 8211 1, 8212 1, 8213 1, 8214 1, 8215 1, 8216 1, 8217 1, 8218 1, 8219 1, 8220 1, 8221 1, 8222 1, 8223 1, 8224 1, 8225 1, 8226 1, 8227 1, 8228 1, 8229 1, 8230 1, 8231 1, 8232 1, 8233 1, 8234 1, 8235 1, 8236 1, 8237 1, 8238 1, 8239 1, 8240 1, 8241 1, 8242 1, 8243 1, 8244 1, 8245 1, 8246 1, 8247 1, 8248 1, 8249 1, 8250 1, 8251 1, 8252 1, 8253 1, 8254 1, 8255 1, 8256 1, 8257 1, 8258 1, 8259 1, 8260 1, 8261 1, 8262 1, 8263 1, 8264 1, 8265 1, 8266 1, 8267 1, 8268 1, 8269 1, 8270 1, 8271 1, 8272 1, 8273 1, 8274 1, 8275 1, 8276 1, 8277 1, 8278 1, 8279 1, 8280 1, 8281 1, 8282 1, 8283 1, 8284 1, 8285 1, 8286 1, 8287 1, 8288 1, 8289 1, 8290 1, 8291 1, 8292 1, 8293 1, 8294 1, 8295 1, 8296 1, 8297 1, 8298 1, 8299 1, 8300 1, 8301 1, 8302 1, 8303 1, 8304 1, 8305 1, 8306 1, 8307 1, 8308 1, 8309 1, 8310 1, 8311 1, 8312 1, 8313 1, 8314 1, 8315 1, 8316 1, 8317 1, 8318 1, 8319 1, 8320 1, 8321 1, 8322 1, 8323 1, 8324 1, 8325 1, 8326 1, 8327 1, 8328 1, 8329 1, 8330 1, 8331 1, 8332 1, 8333 1, 8334 1, 8335 1, 8336 1, 8337 1, 8338 1, 8339 1, 8340 1, 8341 1, 8342 1, 8343 1, 8344 1, 8345 1, 8346 1, 8347 1, 8348 1, 8349 1, 8350 1, 8351 1, 8352 1, 8353 1, 8354 1, 8355 1, 8356 1, 8357 1, 8358 1, 8359 1, 8360 1, 8361 1, 8362 1, 8363 1, 8364 1, 8365 1, 8366 1, 8367 1, 8368 1, 8369 1, 8370 1, 8371 1, 8372 1, 8373 1, 8374 1, 8375 1, 8376 1, 8377 1, 8378 1, 8379 1, 8380 1, 8381 1, 8382 1, 8383 1, 8384 1, 8385 1, 8386 1, 8387 1, 8388 1, 8389 1, 8390 1, 8391 1, 8392 1, 8393 1, 8394 1, 8395 1, 8396 1, 8397 1, 8398 1, 8399 1, 8400 1, 8401 1, 8402 1, 8403 1, 8404 1, 8405 1, 8406 1, 8407 1, 8408 1, 8409 1, 8410 1, 8411 1, 8412 1, 8413 1, 8414 1, 8415 1, 8416 1, 8417 1, 8418 1, 8419 1, 8420 1, 8421 1, 8422 1, 8423 1, 8424 1, 8425 1, 8426 1, 8427 1, 8428 1, 8429 1, 8430 1, 8431 1, 8432 1, 8433 1, 8434 1, 8435 1, 8436 1, 8437 1, 8438 1, 8439 1, 8440 1, 8441 1, 8442 1, 8443 1, 8444 1, 8445 1, 8446 1, 8447 1, 8448 1, 8449 1, 8450 1, 8451 1, 8452 1, 8453 1, 8454 1, 8455 1, 8456 1, 8457 1, 8458 1, 8459 1, 8460 1, 8461 1, 8462 1, 8463 1, 8464 1, 8465 1, 8466 1, 8467 1, 8468 1, 8469 1, 8470 1, 8471 1, 8472 1, 8473 1, 8474 1, 8475 1, 8476 1, 8477 1, 8478 1, 8479 1, 8480 1, 8481 1, 8482 1, 8483 1, 8484 1, 8485 1, 8486 1, 8487 1, 8488 1, 8489 1, 8490 1, 8491 1, 8492 1, 8493 1, 8494 1, 8495 1, 8496 1, 8497 1, 8498 1, 8499 1, 8500 1, 8501 1, 8502 1, 8503 1, 8504 1, 8505 1, 8506 1, 8507 1, 8508 1, 8509 1, 8510 1, 8511 1, 8512 1, 8513 1, 8514 1, 8515 1, 8516 1, 8517 1, 8518 1, 8519 1, 8520 1, 8521 1, 8522 1, 8523 1, 8524 1, 8525 1, 8526 1, 8527 1, 8528 1, 8529 1, 8530 1, 8531 1, 8532 1, 8533 1, 8534 1, 8535 1, 8536 1, 8537 1, 8538 1, 8539 1, 8540 1, 8541 1, 8542 1, 8543 1, 8544 1, 8545 1, 8546 1, 8547 1, 8548 1, 8549 1, 8550 1, 8551 1, 8552 1, 8553 1, 8554 1, 8555 1, 8556 1, 8557 1, 8558 1, 8559 1, 8560 1, 8561 1, 8562 1, 8563 1, 8564 1, 8565 1, 8566 1, 8567 1, 8568 1, 8569 1, 8570 1, 8571 1, 8572 1, 8573 1, 8574 1, 8575 1, 8576 1, 8577 1, 8578 1, 8579 1, 8580 1, 8581 1, 8582 1, 8583 1, 8584 1, 8585 1, 8586 1, 8587 1, 8588 1, 8589 1, 8590 1, 8591 1, 8592 1, 8593 1, 8594 1, 8595 1, 8596 1, 8597 1, 8598 1, 8599 1, 8600 1, 8601 1, 8602 1, 8603 1, 8604 1, 8605 1, 8606 1, 8607 1, 8608 1, 8609 1, 8610 1, 8611 1, 8612 1, 8613 1, 8614 1, 8615 1, 8616 1, 8617 1, 8618 1, 8619 1, 8620 1, 8621 1, 8622 1, 8623 1, 8624 1, 8625 1, 8626 1, 8627 1, 8628 1, 8629 1, 8630 1, 8631 1, 8632 1, 8633 1, 8634 1, 8635 1, 8636 1, 8637 1, 8638 1, 8639 1, 8640 1, 8641 1, 8642 1, 8643 1, 8644 1, 8645 1, 8646 1, 8647 1, 8648 1, 8649 1, 8650 1, 8651 1, 8652 1, 8653 1, 8654 1, 8655 1, 8656 1, 8657 1, 8658 1, 8659 1, 8660 1, 8661 1, 8662 1, 8663 1, 8664 1, 8665 1, 8666 1, 8667 1, 8668 1, 8669 1, 8670 1, 8671 1, 8672 1, 8673 1, 8674 1, 8675 1, 8676 1, 8677 1, 8678 1, 8679 1, 8680 1, 8681 1, 8682 1, 8683 1, 8684 1, 8685 1, 8686 1, 8687 1, 8688 1, 8689 1, 8690 1, 8691 1, 8692 1, 8693 1, 8694 1, 8695 1, 8696 1, 8697 1, 8698 1, 8699 1, 8700 1, 8701 1, 8702 1, 8703 1, 8704 1, 8705 1, 8706 1, 8707 1, 8708 1, 8709 1, 8710 1, 8711 1, 8712 1, 8713 1, 8714 1, 8715 1, 8716 1, 8717 1, 8718 1, 8719 1, 8720 1, 8721 1, 8722 1, 8723 1, 8724 1, 8725 1, 8726 1, 8727 1, 8728 1, 8729 1, 8730 1, 8731 1, 8732 1, 8733 1, 8734 1, 8735 1, 8736 1, 8737 1, 8738 1, 8739 1, 8740 1, 8741 1, 8742 1, 8743 1, 8744 1, 8745 1, 8746 1, 8747 1, 8748 1, 8749 1, 8750 1, 8751 1, 8752 1, 8753 1, 8754 1, 8755 1, 8756 1, 8757 1, 8758 1, 8759 1, 8760 1, 8761 1, 8762 1, 8763 1, 8764 1, 8765 1, 8766 1, 8767 1, 8768 1, 8769 1, 8770 1, 8771 1, 8772 1, 8773 1, 8774 1, 8775 1, 8776 1, 8777 1, 8778 1, 8779 1, 8780 1, 8781 1, 8782 1, 8783 1, 8784 1, 8785 1, 8786 1, 8787 1, 8788 1, 8789 1, 8790 1, 8791 1, 8792 1, 8793 1, 8794 1, 8795 1, 8796 1, 8797 1, 8798 1, 8799 1, 8800 1, 8801 1, 8802 1, 8803 1, 8804 1, 8805 1, 8806 1, 8807 1, 8808 1, 8809 1, 8810 1, 8811 1, 8812 1, 8813 1, 8814 1, 8815 1, 8816 1, 8817 1, 8818 1, 8819 1, 8820 1, 8821 1, 8822 1, 8823 1, 8824 1, 8825 1, 8826 1, 8827 1, 8828 1, 8829 1, 8830 1, 8831 1, 8832 1, 8833 1, 8834 1, 8835 1, 8836 1, 8837 1, 8838 1, 8839 1, 8840 1, 8841 1, 8842 1, 8843 1, 8844 1, 8845 1, 8846 1, 8847 1, 8848 1, 8849 1, 8850 1, 8851 1, 8852 1, 8853 1, 8854 1, 8855 1, 8856 1, 8857 1, 8858 1, 8859 1, 8860 1, 8861 1, 8862 1, 8863 1, 8864 1, 8865 1, 8866 1, 8867 1, 8868 1, 8869 1, 8870 1, 8871 1, 8872 1, 8873 1, 8874 1, 8875 1, 8876 1, 8877 1, 8878 1, 8879 1, 8880 1, 8881 1, 8882 1, 8883 1, 8884 1, 8885 1, 8886 1, 8887 1, 8888 1, 8889 1, 8890 1, 8891 1, 8892 1, 8893 1, 8894 1, 8895 1, 8896 1, 8897 1, 8898 1, 8899 1, 8900 1, 8901 1, 8902 1, 8903 1, 8904 1, 8905 1, 8906 1, 8907 1, 8908 1, 8909 1, 8910 1, 8911 1, 8912 1, 8913 1, 8914 1, 8915 1, 8916 1, 8917 1, 8918 1, 8919 1, 8920 1, 8921 1, 8922 1, 8923 1, 8924 1, 8925 1, 8926 1, 8927 1, 8928 1, 8929 1, 8930 1, 8931 1, 8932 1, 8933 1, 8934 1, 8935 1, 8936 1, 8937 1, 8938 1, 8939 1, 8940 1, 8941 1, 8942 1, 8943 1, 8944 1, 8945 1, 8946 1, 8947 1, 8948 1, 8949 1, 8950 1, 8951 1, 8952 1, 8953 1, 8954 1, 8955 1, 8956 1, 8957 1, 8958 1, 8959 1, 8960 1, 8961 1, 8962 1, 8963 1, 8964 1, 8965 1, 8966 1, 8967 1, 8968 1, 8969 1, 8970 1, 8971 1, 8972 1, 8973 1, 8974 1, 8975 1, 8976 1, 8977 1, 8978 1, 8979 1, 8980 1, 8981 1, 8982 1, 8983 1, 8984 1, 8985 1, 8986 1, 8987 1, 8988 1, 8989 1, 8990 1, 8991 1, 8992 1, 8993 1, 8994 1, 8995 1, 8996 1, 8997 1, 8998 1, 8999 1, 9000 1, 9001 1, 9002 1, 9003 1, 9004 1, 9005 1, 9006 1, 9007 1, 9008 1, 9009 1, 9010 1, 9011 1, 9012 1, 9013 1, 9014 1, 9015 1, 9016 1, 9017 1, 9018 1, 9019 1, 9020 1, 9021 1, 9022 1, 9023 1, 9024 1, 9025 1, 9026 1, 9027 1, 9028 1, 9029 1, 9030 1, 9031 1, 9032 1, 9033 1, 9034 1, 9035 1, 9036 1, 9037 1, 9038 1, 9039 1, 9040 1, 9041 1, 9042 1, 9043 1, 9044 1, 9045 1, 9046 1, 9047 1, 9048 1, 9049 1, 9050 1, 9051 1, 9052 1, 9053 1, 9054 1, 9055 1, 9056 1, 9057 1, 9058 1, 9059 1, 9060 1, 9061 1, 9062 1, 9063 1, 9064 1, 9065 1, 9066 1, 9067 1, 9068 1, 9069 1, 9070 1, 9071 1, 9072 1, 9073 1, 9074 1, 9075 1, 9076 1, 9077 1, 9078 1, 9079 1, 9080 1, 9081 1, 9082 1, 9083 1, 9084 1, 9085 1, 9086 1, 9087 1, 9088 1, 9089 1, 9090 1, 9091 1, 9092 1, 9093 1, 9094 1, 9095 1, 9096 1, 9097 1, 9098 1, 9099 1, 9100 1, 9101 1, 9102 1, 9103 1, 9104 1, 9105 1, 9106 1, 9107 1, 9108 1, 9109 1, 9110 1, 9111 1, 9112 1, 9113 1, 9114 1, 9115 1, 9116 1, 9117 1, 9118 1, 9119 1, 9120 1, 9121 1, 9122 1, 9123 1, 9124 1, 9125 1, 9126 1, 9127 1, 9128 1, 9129 1, 9130 1, 9131 1, 9132 1, 9133 1, 9134 1, 9135 1, 9136 1, 9137 1, 9138 1, 9139 1, 9140 1, 9141 1, 9142 1, 9143 1, 9144 1, 9145 1, 9146 1, 9147 1, 9148 1, 9149 1, 9150 1, 9151 1, 9152 1, 9153 1, 9154 1, 9155 1, 9156 1, 9157 1, 9158 1, 9159 1, 9160 1, 9161 1, 9162 1, 9163 1, 9164 1, 9165 1, 9166 1, 9167 1, 9168 1, 9169 1, 9170 1, 9171 1, 9172 1, 9173 1, 9174 1, 9175 1, 9176 1, 9177 1, 9178 1, 9179 1, 9180 1, 9181 1, 9182 1, 9183 1, 9184 1, 9185 1, 9186 1, 9187 1, 9188 1, 9189 1, 9190 1, 9191 1, 9192 1, 9193 1, 9194 1, 9195 1, 9196 1, 9197 1, 9198 1, 9199 1, 9200 1, 9201 1, 9202 1, 9203 1, 9204 1, 9205 1, 9206 1, 9207 1, 9208 1, 9209 1, 9210 1, 9211 1, 9212 1, 9213 1, 9214 1, 9215 1, 9216 1, 9217 1, 9218 1, 9219 1, 9220 1, 9221 1, 9222 1, 9223 1, 9224 1, 9225 1, 9226 1, 9227 1, 9228 1, 9229 1, 9230 1, 9231 1, 9232 1, 9233 1, 9234 1, 9235 1, 9236 1, 9237 1, 9238 1, 9239 1, 9240 1, 9241 1, 9242 1, 9243 1, 9244 1, 9245 1, 9246 1, 9247 1, 9248 1, 9249 1, 9250 1, 9251 1, 9252 1, 9253 1, 9254 1, 9255 1, 9256 1, 9257 1, 9258 1, 9259 1, 9260 1, 9261 1, 9262 1, 9263 1, 9264 1, 9265 1, 9266 1, 9267 1, 9268 1, 9269 1, 9270 1, 9271 1, 9272 1, 9273 1, 9274 1, 9275 1, 9276 1, 9277 1, 9278 1, 9279 1, 9280 1, 9281 1, 9282 1, 9283 1, 9284 1, 9285 1, 9286 1, 9287 1, 9288 1, 9289 1, 9290 1, 9291 1, 9292 1, 9293 1, 9294 1, 9295 1, 9296 1, 9297 1, 9298 1, 9299 1, 9300 1, 9301 1, 9302 1, 9303 1, 9304 1, 9305 1, 9306 1, 9307 1, 9308 1, 9309 1, 9310 1, 9311 1, 9312 1, 9313 1, 9314 1, 9315 1, 9316 1, 9317 1, 9318 1, 9319 1, 9320 1, 9321 1, 9322 1, 9323 1, 9324 1, 9325 1, 9326 1, 9327 1, 9328 1, 9329 ]; 9330 let pub_key = MlDsaVerKey::<MlDsa65>::decode(&[1u8; 1952].into()) 9331 .to_public_key_der() 9332 .unwrap(); 9333 let att_obj_len = att_obj.len(); 9334 let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes()); 9335 let b64_adata = base64url_nopad::encode(&att_obj[att_obj_len - 2033..]); 9336 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 9337 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 9338 // Base case is valid. 9339 assert!( 9340 serde_json::from_str::<Registration>( 9341 serde_json::json!({ 9342 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9343 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9344 "response": { 9345 "clientDataJSON": b64_cdata_json, 9346 "authenticatorData": b64_adata, 9347 "transports": [], 9348 "publicKey": b64_key, 9349 "publicKeyAlgorithm": -49i8, 9350 "attestationObject": b64_aobj, 9351 }, 9352 "clientExtensionResults": {}, 9353 "type": "public-key" 9354 }) 9355 .to_string() 9356 .as_str() 9357 ) 9358 .is_ok_and( 9359 |reg| reg.response.client_data_json == c_data_json.as_bytes() 9360 && reg.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 9361 && reg.response.attestation_object_and_c_data_hash[att_obj_len..] 9362 == *Sha256::digest(c_data_json.as_bytes()) 9363 && reg.response.transports.is_empty() 9364 && matches!(reg.authenticator_attachment, AuthenticatorAttachment::None) 9365 && reg.client_extension_results.cred_props.is_none() 9366 && reg.client_extension_results.prf.is_none() 9367 ) 9368 ); 9369 // `publicKeyAlgorithm` mismatch. 9370 let mut err = Error::invalid_value( 9371 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()), 9372 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa65).as_str() 9373 ) 9374 .to_string().into_bytes(); 9375 assert_eq!( 9376 serde_json::from_str::<Registration>( 9377 serde_json::json!({ 9378 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9379 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9380 "response": { 9381 "clientDataJSON": b64_cdata_json, 9382 "authenticatorData": b64_adata, 9383 "transports": [], 9384 "publicKey": b64_key, 9385 "publicKeyAlgorithm": -8i8, 9386 "attestationObject": b64_aobj, 9387 }, 9388 "clientExtensionResults": {}, 9389 "type": "public-key" 9390 }) 9391 .to_string() 9392 .as_str() 9393 ) 9394 .unwrap_err() 9395 .to_string() 9396 .into_bytes() 9397 .get(..err.len()), 9398 Some(err.as_slice()) 9399 ); 9400 // Missing `publicKeyAlgorithm`. 9401 err = Error::missing_field("publicKeyAlgorithm") 9402 .to_string() 9403 .into_bytes(); 9404 assert_eq!( 9405 serde_json::from_str::<Registration>( 9406 serde_json::json!({ 9407 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9408 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9409 "response": { 9410 "clientDataJSON": b64_cdata_json, 9411 "authenticatorData": b64_adata, 9412 "transports": [], 9413 "publicKey": b64_key, 9414 "attestationObject": b64_aobj, 9415 }, 9416 "clientExtensionResults": {}, 9417 "type": "public-key" 9418 }) 9419 .to_string() 9420 .as_str() 9421 ) 9422 .unwrap_err() 9423 .to_string() 9424 .into_bytes() 9425 .get(..err.len()), 9426 Some(err.as_slice()) 9427 ); 9428 // `null` `publicKeyAlgorithm`. 9429 err = Error::invalid_type(Unexpected::Other("null"), &"publicKeyAlgorithm") 9430 .to_string() 9431 .into_bytes(); 9432 assert_eq!( 9433 serde_json::from_str::<Registration>( 9434 serde_json::json!({ 9435 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9436 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9437 "response": { 9438 "clientDataJSON": b64_cdata_json, 9439 "authenticatorData": b64_adata, 9440 "transports": [], 9441 "publicKey": b64_key, 9442 "publicKeyAlgorithm": null, 9443 "attestationObject": b64_aobj, 9444 }, 9445 "clientExtensionResults": {}, 9446 "type": "public-key" 9447 }) 9448 .to_string() 9449 .as_str() 9450 ) 9451 .unwrap_err() 9452 .to_string() 9453 .into_bytes() 9454 .get(..err.len()), 9455 Some(err.as_slice()) 9456 ); 9457 // `publicKey` mismatch. 9458 let bad_pub_key = MlDsaVerKey::<MlDsa65>::decode(&[2; 1952].into()); 9459 err = Error::invalid_value( 9460 Unexpected::Bytes([0; 32].as_slice()), 9461 &format!( 9462 "DER-encoded public key to match the public key within the attestation object: MlDsa65(MlDsa65PubKey({:?}))", 9463 &[1u8; 1952] 9464 ) 9465 .as_str(), 9466 ) 9467 .to_string().into_bytes(); 9468 assert_eq!(serde_json::from_str::<Registration>( 9469 serde_json::json!({ 9470 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9471 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9472 "response": { 9473 "clientDataJSON": b64_cdata_json, 9474 "authenticatorData": b64_adata, 9475 "transports": [], 9476 "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()), 9477 "publicKeyAlgorithm": -49i8, 9478 "attestationObject": b64_aobj, 9479 }, 9480 "clientExtensionResults": {}, 9481 "type": "public-key" 9482 }) 9483 .to_string() 9484 .as_str() 9485 ) 9486 .unwrap_err().to_string().into_bytes().get(..err.len()), 9487 Some(err.as_slice()) 9488 ); 9489 // Missing `publicKey` is allowed when not using EdDSA, ES256, or RS256. 9490 assert!( 9491 serde_json::from_str::<Registration>( 9492 serde_json::json!({ 9493 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9494 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9495 "response": { 9496 "clientDataJSON": b64_cdata_json, 9497 "authenticatorData": b64_adata, 9498 "transports": [], 9499 "publicKeyAlgorithm": -49i8, 9500 "attestationObject": b64_aobj, 9501 }, 9502 "clientExtensionResults": {}, 9503 "type": "public-key" 9504 }) 9505 .to_string() 9506 .as_str() 9507 ) 9508 .is_ok() 9509 ); 9510 // `publicKeyAlgorithm` mismatch when `publicKey` does not exist. 9511 err = Error::invalid_value( 9512 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 9513 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa65).as_str() 9514 ) 9515 .to_string().into_bytes(); 9516 assert_eq!( 9517 serde_json::from_str::<Registration>( 9518 serde_json::json!({ 9519 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9520 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9521 "response": { 9522 "clientDataJSON": b64_cdata_json, 9523 "authenticatorData": b64_adata, 9524 "transports": [], 9525 "publicKeyAlgorithm": -7i8, 9526 "attestationObject": b64_aobj, 9527 }, 9528 "clientExtensionResults": {}, 9529 "type": "public-key" 9530 }) 9531 .to_string() 9532 .as_str() 9533 ) 9534 .unwrap_err() 9535 .to_string() 9536 .into_bytes() 9537 .get(..err.len()), 9538 Some(err.as_slice()) 9539 ); 9540 // `null` `publicKey` is allowed when not using EdDSA, ES256, or RS256. 9541 assert!( 9542 serde_json::from_str::<Registration>( 9543 serde_json::json!({ 9544 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9545 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9546 "response": { 9547 "clientDataJSON": b64_cdata_json, 9548 "authenticatorData": b64_adata, 9549 "transports": [], 9550 "publicKey": null, 9551 "publicKeyAlgorithm": -49i8, 9552 "attestationObject": b64_aobj, 9553 }, 9554 "clientExtensionResults": {}, 9555 "type": "public-key" 9556 }) 9557 .to_string() 9558 .as_str() 9559 ) 9560 .is_ok() 9561 ); 9562 // `publicKeyAlgorithm` mismatch when `publicKey` is null. 9563 err = Error::invalid_value( 9564 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 9565 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa65).as_str() 9566 ) 9567 .to_string().into_bytes(); 9568 assert_eq!( 9569 serde_json::from_str::<Registration>( 9570 serde_json::json!({ 9571 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9572 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9573 "response": { 9574 "clientDataJSON": b64_cdata_json, 9575 "authenticatorData": b64_adata, 9576 "transports": [], 9577 "publicKey": null, 9578 "publicKeyAlgorithm": -7i8, 9579 "attestationObject": b64_aobj, 9580 }, 9581 "clientExtensionResults": {}, 9582 "type": "public-key" 9583 }) 9584 .to_string() 9585 .as_str() 9586 ) 9587 .unwrap_err() 9588 .to_string() 9589 .into_bytes() 9590 .get(..err.len()), 9591 Some(err.as_slice()) 9592 ); 9593 } 9594 #[expect( 9595 clippy::assertions_on_result_states, 9596 clippy::unwrap_used, 9597 reason = "OK in tests" 9598 )] 9599 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 9600 #[expect(clippy::too_many_lines, reason = "a lot to test")] 9601 #[test] 9602 fn mldsa44_registration_deserialize_data_mismatch() { 9603 let c_data_json = serde_json::json!({}).to_string(); 9604 let att_obj: [u8; 1424] = [ 9605 cbor::MAP_3, 9606 cbor::TEXT_3, 9607 b'f', 9608 b'm', 9609 b't', 9610 cbor::TEXT_4, 9611 b'n', 9612 b'o', 9613 b'n', 9614 b'e', 9615 cbor::TEXT_7, 9616 b'a', 9617 b't', 9618 b't', 9619 b'S', 9620 b't', 9621 b'm', 9622 b't', 9623 cbor::MAP_0, 9624 cbor::TEXT_8, 9625 b'a', 9626 b'u', 9627 b't', 9628 b'h', 9629 b'D', 9630 b'a', 9631 b't', 9632 b'a', 9633 cbor::BYTES_INFO_25, 9634 5, 9635 113, 9636 // `rpIdHash`. 9637 0, 9638 0, 9639 0, 9640 0, 9641 0, 9642 0, 9643 0, 9644 0, 9645 0, 9646 0, 9647 0, 9648 0, 9649 0, 9650 0, 9651 0, 9652 0, 9653 0, 9654 0, 9655 0, 9656 0, 9657 0, 9658 0, 9659 0, 9660 0, 9661 0, 9662 0, 9663 0, 9664 0, 9665 0, 9666 0, 9667 0, 9668 0, 9669 // `flags`. 9670 0b0100_0101, 9671 // `signCount`. 9672 0, 9673 0, 9674 0, 9675 0, 9676 // `aaguid`. 9677 0, 9678 0, 9679 0, 9680 0, 9681 0, 9682 0, 9683 0, 9684 0, 9685 0, 9686 0, 9687 0, 9688 0, 9689 0, 9690 0, 9691 0, 9692 0, 9693 // `credentialIdLength`. 9694 0, 9695 16, 9696 // `credentialId`. 9697 0, 9698 0, 9699 0, 9700 0, 9701 0, 9702 0, 9703 0, 9704 0, 9705 0, 9706 0, 9707 0, 9708 0, 9709 0, 9710 0, 9711 0, 9712 0, 9713 // ML-DSA-44 COSE key. 9714 cbor::MAP_3, 9715 KTY, 9716 AKP, 9717 ALG, 9718 cbor::NEG_INFO_24, 9719 MLDSA44, 9720 // `pub`. 9721 cbor::NEG_ONE, 9722 cbor::BYTES_INFO_25, 9723 5, 9724 32, 9725 // Encoded key. 9726 1, 9727 1, 9728 1, 9729 1, 9730 1, 9731 1, 9732 1, 9733 1, 9734 1, 9735 1, 9736 1, 9737 1, 9738 1, 9739 1, 9740 1, 9741 1, 9742 1, 9743 1, 9744 1, 9745 1, 9746 1, 9747 1, 9748 1, 9749 1, 9750 1, 9751 1, 9752 1, 9753 1, 9754 1, 9755 1, 9756 1, 9757 1, 9758 1, 9759 1, 9760 1, 9761 1, 9762 1, 9763 1, 9764 1, 9765 1, 9766 1, 9767 1, 9768 1, 9769 1, 9770 1, 9771 1, 9772 1, 9773 1, 9774 1, 9775 1, 9776 1, 9777 1, 9778 1, 9779 1, 9780 1, 9781 1, 9782 1, 9783 1, 9784 1, 9785 1, 9786 1, 9787 1, 9788 1, 9789 1, 9790 1, 9791 1, 9792 1, 9793 1, 9794 1, 9795 1, 9796 1, 9797 1, 9798 1, 9799 1, 9800 1, 9801 1, 9802 1, 9803 1, 9804 1, 9805 1, 9806 1, 9807 1, 9808 1, 9809 1, 9810 1, 9811 1, 9812 1, 9813 1, 9814 1, 9815 1, 9816 1, 9817 1, 9818 1, 9819 1, 9820 1, 9821 1, 9822 1, 9823 1, 9824 1, 9825 1, 9826 1, 9827 1, 9828 1, 9829 1, 9830 1, 9831 1, 9832 1, 9833 1, 9834 1, 9835 1, 9836 1, 9837 1, 9838 1, 9839 1, 9840 1, 9841 1, 9842 1, 9843 1, 9844 1, 9845 1, 9846 1, 9847 1, 9848 1, 9849 1, 9850 1, 9851 1, 9852 1, 9853 1, 9854 1, 9855 1, 9856 1, 9857 1, 9858 1, 9859 1, 9860 1, 9861 1, 9862 1, 9863 1, 9864 1, 9865 1, 9866 1, 9867 1, 9868 1, 9869 1, 9870 1, 9871 1, 9872 1, 9873 1, 9874 1, 9875 1, 9876 1, 9877 1, 9878 1, 9879 1, 9880 1, 9881 1, 9882 1, 9883 1, 9884 1, 9885 1, 9886 1, 9887 1, 9888 1, 9889 1, 9890 1, 9891 1, 9892 1, 9893 1, 9894 1, 9895 1, 9896 1, 9897 1, 9898 1, 9899 1, 9900 1, 9901 1, 9902 1, 9903 1, 9904 1, 9905 1, 9906 1, 9907 1, 9908 1, 9909 1, 9910 1, 9911 1, 9912 1, 9913 1, 9914 1, 9915 1, 9916 1, 9917 1, 9918 1, 9919 1, 9920 1, 9921 1, 9922 1, 9923 1, 9924 1, 9925 1, 9926 1, 9927 1, 9928 1, 9929 1, 9930 1, 9931 1, 9932 1, 9933 1, 9934 1, 9935 1, 9936 1, 9937 1, 9938 1, 9939 1, 9940 1, 9941 1, 9942 1, 9943 1, 9944 1, 9945 1, 9946 1, 9947 1, 9948 1, 9949 1, 9950 1, 9951 1, 9952 1, 9953 1, 9954 1, 9955 1, 9956 1, 9957 1, 9958 1, 9959 1, 9960 1, 9961 1, 9962 1, 9963 1, 9964 1, 9965 1, 9966 1, 9967 1, 9968 1, 9969 1, 9970 1, 9971 1, 9972 1, 9973 1, 9974 1, 9975 1, 9976 1, 9977 1, 9978 1, 9979 1, 9980 1, 9981 1, 9982 1, 9983 1, 9984 1, 9985 1, 9986 1, 9987 1, 9988 1, 9989 1, 9990 1, 9991 1, 9992 1, 9993 1, 9994 1, 9995 1, 9996 1, 9997 1, 9998 1, 9999 1, 10000 1, 10001 1, 10002 1, 10003 1, 10004 1, 10005 1, 10006 1, 10007 1, 10008 1, 10009 1, 10010 1, 10011 1, 10012 1, 10013 1, 10014 1, 10015 1, 10016 1, 10017 1, 10018 1, 10019 1, 10020 1, 10021 1, 10022 1, 10023 1, 10024 1, 10025 1, 10026 1, 10027 1, 10028 1, 10029 1, 10030 1, 10031 1, 10032 1, 10033 1, 10034 1, 10035 1, 10036 1, 10037 1, 10038 1, 10039 1, 10040 1, 10041 1, 10042 1, 10043 1, 10044 1, 10045 1, 10046 1, 10047 1, 10048 1, 10049 1, 10050 1, 10051 1, 10052 1, 10053 1, 10054 1, 10055 1, 10056 1, 10057 1, 10058 1, 10059 1, 10060 1, 10061 1, 10062 1, 10063 1, 10064 1, 10065 1, 10066 1, 10067 1, 10068 1, 10069 1, 10070 1, 10071 1, 10072 1, 10073 1, 10074 1, 10075 1, 10076 1, 10077 1, 10078 1, 10079 1, 10080 1, 10081 1, 10082 1, 10083 1, 10084 1, 10085 1, 10086 1, 10087 1, 10088 1, 10089 1, 10090 1, 10091 1, 10092 1, 10093 1, 10094 1, 10095 1, 10096 1, 10097 1, 10098 1, 10099 1, 10100 1, 10101 1, 10102 1, 10103 1, 10104 1, 10105 1, 10106 1, 10107 1, 10108 1, 10109 1, 10110 1, 10111 1, 10112 1, 10113 1, 10114 1, 10115 1, 10116 1, 10117 1, 10118 1, 10119 1, 10120 1, 10121 1, 10122 1, 10123 1, 10124 1, 10125 1, 10126 1, 10127 1, 10128 1, 10129 1, 10130 1, 10131 1, 10132 1, 10133 1, 10134 1, 10135 1, 10136 1, 10137 1, 10138 1, 10139 1, 10140 1, 10141 1, 10142 1, 10143 1, 10144 1, 10145 1, 10146 1, 10147 1, 10148 1, 10149 1, 10150 1, 10151 1, 10152 1, 10153 1, 10154 1, 10155 1, 10156 1, 10157 1, 10158 1, 10159 1, 10160 1, 10161 1, 10162 1, 10163 1, 10164 1, 10165 1, 10166 1, 10167 1, 10168 1, 10169 1, 10170 1, 10171 1, 10172 1, 10173 1, 10174 1, 10175 1, 10176 1, 10177 1, 10178 1, 10179 1, 10180 1, 10181 1, 10182 1, 10183 1, 10184 1, 10185 1, 10186 1, 10187 1, 10188 1, 10189 1, 10190 1, 10191 1, 10192 1, 10193 1, 10194 1, 10195 1, 10196 1, 10197 1, 10198 1, 10199 1, 10200 1, 10201 1, 10202 1, 10203 1, 10204 1, 10205 1, 10206 1, 10207 1, 10208 1, 10209 1, 10210 1, 10211 1, 10212 1, 10213 1, 10214 1, 10215 1, 10216 1, 10217 1, 10218 1, 10219 1, 10220 1, 10221 1, 10222 1, 10223 1, 10224 1, 10225 1, 10226 1, 10227 1, 10228 1, 10229 1, 10230 1, 10231 1, 10232 1, 10233 1, 10234 1, 10235 1, 10236 1, 10237 1, 10238 1, 10239 1, 10240 1, 10241 1, 10242 1, 10243 1, 10244 1, 10245 1, 10246 1, 10247 1, 10248 1, 10249 1, 10250 1, 10251 1, 10252 1, 10253 1, 10254 1, 10255 1, 10256 1, 10257 1, 10258 1, 10259 1, 10260 1, 10261 1, 10262 1, 10263 1, 10264 1, 10265 1, 10266 1, 10267 1, 10268 1, 10269 1, 10270 1, 10271 1, 10272 1, 10273 1, 10274 1, 10275 1, 10276 1, 10277 1, 10278 1, 10279 1, 10280 1, 10281 1, 10282 1, 10283 1, 10284 1, 10285 1, 10286 1, 10287 1, 10288 1, 10289 1, 10290 1, 10291 1, 10292 1, 10293 1, 10294 1, 10295 1, 10296 1, 10297 1, 10298 1, 10299 1, 10300 1, 10301 1, 10302 1, 10303 1, 10304 1, 10305 1, 10306 1, 10307 1, 10308 1, 10309 1, 10310 1, 10311 1, 10312 1, 10313 1, 10314 1, 10315 1, 10316 1, 10317 1, 10318 1, 10319 1, 10320 1, 10321 1, 10322 1, 10323 1, 10324 1, 10325 1, 10326 1, 10327 1, 10328 1, 10329 1, 10330 1, 10331 1, 10332 1, 10333 1, 10334 1, 10335 1, 10336 1, 10337 1, 10338 1, 10339 1, 10340 1, 10341 1, 10342 1, 10343 1, 10344 1, 10345 1, 10346 1, 10347 1, 10348 1, 10349 1, 10350 1, 10351 1, 10352 1, 10353 1, 10354 1, 10355 1, 10356 1, 10357 1, 10358 1, 10359 1, 10360 1, 10361 1, 10362 1, 10363 1, 10364 1, 10365 1, 10366 1, 10367 1, 10368 1, 10369 1, 10370 1, 10371 1, 10372 1, 10373 1, 10374 1, 10375 1, 10376 1, 10377 1, 10378 1, 10379 1, 10380 1, 10381 1, 10382 1, 10383 1, 10384 1, 10385 1, 10386 1, 10387 1, 10388 1, 10389 1, 10390 1, 10391 1, 10392 1, 10393 1, 10394 1, 10395 1, 10396 1, 10397 1, 10398 1, 10399 1, 10400 1, 10401 1, 10402 1, 10403 1, 10404 1, 10405 1, 10406 1, 10407 1, 10408 1, 10409 1, 10410 1, 10411 1, 10412 1, 10413 1, 10414 1, 10415 1, 10416 1, 10417 1, 10418 1, 10419 1, 10420 1, 10421 1, 10422 1, 10423 1, 10424 1, 10425 1, 10426 1, 10427 1, 10428 1, 10429 1, 10430 1, 10431 1, 10432 1, 10433 1, 10434 1, 10435 1, 10436 1, 10437 1, 10438 1, 10439 1, 10440 1, 10441 1, 10442 1, 10443 1, 10444 1, 10445 1, 10446 1, 10447 1, 10448 1, 10449 1, 10450 1, 10451 1, 10452 1, 10453 1, 10454 1, 10455 1, 10456 1, 10457 1, 10458 1, 10459 1, 10460 1, 10461 1, 10462 1, 10463 1, 10464 1, 10465 1, 10466 1, 10467 1, 10468 1, 10469 1, 10470 1, 10471 1, 10472 1, 10473 1, 10474 1, 10475 1, 10476 1, 10477 1, 10478 1, 10479 1, 10480 1, 10481 1, 10482 1, 10483 1, 10484 1, 10485 1, 10486 1, 10487 1, 10488 1, 10489 1, 10490 1, 10491 1, 10492 1, 10493 1, 10494 1, 10495 1, 10496 1, 10497 1, 10498 1, 10499 1, 10500 1, 10501 1, 10502 1, 10503 1, 10504 1, 10505 1, 10506 1, 10507 1, 10508 1, 10509 1, 10510 1, 10511 1, 10512 1, 10513 1, 10514 1, 10515 1, 10516 1, 10517 1, 10518 1, 10519 1, 10520 1, 10521 1, 10522 1, 10523 1, 10524 1, 10525 1, 10526 1, 10527 1, 10528 1, 10529 1, 10530 1, 10531 1, 10532 1, 10533 1, 10534 1, 10535 1, 10536 1, 10537 1, 10538 1, 10539 1, 10540 1, 10541 1, 10542 1, 10543 1, 10544 1, 10545 1, 10546 1, 10547 1, 10548 1, 10549 1, 10550 1, 10551 1, 10552 1, 10553 1, 10554 1, 10555 1, 10556 1, 10557 1, 10558 1, 10559 1, 10560 1, 10561 1, 10562 1, 10563 1, 10564 1, 10565 1, 10566 1, 10567 1, 10568 1, 10569 1, 10570 1, 10571 1, 10572 1, 10573 1, 10574 1, 10575 1, 10576 1, 10577 1, 10578 1, 10579 1, 10580 1, 10581 1, 10582 1, 10583 1, 10584 1, 10585 1, 10586 1, 10587 1, 10588 1, 10589 1, 10590 1, 10591 1, 10592 1, 10593 1, 10594 1, 10595 1, 10596 1, 10597 1, 10598 1, 10599 1, 10600 1, 10601 1, 10602 1, 10603 1, 10604 1, 10605 1, 10606 1, 10607 1, 10608 1, 10609 1, 10610 1, 10611 1, 10612 1, 10613 1, 10614 1, 10615 1, 10616 1, 10617 1, 10618 1, 10619 1, 10620 1, 10621 1, 10622 1, 10623 1, 10624 1, 10625 1, 10626 1, 10627 1, 10628 1, 10629 1, 10630 1, 10631 1, 10632 1, 10633 1, 10634 1, 10635 1, 10636 1, 10637 1, 10638 1, 10639 1, 10640 1, 10641 1, 10642 1, 10643 1, 10644 1, 10645 1, 10646 1, 10647 1, 10648 1, 10649 1, 10650 1, 10651 1, 10652 1, 10653 1, 10654 1, 10655 1, 10656 1, 10657 1, 10658 1, 10659 1, 10660 1, 10661 1, 10662 1, 10663 1, 10664 1, 10665 1, 10666 1, 10667 1, 10668 1, 10669 1, 10670 1, 10671 1, 10672 1, 10673 1, 10674 1, 10675 1, 10676 1, 10677 1, 10678 1, 10679 1, 10680 1, 10681 1, 10682 1, 10683 1, 10684 1, 10685 1, 10686 1, 10687 1, 10688 1, 10689 1, 10690 1, 10691 1, 10692 1, 10693 1, 10694 1, 10695 1, 10696 1, 10697 1, 10698 1, 10699 1, 10700 1, 10701 1, 10702 1, 10703 1, 10704 1, 10705 1, 10706 1, 10707 1, 10708 1, 10709 1, 10710 1, 10711 1, 10712 1, 10713 1, 10714 1, 10715 1, 10716 1, 10717 1, 10718 1, 10719 1, 10720 1, 10721 1, 10722 1, 10723 1, 10724 1, 10725 1, 10726 1, 10727 1, 10728 1, 10729 1, 10730 1, 10731 1, 10732 1, 10733 1, 10734 1, 10735 1, 10736 1, 10737 1, 10738 1, 10739 1, 10740 1, 10741 1, 10742 1, 10743 1, 10744 1, 10745 1, 10746 1, 10747 1, 10748 1, 10749 1, 10750 1, 10751 1, 10752 1, 10753 1, 10754 1, 10755 1, 10756 1, 10757 1, 10758 1, 10759 1, 10760 1, 10761 1, 10762 1, 10763 1, 10764 1, 10765 1, 10766 1, 10767 1, 10768 1, 10769 1, 10770 1, 10771 1, 10772 1, 10773 1, 10774 1, 10775 1, 10776 1, 10777 1, 10778 1, 10779 1, 10780 1, 10781 1, 10782 1, 10783 1, 10784 1, 10785 1, 10786 1, 10787 1, 10788 1, 10789 1, 10790 1, 10791 1, 10792 1, 10793 1, 10794 1, 10795 1, 10796 1, 10797 1, 10798 1, 10799 1, 10800 1, 10801 1, 10802 1, 10803 1, 10804 1, 10805 1, 10806 1, 10807 1, 10808 1, 10809 1, 10810 1, 10811 1, 10812 1, 10813 1, 10814 1, 10815 1, 10816 1, 10817 1, 10818 1, 10819 1, 10820 1, 10821 1, 10822 1, 10823 1, 10824 1, 10825 1, 10826 1, 10827 1, 10828 1, 10829 1, 10830 1, 10831 1, 10832 1, 10833 1, 10834 1, 10835 1, 10836 1, 10837 1, 10838 1, 10839 1, 10840 1, 10841 1, 10842 1, 10843 1, 10844 1, 10845 1, 10846 1, 10847 1, 10848 1, 10849 1, 10850 1, 10851 1, 10852 1, 10853 1, 10854 1, 10855 1, 10856 1, 10857 1, 10858 1, 10859 1, 10860 1, 10861 1, 10862 1, 10863 1, 10864 1, 10865 1, 10866 1, 10867 1, 10868 1, 10869 1, 10870 1, 10871 1, 10872 1, 10873 1, 10874 1, 10875 1, 10876 1, 10877 1, 10878 1, 10879 1, 10880 1, 10881 1, 10882 1, 10883 1, 10884 1, 10885 1, 10886 1, 10887 1, 10888 1, 10889 1, 10890 1, 10891 1, 10892 1, 10893 1, 10894 1, 10895 1, 10896 1, 10897 1, 10898 1, 10899 1, 10900 1, 10901 1, 10902 1, 10903 1, 10904 1, 10905 1, 10906 1, 10907 1, 10908 1, 10909 1, 10910 1, 10911 1, 10912 1, 10913 1, 10914 1, 10915 1, 10916 1, 10917 1, 10918 1, 10919 1, 10920 1, 10921 1, 10922 1, 10923 1, 10924 1, 10925 1, 10926 1, 10927 1, 10928 1, 10929 1, 10930 1, 10931 1, 10932 1, 10933 1, 10934 1, 10935 1, 10936 1, 10937 1, 10938 1, 10939 1, 10940 1, 10941 1, 10942 1, 10943 1, 10944 1, 10945 1, 10946 1, 10947 1, 10948 1, 10949 1, 10950 1, 10951 1, 10952 1, 10953 1, 10954 1, 10955 1, 10956 1, 10957 1, 10958 1, 10959 1, 10960 1, 10961 1, 10962 1, 10963 1, 10964 1, 10965 1, 10966 1, 10967 1, 10968 1, 10969 1, 10970 1, 10971 1, 10972 1, 10973 1, 10974 1, 10975 1, 10976 1, 10977 1, 10978 1, 10979 1, 10980 1, 10981 1, 10982 1, 10983 1, 10984 1, 10985 1, 10986 1, 10987 1, 10988 1, 10989 1, 10990 1, 10991 1, 10992 1, 10993 1, 10994 1, 10995 1, 10996 1, 10997 1, 10998 1, 10999 1, 11000 1, 11001 1, 11002 1, 11003 1, 11004 1, 11005 1, 11006 1, 11007 1, 11008 1, 11009 1, 11010 1, 11011 1, 11012 1, 11013 1, 11014 1, 11015 1, 11016 1, 11017 1, 11018 1, 11019 1, 11020 1, 11021 1, 11022 1, 11023 1, 11024 1, 11025 1, 11026 1, 11027 1, 11028 1, 11029 1, 11030 1, 11031 1, 11032 1, 11033 1, 11034 1, 11035 1, 11036 1, 11037 1, 11038 ]; 11039 let pub_key = MlDsaVerKey::<MlDsa44>::decode(&[1u8; 1312].into()) 11040 .to_public_key_der() 11041 .unwrap(); 11042 let att_obj_len = att_obj.len(); 11043 let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes()); 11044 let b64_adata = base64url_nopad::encode(&att_obj[att_obj_len - 1393..]); 11045 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 11046 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 11047 // Base case is valid. 11048 assert!( 11049 serde_json::from_str::<Registration>( 11050 serde_json::json!({ 11051 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11052 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11053 "response": { 11054 "clientDataJSON": b64_cdata_json, 11055 "authenticatorData": b64_adata, 11056 "transports": [], 11057 "publicKey": b64_key, 11058 "publicKeyAlgorithm": -48i8, 11059 "attestationObject": b64_aobj, 11060 }, 11061 "clientExtensionResults": {}, 11062 "type": "public-key" 11063 }) 11064 .to_string() 11065 .as_str() 11066 ) 11067 .is_ok_and( 11068 |reg| reg.response.client_data_json == c_data_json.as_bytes() 11069 && reg.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 11070 && reg.response.attestation_object_and_c_data_hash[att_obj_len..] 11071 == *Sha256::digest(c_data_json.as_bytes()) 11072 && reg.response.transports.is_empty() 11073 && matches!(reg.authenticator_attachment, AuthenticatorAttachment::None) 11074 && reg.client_extension_results.cred_props.is_none() 11075 && reg.client_extension_results.prf.is_none() 11076 ) 11077 ); 11078 // `publicKeyAlgorithm` mismatch. 11079 let mut err = Error::invalid_value( 11080 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()), 11081 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa44).as_str() 11082 ) 11083 .to_string().into_bytes(); 11084 assert_eq!( 11085 serde_json::from_str::<Registration>( 11086 serde_json::json!({ 11087 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11088 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11089 "response": { 11090 "clientDataJSON": b64_cdata_json, 11091 "authenticatorData": b64_adata, 11092 "transports": [], 11093 "publicKey": b64_key, 11094 "publicKeyAlgorithm": -8i8, 11095 "attestationObject": b64_aobj, 11096 }, 11097 "clientExtensionResults": {}, 11098 "type": "public-key" 11099 }) 11100 .to_string() 11101 .as_str() 11102 ) 11103 .unwrap_err() 11104 .to_string() 11105 .into_bytes() 11106 .get(..err.len()), 11107 Some(err.as_slice()) 11108 ); 11109 // Missing `publicKeyAlgorithm`. 11110 err = Error::missing_field("publicKeyAlgorithm") 11111 .to_string() 11112 .into_bytes(); 11113 assert_eq!( 11114 serde_json::from_str::<Registration>( 11115 serde_json::json!({ 11116 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11117 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11118 "response": { 11119 "clientDataJSON": b64_cdata_json, 11120 "authenticatorData": b64_adata, 11121 "transports": [], 11122 "publicKey": b64_key, 11123 "attestationObject": b64_aobj, 11124 }, 11125 "clientExtensionResults": {}, 11126 "type": "public-key" 11127 }) 11128 .to_string() 11129 .as_str() 11130 ) 11131 .unwrap_err() 11132 .to_string() 11133 .into_bytes() 11134 .get(..err.len()), 11135 Some(err.as_slice()) 11136 ); 11137 // `null` `publicKeyAlgorithm`. 11138 err = Error::invalid_type(Unexpected::Other("null"), &"publicKeyAlgorithm") 11139 .to_string() 11140 .into_bytes(); 11141 assert_eq!( 11142 serde_json::from_str::<Registration>( 11143 serde_json::json!({ 11144 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11145 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11146 "response": { 11147 "clientDataJSON": b64_cdata_json, 11148 "authenticatorData": b64_adata, 11149 "transports": [], 11150 "publicKey": b64_key, 11151 "publicKeyAlgorithm": null, 11152 "attestationObject": b64_aobj, 11153 }, 11154 "clientExtensionResults": {}, 11155 "type": "public-key" 11156 }) 11157 .to_string() 11158 .as_str() 11159 ) 11160 .unwrap_err() 11161 .to_string() 11162 .into_bytes() 11163 .get(..err.len()), 11164 Some(err.as_slice()) 11165 ); 11166 // `publicKey` mismatch. 11167 let bad_pub_key = MlDsaVerKey::<MlDsa44>::decode(&[2; 1312].into()); 11168 err = Error::invalid_value( 11169 Unexpected::Bytes([0; 32].as_slice()), 11170 &format!( 11171 "DER-encoded public key to match the public key within the attestation object: MlDsa44(MlDsa44PubKey({:?}))", 11172 &[1u8; 1312] 11173 ) 11174 .as_str(), 11175 ) 11176 .to_string().into_bytes(); 11177 assert_eq!(serde_json::from_str::<Registration>( 11178 serde_json::json!({ 11179 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11180 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11181 "response": { 11182 "clientDataJSON": b64_cdata_json, 11183 "authenticatorData": b64_adata, 11184 "transports": [], 11185 "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()), 11186 "publicKeyAlgorithm": -48i8, 11187 "attestationObject": b64_aobj, 11188 }, 11189 "clientExtensionResults": {}, 11190 "type": "public-key" 11191 }) 11192 .to_string() 11193 .as_str() 11194 ) 11195 .unwrap_err().to_string().into_bytes().get(..err.len()), 11196 Some(err.as_slice()) 11197 ); 11198 // Missing `publicKey` is allowed when not using EdDSA, ES256, or RS256. 11199 assert!( 11200 serde_json::from_str::<Registration>( 11201 serde_json::json!({ 11202 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11203 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11204 "response": { 11205 "clientDataJSON": b64_cdata_json, 11206 "authenticatorData": b64_adata, 11207 "transports": [], 11208 "publicKeyAlgorithm": -48i8, 11209 "attestationObject": b64_aobj, 11210 }, 11211 "clientExtensionResults": {}, 11212 "type": "public-key" 11213 }) 11214 .to_string() 11215 .as_str() 11216 ) 11217 .is_ok() 11218 ); 11219 // `publicKeyAlgorithm` mismatch when `publicKey` does not exist. 11220 err = Error::invalid_value( 11221 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 11222 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa44).as_str() 11223 ) 11224 .to_string().into_bytes(); 11225 assert_eq!( 11226 serde_json::from_str::<Registration>( 11227 serde_json::json!({ 11228 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11229 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11230 "response": { 11231 "clientDataJSON": b64_cdata_json, 11232 "authenticatorData": b64_adata, 11233 "transports": [], 11234 "publicKeyAlgorithm": -7i8, 11235 "attestationObject": b64_aobj, 11236 }, 11237 "clientExtensionResults": {}, 11238 "type": "public-key" 11239 }) 11240 .to_string() 11241 .as_str() 11242 ) 11243 .unwrap_err() 11244 .to_string() 11245 .into_bytes() 11246 .get(..err.len()), 11247 Some(err.as_slice()) 11248 ); 11249 // `null` `publicKey` is allowed when not using EdDSA, ES256, or RS256. 11250 assert!( 11251 serde_json::from_str::<Registration>( 11252 serde_json::json!({ 11253 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11254 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11255 "response": { 11256 "clientDataJSON": b64_cdata_json, 11257 "authenticatorData": b64_adata, 11258 "transports": [], 11259 "publicKey": null, 11260 "publicKeyAlgorithm": -48i8, 11261 "attestationObject": b64_aobj, 11262 }, 11263 "clientExtensionResults": {}, 11264 "type": "public-key" 11265 }) 11266 .to_string() 11267 .as_str() 11268 ) 11269 .is_ok() 11270 ); 11271 // `publicKeyAlgorithm` mismatch when `publicKey` is null. 11272 err = Error::invalid_value( 11273 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 11274 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa44).as_str() 11275 ) 11276 .to_string().into_bytes(); 11277 assert_eq!( 11278 serde_json::from_str::<Registration>( 11279 serde_json::json!({ 11280 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11281 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11282 "response": { 11283 "clientDataJSON": b64_cdata_json, 11284 "authenticatorData": b64_adata, 11285 "transports": [], 11286 "publicKey": null, 11287 "publicKeyAlgorithm": -7i8, 11288 "attestationObject": b64_aobj, 11289 }, 11290 "clientExtensionResults": {}, 11291 "type": "public-key" 11292 }) 11293 .to_string() 11294 .as_str() 11295 ) 11296 .unwrap_err() 11297 .to_string() 11298 .into_bytes() 11299 .get(..err.len()), 11300 Some(err.as_slice()) 11301 ); 11302 } 11303 #[expect(clippy::unwrap_used, reason = "OK in tests")] 11304 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 11305 #[expect(clippy::too_many_lines, reason = "a lot to test")] 11306 #[test] 11307 fn es256_registration_deserialize_data_mismatch() { 11308 let c_data_json = serde_json::json!({}).to_string(); 11309 let mut att_obj: [u8; 178] = [ 11310 cbor::MAP_3, 11311 cbor::TEXT_3, 11312 b'f', 11313 b'm', 11314 b't', 11315 cbor::TEXT_4, 11316 b'n', 11317 b'o', 11318 b'n', 11319 b'e', 11320 cbor::TEXT_7, 11321 b'a', 11322 b't', 11323 b't', 11324 b'S', 11325 b't', 11326 b'm', 11327 b't', 11328 cbor::MAP_0, 11329 cbor::TEXT_8, 11330 b'a', 11331 b'u', 11332 b't', 11333 b'h', 11334 b'D', 11335 b'a', 11336 b't', 11337 b'a', 11338 cbor::BYTES_INFO_24, 11339 148, 11340 // `rpIdHash`. 11341 0, 11342 0, 11343 0, 11344 0, 11345 0, 11346 0, 11347 0, 11348 0, 11349 0, 11350 0, 11351 0, 11352 0, 11353 0, 11354 0, 11355 0, 11356 0, 11357 0, 11358 0, 11359 0, 11360 0, 11361 0, 11362 0, 11363 0, 11364 0, 11365 0, 11366 0, 11367 0, 11368 0, 11369 0, 11370 0, 11371 0, 11372 0, 11373 // `flags`. 11374 0b0100_0101, 11375 // `signCount`. 11376 0, 11377 0, 11378 0, 11379 0, 11380 // `aaguid`. 11381 0, 11382 0, 11383 0, 11384 0, 11385 0, 11386 0, 11387 0, 11388 0, 11389 0, 11390 0, 11391 0, 11392 0, 11393 0, 11394 0, 11395 0, 11396 0, 11397 // `credentialIdLength`. 11398 0, 11399 16, 11400 // `credentialId`. 11401 0, 11402 0, 11403 0, 11404 0, 11405 0, 11406 0, 11407 0, 11408 0, 11409 0, 11410 0, 11411 0, 11412 0, 11413 0, 11414 0, 11415 0, 11416 0, 11417 // P-256 COSE key. 11418 cbor::MAP_5, 11419 KTY, 11420 EC2, 11421 ALG, 11422 ES256, 11423 // `crv`. 11424 cbor::NEG_ONE, 11425 // `P-256`. 11426 cbor::ONE, 11427 // `x`. 11428 cbor::NEG_TWO, 11429 cbor::BYTES_INFO_24, 11430 32, 11431 // x-coordinate. This will be overwritten later. 11432 0, 11433 0, 11434 0, 11435 0, 11436 0, 11437 0, 11438 0, 11439 0, 11440 0, 11441 0, 11442 0, 11443 0, 11444 0, 11445 0, 11446 0, 11447 0, 11448 0, 11449 0, 11450 0, 11451 0, 11452 0, 11453 0, 11454 0, 11455 0, 11456 0, 11457 0, 11458 0, 11459 0, 11460 0, 11461 0, 11462 0, 11463 0, 11464 // `y`. 11465 cbor::NEG_THREE, 11466 cbor::BYTES_INFO_24, 11467 32, 11468 // y-coordinate. This will be overwritten later. 11469 0, 11470 0, 11471 0, 11472 0, 11473 0, 11474 0, 11475 0, 11476 0, 11477 0, 11478 0, 11479 0, 11480 0, 11481 0, 11482 0, 11483 0, 11484 0, 11485 0, 11486 0, 11487 0, 11488 0, 11489 0, 11490 0, 11491 0, 11492 0, 11493 0, 11494 0, 11495 0, 11496 0, 11497 0, 11498 0, 11499 0, 11500 0, 11501 ]; 11502 let key = P256Key::from_bytes( 11503 &[ 11504 137, 133, 36, 206, 163, 47, 255, 5, 76, 144, 163, 141, 40, 109, 108, 240, 246, 115, 11505 178, 237, 169, 68, 6, 129, 92, 21, 238, 127, 55, 158, 207, 95, 11506 ] 11507 .into(), 11508 ) 11509 .unwrap() 11510 .public_key(); 11511 let enc_key = key.to_sec1_point(false); 11512 let pub_key = key.to_public_key_der().unwrap(); 11513 let att_obj_len = att_obj.len(); 11514 let x_start = att_obj_len - 67; 11515 let y_meta_start = x_start + 32; 11516 let y_start = y_meta_start + 3; 11517 att_obj[x_start..y_meta_start].copy_from_slice(enc_key.x().unwrap()); 11518 att_obj[y_start..].copy_from_slice(enc_key.y().unwrap()); 11519 let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes()); 11520 let b64_adata = base64url_nopad::encode(&att_obj[att_obj.len() - 148..]); 11521 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 11522 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 11523 // Base case is valid. 11524 assert!( 11525 serde_json::from_str::<Registration>( 11526 serde_json::json!({ 11527 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11528 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11529 "response": { 11530 "clientDataJSON": b64_cdata_json, 11531 "authenticatorData": b64_adata, 11532 "transports": [], 11533 "publicKey": b64_key, 11534 "publicKeyAlgorithm": -7i8, 11535 "attestationObject": b64_aobj, 11536 }, 11537 "clientExtensionResults": {}, 11538 "type": "public-key" 11539 }) 11540 .to_string() 11541 .as_str() 11542 ) 11543 .is_ok_and( 11544 |reg| reg.response.client_data_json == c_data_json.as_bytes() 11545 && reg.response.attestation_object_and_c_data_hash[..att_obj.len()] == att_obj 11546 && reg.response.attestation_object_and_c_data_hash[att_obj.len()..] 11547 == *Sha256::digest(c_data_json.as_bytes()) 11548 && reg.response.transports.is_empty() 11549 && matches!(reg.authenticator_attachment, AuthenticatorAttachment::None) 11550 && reg.client_extension_results.cred_props.is_none() 11551 && reg.client_extension_results.prf.is_none() 11552 ) 11553 ); 11554 // `publicKeyAlgorithm` mismatch. 11555 let mut err = Error::invalid_value( 11556 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()), 11557 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es256).as_str() 11558 ) 11559 .to_string().into_bytes(); 11560 assert_eq!( 11561 serde_json::from_str::<Registration>( 11562 serde_json::json!({ 11563 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11564 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11565 "response": { 11566 "clientDataJSON": b64_cdata_json, 11567 "authenticatorData": b64_adata, 11568 "transports": [], 11569 "publicKey": b64_key, 11570 "publicKeyAlgorithm": -8i8, 11571 "attestationObject": b64_aobj, 11572 }, 11573 "clientExtensionResults": {}, 11574 "type": "public-key" 11575 }) 11576 .to_string() 11577 .as_str() 11578 ) 11579 .unwrap_err() 11580 .to_string() 11581 .into_bytes() 11582 .get(..err.len()), 11583 Some(err.as_slice()) 11584 ); 11585 // Missing `publicKeyAlgorithm`. 11586 err = Error::missing_field("publicKeyAlgorithm") 11587 .to_string() 11588 .into_bytes(); 11589 assert_eq!( 11590 serde_json::from_str::<Registration>( 11591 serde_json::json!({ 11592 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11593 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11594 "response": { 11595 "clientDataJSON": b64_cdata_json, 11596 "authenticatorData": b64_adata, 11597 "transports": [], 11598 "publicKey": b64_key, 11599 "attestationObject": b64_aobj, 11600 }, 11601 "clientExtensionResults": {}, 11602 "type": "public-key" 11603 }) 11604 .to_string() 11605 .as_str() 11606 ) 11607 .unwrap_err() 11608 .to_string() 11609 .into_bytes() 11610 .get(..err.len()), 11611 Some(err.as_slice()) 11612 ); 11613 // `null` `publicKeyAlgorithm`. 11614 err = Error::invalid_type(Unexpected::Other("null"), &"publicKeyAlgorithm") 11615 .to_string() 11616 .into_bytes(); 11617 assert_eq!( 11618 serde_json::from_str::<Registration>( 11619 serde_json::json!({ 11620 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11621 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11622 "response": { 11623 "clientDataJSON": b64_cdata_json, 11624 "authenticatorData": b64_adata, 11625 "transports": [], 11626 "publicKey": b64_key, 11627 "publicKeyAlgorithm": null, 11628 "attestationObject": b64_aobj, 11629 }, 11630 "clientExtensionResults": {}, 11631 "type": "public-key" 11632 }) 11633 .to_string() 11634 .as_str() 11635 ) 11636 .unwrap_err() 11637 .to_string() 11638 .into_bytes() 11639 .get(..err.len()), 11640 Some(err.as_slice()) 11641 ); 11642 // `publicKey` mismatch. 11643 let bad_pub_key = P256PubKey::from_sec1_point(&P256Pt::from_affine_coordinates( 11644 &[ 11645 66, 71, 188, 41, 125, 2, 226, 44, 148, 62, 63, 190, 172, 64, 33, 214, 6, 37, 148, 11646 23, 240, 235, 203, 84, 112, 219, 232, 197, 54, 182, 17, 235, 11647 ] 11648 .into(), 11649 &[ 11650 22, 172, 123, 13, 170, 242, 217, 248, 193, 209, 206, 163, 92, 4, 162, 168, 113, 63, 11651 2, 117, 16, 223, 239, 196, 109, 179, 10, 130, 43, 213, 205, 92, 11652 ] 11653 .into(), 11654 false, 11655 )) 11656 .unwrap(); 11657 err = Error::invalid_value( 11658 Unexpected::Bytes([0; 32].as_slice()), 11659 &format!( 11660 "DER-encoded public key to match the public key within the attestation object: P256(UncompressedP256PubKey({:?}, {:?}))", 11661 &att_obj[x_start..y_meta_start], 11662 &att_obj[y_start..], 11663 ) 11664 .as_str(), 11665 ) 11666 .to_string().into_bytes(); 11667 assert_eq!(serde_json::from_str::<Registration>( 11668 serde_json::json!({ 11669 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11670 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11671 "response": { 11672 "clientDataJSON": b64_cdata_json, 11673 "authenticatorData": b64_adata, 11674 "transports": [], 11675 "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()), 11676 "publicKeyAlgorithm": -7i8, 11677 "attestationObject": b64_aobj, 11678 }, 11679 "clientExtensionResults": {}, 11680 "type": "public-key" 11681 }) 11682 .to_string() 11683 .as_str() 11684 ) 11685 .unwrap_err().to_string().into_bytes().get(..err.len()), 11686 Some(err.as_slice()) 11687 ); 11688 // Missing `publicKey` when using EdDSA, ES256, or RS256. 11689 err = Error::missing_field("publicKey").to_string().into_bytes(); 11690 assert_eq!( 11691 serde_json::from_str::<Registration>( 11692 serde_json::json!({ 11693 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11694 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11695 "response": { 11696 "clientDataJSON": b64_cdata_json, 11697 "authenticatorData": b64_adata, 11698 "transports": [], 11699 "publicKeyAlgorithm": -7i8, 11700 "attestationObject": b64_aobj, 11701 }, 11702 "clientExtensionResults": {}, 11703 "type": "public-key" 11704 }) 11705 .to_string() 11706 .as_str() 11707 ) 11708 .unwrap_err() 11709 .to_string() 11710 .into_bytes() 11711 .get(..err.len()), 11712 Some(err.as_slice()) 11713 ); 11714 // `null` `publicKey` when using EdDSA, ES256, or RS256. 11715 err = Error::invalid_type(Unexpected::Other("null"), &"publicKey") 11716 .to_string() 11717 .into_bytes(); 11718 assert_eq!( 11719 serde_json::from_str::<Registration>( 11720 serde_json::json!({ 11721 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11722 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11723 "response": { 11724 "clientDataJSON": b64_cdata_json, 11725 "authenticatorData": b64_adata, 11726 "transports": [], 11727 "publicKey": null, 11728 "publicKeyAlgorithm": -7i8, 11729 "attestationObject": b64_aobj, 11730 }, 11731 "clientExtensionResults": {}, 11732 "type": "public-key" 11733 }) 11734 .to_string() 11735 .as_str() 11736 ) 11737 .unwrap_err() 11738 .to_string() 11739 .into_bytes() 11740 .get(..err.len()), 11741 Some(err.as_slice()) 11742 ); 11743 } 11744 #[expect( 11745 clippy::assertions_on_result_states, 11746 clippy::unwrap_used, 11747 reason = "OK in tests" 11748 )] 11749 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 11750 #[expect(clippy::too_many_lines, reason = "a lot to test")] 11751 #[test] 11752 fn es384_registration_deserialize_data_mismatch() { 11753 let c_data_json = serde_json::json!({}).to_string(); 11754 let mut att_obj: [u8; 211] = [ 11755 cbor::MAP_3, 11756 cbor::TEXT_3, 11757 b'f', 11758 b'm', 11759 b't', 11760 cbor::TEXT_4, 11761 b'n', 11762 b'o', 11763 b'n', 11764 b'e', 11765 cbor::TEXT_7, 11766 b'a', 11767 b't', 11768 b't', 11769 b'S', 11770 b't', 11771 b'm', 11772 b't', 11773 cbor::MAP_0, 11774 cbor::TEXT_8, 11775 b'a', 11776 b'u', 11777 b't', 11778 b'h', 11779 b'D', 11780 b'a', 11781 b't', 11782 b'a', 11783 cbor::BYTES_INFO_24, 11784 181, 11785 // `rpIdHash`. 11786 0, 11787 0, 11788 0, 11789 0, 11790 0, 11791 0, 11792 0, 11793 0, 11794 0, 11795 0, 11796 0, 11797 0, 11798 0, 11799 0, 11800 0, 11801 0, 11802 0, 11803 0, 11804 0, 11805 0, 11806 0, 11807 0, 11808 0, 11809 0, 11810 0, 11811 0, 11812 0, 11813 0, 11814 0, 11815 0, 11816 0, 11817 0, 11818 // `flags`. 11819 0b0100_0101, 11820 // `signCount`. 11821 0, 11822 0, 11823 0, 11824 0, 11825 // `aaguid`. 11826 0, 11827 0, 11828 0, 11829 0, 11830 0, 11831 0, 11832 0, 11833 0, 11834 0, 11835 0, 11836 0, 11837 0, 11838 0, 11839 0, 11840 0, 11841 0, 11842 // `credentialIdLength`. 11843 0, 11844 16, 11845 // `credentialId`. 11846 0, 11847 0, 11848 0, 11849 0, 11850 0, 11851 0, 11852 0, 11853 0, 11854 0, 11855 0, 11856 0, 11857 0, 11858 0, 11859 0, 11860 0, 11861 0, 11862 // P-384 COSE key. 11863 cbor::MAP_5, 11864 KTY, 11865 EC2, 11866 ALG, 11867 cbor::NEG_INFO_24, 11868 ES384, 11869 // `crv`. 11870 cbor::NEG_ONE, 11871 // `P-384`. 11872 cbor::TWO, 11873 // `x`. 11874 cbor::NEG_TWO, 11875 cbor::BYTES_INFO_24, 11876 48, 11877 // x-coordinate. This will be overwritten later. 11878 0, 11879 0, 11880 0, 11881 0, 11882 0, 11883 0, 11884 0, 11885 0, 11886 0, 11887 0, 11888 0, 11889 0, 11890 0, 11891 0, 11892 0, 11893 0, 11894 0, 11895 0, 11896 0, 11897 0, 11898 0, 11899 0, 11900 0, 11901 0, 11902 0, 11903 0, 11904 0, 11905 0, 11906 0, 11907 0, 11908 0, 11909 0, 11910 0, 11911 0, 11912 0, 11913 0, 11914 0, 11915 0, 11916 0, 11917 0, 11918 0, 11919 0, 11920 0, 11921 0, 11922 0, 11923 0, 11924 0, 11925 0, 11926 // `y`. 11927 cbor::NEG_THREE, 11928 cbor::BYTES_INFO_24, 11929 48, 11930 // y-coordinate. This will be overwritten later. 11931 0, 11932 0, 11933 0, 11934 0, 11935 0, 11936 0, 11937 0, 11938 0, 11939 0, 11940 0, 11941 0, 11942 0, 11943 0, 11944 0, 11945 0, 11946 0, 11947 0, 11948 0, 11949 0, 11950 0, 11951 0, 11952 0, 11953 0, 11954 0, 11955 0, 11956 0, 11957 0, 11958 0, 11959 0, 11960 0, 11961 0, 11962 0, 11963 0, 11964 0, 11965 0, 11966 0, 11967 0, 11968 0, 11969 0, 11970 0, 11971 0, 11972 0, 11973 0, 11974 0, 11975 0, 11976 0, 11977 0, 11978 0, 11979 ]; 11980 let key = P384Key::from_bytes( 11981 &[ 11982 158, 99, 156, 49, 190, 211, 85, 167, 28, 2, 80, 57, 31, 22, 17, 38, 85, 78, 232, 11983 42, 45, 199, 154, 243, 136, 251, 84, 34, 5, 120, 208, 91, 61, 248, 64, 144, 87, 1, 11984 32, 86, 220, 68, 182, 11, 105, 223, 75, 70, 11985 ] 11986 .into(), 11987 ) 11988 .unwrap() 11989 .public_key(); 11990 let enc_key = key.to_sec1_point(false); 11991 let pub_key = key.to_public_key_der().unwrap(); 11992 let att_obj_len = att_obj.len(); 11993 let x_start = att_obj_len - 99; 11994 let y_meta_start = x_start + 48; 11995 let y_start = y_meta_start + 3; 11996 att_obj[x_start..y_meta_start].copy_from_slice(enc_key.x().unwrap()); 11997 att_obj[y_start..].copy_from_slice(enc_key.y().unwrap()); 11998 let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes()); 11999 let b64_adata = base64url_nopad::encode(&att_obj[att_obj_len - 181..]); 12000 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 12001 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 12002 // Base case is valid. 12003 assert!( 12004 serde_json::from_str::<Registration>( 12005 serde_json::json!({ 12006 "id": "AAAAAAAAAAAAAAAAAAAAAA", 12007 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 12008 "response": { 12009 "clientDataJSON": b64_cdata_json, 12010 "authenticatorData": b64_adata, 12011 "transports": [], 12012 "publicKey": b64_key, 12013 "publicKeyAlgorithm": -35i8, 12014 "attestationObject": b64_aobj, 12015 }, 12016 "clientExtensionResults": {}, 12017 "type": "public-key" 12018 }) 12019 .to_string() 12020 .as_str() 12021 ) 12022 .is_ok_and( 12023 |reg| reg.response.client_data_json == c_data_json.as_bytes() 12024 && reg.response.attestation_object_and_c_data_hash[..att_obj.len()] == att_obj 12025 && reg.response.attestation_object_and_c_data_hash[att_obj.len()..] 12026 == *Sha256::digest(c_data_json.as_bytes()) 12027 && reg.response.transports.is_empty() 12028 && matches!(reg.authenticator_attachment, AuthenticatorAttachment::None) 12029 && reg.client_extension_results.cred_props.is_none() 12030 && reg.client_extension_results.prf.is_none() 12031 ) 12032 ); 12033 // `publicKeyAlgorithm` mismatch. 12034 let mut err = Error::invalid_value( 12035 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 12036 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es384).as_str() 12037 ) 12038 .to_string().into_bytes(); 12039 assert_eq!( 12040 serde_json::from_str::<Registration>( 12041 serde_json::json!({ 12042 "id": "AAAAAAAAAAAAAAAAAAAAAA", 12043 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 12044 "response": { 12045 "clientDataJSON": b64_cdata_json, 12046 "authenticatorData": b64_adata, 12047 "transports": [], 12048 "publicKey": b64_key, 12049 "publicKeyAlgorithm": -7i8, 12050 "attestationObject": b64_aobj, 12051 }, 12052 "clientExtensionResults": {}, 12053 "type": "public-key" 12054 }) 12055 .to_string() 12056 .as_str() 12057 ) 12058 .unwrap_err() 12059 .to_string() 12060 .into_bytes() 12061 .get(..err.len()), 12062 Some(err.as_slice()) 12063 ); 12064 // Missing `publicKeyAlgorithm`. 12065 err = Error::missing_field("publicKeyAlgorithm") 12066 .to_string() 12067 .into_bytes(); 12068 assert_eq!( 12069 serde_json::from_str::<Registration>( 12070 serde_json::json!({ 12071 "id": "AAAAAAAAAAAAAAAAAAAAAA", 12072 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 12073 "response": { 12074 "clientDataJSON": b64_cdata_json, 12075 "authenticatorData": b64_adata, 12076 "transports": [], 12077 "publicKey": b64_key, 12078 "attestationObject": b64_aobj, 12079 }, 12080 "clientExtensionResults": {}, 12081 "type": "public-key" 12082 }) 12083 .to_string() 12084 .as_str() 12085 ) 12086 .unwrap_err() 12087 .to_string() 12088 .into_bytes() 12089 .get(..err.len()), 12090 Some(err.as_slice()) 12091 ); 12092 // `null` `publicKeyAlgorithm`. 12093 err = Error::invalid_type(Unexpected::Other("null"), &"publicKeyAlgorithm") 12094 .to_string() 12095 .into_bytes(); 12096 assert_eq!( 12097 serde_json::from_str::<Registration>( 12098 serde_json::json!({ 12099 "id": "AAAAAAAAAAAAAAAAAAAAAA", 12100 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 12101 "response": { 12102 "clientDataJSON": b64_cdata_json, 12103 "authenticatorData": b64_adata, 12104 "transports": [], 12105 "publicKey": b64_key, 12106 "publicKeyAlgorithm": null, 12107 "attestationObject": b64_aobj, 12108 }, 12109 "clientExtensionResults": {}, 12110 "type": "public-key" 12111 }) 12112 .to_string() 12113 .as_str() 12114 ) 12115 .unwrap_err() 12116 .to_string() 12117 .into_bytes() 12118 .get(..err.len()), 12119 Some(err.as_slice()) 12120 ); 12121 // `publicKey` mismatch. 12122 let bad_pub_key = P384PubKey::from_sec1_point(&P384Pt::from_affine_coordinates( 12123 &[ 12124 192, 10, 27, 46, 66, 67, 80, 98, 33, 230, 156, 95, 1, 135, 150, 110, 64, 243, 22, 12125 118, 5, 255, 107, 44, 234, 111, 217, 105, 125, 114, 39, 7, 126, 2, 191, 111, 48, 12126 93, 234, 175, 18, 172, 59, 28, 97, 106, 178, 152, 12127 ] 12128 .into(), 12129 &[ 12130 57, 36, 196, 12, 109, 129, 253, 115, 88, 154, 6, 43, 195, 85, 169, 5, 230, 51, 28, 12131 205, 142, 28, 150, 35, 24, 222, 170, 253, 14, 248, 84, 151, 109, 191, 152, 111, 12132 222, 70, 134, 247, 109, 171, 211, 33, 214, 217, 200, 111, 12133 ] 12134 .into(), 12135 false, 12136 )) 12137 .unwrap(); 12138 err = Error::invalid_value( 12139 Unexpected::Bytes([0; 32].as_slice()), 12140 &format!( 12141 "DER-encoded public key to match the public key within the attestation object: P384(UncompressedP384PubKey({:?}, {:?}))", 12142 &att_obj[x_start..y_meta_start], 12143 &att_obj[y_start..], 12144 ) 12145 .as_str(), 12146 ) 12147 .to_string().into_bytes(); 12148 assert_eq!(serde_json::from_str::<Registration>( 12149 serde_json::json!({ 12150 "id": "AAAAAAAAAAAAAAAAAAAAAA", 12151 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 12152 "response": { 12153 "clientDataJSON": b64_cdata_json, 12154 "authenticatorData": b64_adata, 12155 "transports": [], 12156 "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()), 12157 "publicKeyAlgorithm": -35i8, 12158 "attestationObject": b64_aobj, 12159 }, 12160 "clientExtensionResults": {}, 12161 "type": "public-key" 12162 }) 12163 .to_string() 12164 .as_str() 12165 ) 12166 .unwrap_err().to_string().into_bytes().get(..err.len()), 12167 Some(err.as_slice()) 12168 ); 12169 // Missing `publicKey` is allowed when not using EdDSA, ES256, or RS256. 12170 assert!( 12171 serde_json::from_str::<Registration>( 12172 serde_json::json!({ 12173 "id": "AAAAAAAAAAAAAAAAAAAAAA", 12174 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 12175 "response": { 12176 "clientDataJSON": b64_cdata_json, 12177 "authenticatorData": b64_adata, 12178 "transports": [], 12179 "publicKeyAlgorithm": -35i8, 12180 "attestationObject": b64_aobj, 12181 }, 12182 "clientExtensionResults": {}, 12183 "type": "public-key" 12184 }) 12185 .to_string() 12186 .as_str() 12187 ) 12188 .is_ok() 12189 ); 12190 // `publicKeyAlgorithm` mismatch when `publicKey` does not exist. 12191 err = Error::invalid_value( 12192 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 12193 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es384).as_str() 12194 ) 12195 .to_string().into_bytes(); 12196 assert_eq!( 12197 serde_json::from_str::<Registration>( 12198 serde_json::json!({ 12199 "id": "AAAAAAAAAAAAAAAAAAAAAA", 12200 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 12201 "response": { 12202 "clientDataJSON": b64_cdata_json, 12203 "authenticatorData": b64_adata, 12204 "transports": [], 12205 "publicKeyAlgorithm": -7i8, 12206 "attestationObject": b64_aobj, 12207 }, 12208 "clientExtensionResults": {}, 12209 "type": "public-key" 12210 }) 12211 .to_string() 12212 .as_str() 12213 ) 12214 .unwrap_err() 12215 .to_string() 12216 .into_bytes() 12217 .get(..err.len()), 12218 Some(err.as_slice()) 12219 ); 12220 // `null` `publicKey` is allowed when not using EdDSA, ES256, or RS256. 12221 assert!( 12222 serde_json::from_str::<Registration>( 12223 serde_json::json!({ 12224 "id": "AAAAAAAAAAAAAAAAAAAAAA", 12225 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 12226 "response": { 12227 "clientDataJSON": b64_cdata_json, 12228 "authenticatorData": b64_adata, 12229 "transports": [], 12230 "publicKey": null, 12231 "publicKeyAlgorithm": -35i8, 12232 "attestationObject": b64_aobj, 12233 }, 12234 "clientExtensionResults": {}, 12235 "type": "public-key" 12236 }) 12237 .to_string() 12238 .as_str() 12239 ) 12240 .is_ok() 12241 ); 12242 // `publicKeyAlgorithm` mismatch when `publicKey` is null. 12243 err = Error::invalid_value( 12244 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 12245 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es384).as_str() 12246 ) 12247 .to_string().into_bytes(); 12248 assert_eq!( 12249 serde_json::from_str::<Registration>( 12250 serde_json::json!({ 12251 "id": "AAAAAAAAAAAAAAAAAAAAAA", 12252 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 12253 "response": { 12254 "clientDataJSON": b64_cdata_json, 12255 "authenticatorData": b64_adata, 12256 "transports": [], 12257 "publicKey": null, 12258 "publicKeyAlgorithm": -7i8, 12259 "attestationObject": b64_aobj, 12260 }, 12261 "clientExtensionResults": {}, 12262 "type": "public-key" 12263 }) 12264 .to_string() 12265 .as_str() 12266 ) 12267 .unwrap_err() 12268 .to_string() 12269 .into_bytes() 12270 .get(..err.len()), 12271 Some(err.as_slice()) 12272 ); 12273 } 12274 #[expect(clippy::unwrap_used, reason = "OK in tests")] 12275 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 12276 #[expect(clippy::too_many_lines, reason = "a lot to test")] 12277 #[test] 12278 fn rs256_registration_deserialize_data_mismatch() { 12279 let c_data_json = serde_json::json!({}).to_string(); 12280 let mut att_obj: [u8; 374] = [ 12281 cbor::MAP_3, 12282 cbor::TEXT_3, 12283 b'f', 12284 b'm', 12285 b't', 12286 cbor::TEXT_4, 12287 b'n', 12288 b'o', 12289 b'n', 12290 b'e', 12291 cbor::TEXT_7, 12292 b'a', 12293 b't', 12294 b't', 12295 b'S', 12296 b't', 12297 b'm', 12298 b't', 12299 cbor::MAP_0, 12300 cbor::TEXT_8, 12301 b'a', 12302 b'u', 12303 b't', 12304 b'h', 12305 b'D', 12306 b'a', 12307 b't', 12308 b'a', 12309 cbor::BYTES_INFO_25, 12310 1, 12311 87, 12312 // `rpIdHash`. 12313 0, 12314 0, 12315 0, 12316 0, 12317 0, 12318 0, 12319 0, 12320 0, 12321 0, 12322 0, 12323 0, 12324 0, 12325 0, 12326 0, 12327 0, 12328 0, 12329 0, 12330 0, 12331 0, 12332 0, 12333 0, 12334 0, 12335 0, 12336 0, 12337 0, 12338 0, 12339 0, 12340 0, 12341 0, 12342 0, 12343 0, 12344 0, 12345 // `flags`. 12346 0b0100_0101, 12347 // `signCount`. 12348 0, 12349 0, 12350 0, 12351 0, 12352 // `aaguid`. 12353 0, 12354 0, 12355 0, 12356 0, 12357 0, 12358 0, 12359 0, 12360 0, 12361 0, 12362 0, 12363 0, 12364 0, 12365 0, 12366 0, 12367 0, 12368 0, 12369 // `credentialIdLength`. 12370 0, 12371 16, 12372 // `credentialId`. 12373 0, 12374 0, 12375 0, 12376 0, 12377 0, 12378 0, 12379 0, 12380 0, 12381 0, 12382 0, 12383 0, 12384 0, 12385 0, 12386 0, 12387 0, 12388 0, 12389 // RSA COSE key. 12390 cbor::MAP_4, 12391 KTY, 12392 RSA, 12393 ALG, 12394 cbor::NEG_INFO_25, 12395 // RS256. 12396 1, 12397 0, 12398 // `n`. 12399 cbor::NEG_ONE, 12400 cbor::BYTES_INFO_25, 12401 1, 12402 0, 12403 // n. This will be overwritten later. 12404 0, 12405 0, 12406 0, 12407 0, 12408 0, 12409 0, 12410 0, 12411 0, 12412 0, 12413 0, 12414 0, 12415 0, 12416 0, 12417 0, 12418 0, 12419 0, 12420 0, 12421 0, 12422 0, 12423 0, 12424 0, 12425 0, 12426 0, 12427 0, 12428 0, 12429 0, 12430 0, 12431 0, 12432 0, 12433 0, 12434 0, 12435 0, 12436 0, 12437 0, 12438 0, 12439 0, 12440 0, 12441 0, 12442 0, 12443 0, 12444 0, 12445 0, 12446 0, 12447 0, 12448 0, 12449 0, 12450 0, 12451 0, 12452 0, 12453 0, 12454 0, 12455 0, 12456 0, 12457 0, 12458 0, 12459 0, 12460 0, 12461 0, 12462 0, 12463 0, 12464 0, 12465 0, 12466 0, 12467 0, 12468 0, 12469 0, 12470 0, 12471 0, 12472 0, 12473 0, 12474 0, 12475 0, 12476 0, 12477 0, 12478 0, 12479 0, 12480 0, 12481 0, 12482 0, 12483 0, 12484 0, 12485 0, 12486 0, 12487 0, 12488 0, 12489 0, 12490 0, 12491 0, 12492 0, 12493 0, 12494 0, 12495 0, 12496 0, 12497 0, 12498 0, 12499 0, 12500 0, 12501 0, 12502 0, 12503 0, 12504 0, 12505 0, 12506 0, 12507 0, 12508 0, 12509 0, 12510 0, 12511 0, 12512 0, 12513 0, 12514 0, 12515 0, 12516 0, 12517 0, 12518 0, 12519 0, 12520 0, 12521 0, 12522 0, 12523 0, 12524 0, 12525 0, 12526 0, 12527 0, 12528 0, 12529 0, 12530 0, 12531 0, 12532 0, 12533 0, 12534 0, 12535 0, 12536 0, 12537 0, 12538 0, 12539 0, 12540 0, 12541 0, 12542 0, 12543 0, 12544 0, 12545 0, 12546 0, 12547 0, 12548 0, 12549 0, 12550 0, 12551 0, 12552 0, 12553 0, 12554 0, 12555 0, 12556 0, 12557 0, 12558 0, 12559 0, 12560 0, 12561 0, 12562 0, 12563 0, 12564 0, 12565 0, 12566 0, 12567 0, 12568 0, 12569 0, 12570 0, 12571 0, 12572 0, 12573 0, 12574 0, 12575 0, 12576 0, 12577 0, 12578 0, 12579 0, 12580 0, 12581 0, 12582 0, 12583 0, 12584 0, 12585 0, 12586 0, 12587 0, 12588 0, 12589 0, 12590 0, 12591 0, 12592 0, 12593 0, 12594 0, 12595 0, 12596 0, 12597 0, 12598 0, 12599 0, 12600 0, 12601 0, 12602 0, 12603 0, 12604 0, 12605 0, 12606 0, 12607 0, 12608 0, 12609 0, 12610 0, 12611 0, 12612 0, 12613 0, 12614 0, 12615 0, 12616 0, 12617 0, 12618 0, 12619 0, 12620 0, 12621 0, 12622 0, 12623 0, 12624 0, 12625 0, 12626 0, 12627 0, 12628 0, 12629 0, 12630 0, 12631 0, 12632 0, 12633 0, 12634 0, 12635 0, 12636 0, 12637 0, 12638 0, 12639 0, 12640 0, 12641 0, 12642 0, 12643 0, 12644 0, 12645 0, 12646 0, 12647 0, 12648 0, 12649 0, 12650 0, 12651 0, 12652 0, 12653 0, 12654 0, 12655 0, 12656 0, 12657 0, 12658 0, 12659 0, 12660 // `e`. 12661 cbor::NEG_TWO, 12662 cbor::BYTES | 3, 12663 // e. 12664 1, 12665 0, 12666 1, 12667 ]; 12668 let n = [ 12669 111, 183, 124, 133, 38, 167, 70, 148, 44, 50, 30, 60, 121, 14, 38, 37, 96, 114, 107, 12670 195, 248, 64, 79, 36, 237, 140, 43, 27, 94, 74, 102, 152, 135, 102, 184, 150, 186, 206, 12671 185, 19, 165, 209, 48, 98, 98, 9, 3, 205, 208, 82, 250, 105, 132, 201, 73, 62, 60, 165, 12672 100, 128, 153, 9, 41, 118, 66, 95, 236, 214, 73, 135, 197, 68, 184, 10, 27, 116, 204, 12673 145, 50, 174, 58, 42, 183, 181, 119, 232, 126, 252, 217, 96, 162, 190, 103, 122, 64, 12674 87, 145, 45, 32, 207, 17, 239, 223, 3, 35, 14, 112, 119, 124, 141, 123, 208, 239, 105, 12675 81, 217, 151, 162, 190, 17, 88, 182, 176, 158, 81, 200, 42, 166, 133, 48, 23, 236, 55, 12676 117, 248, 233, 151, 203, 122, 155, 231, 46, 177, 20, 20, 151, 64, 222, 239, 226, 7, 21, 12677 254, 81, 202, 64, 232, 161, 235, 22, 51, 246, 207, 213, 0, 229, 138, 46, 222, 205, 157, 12678 108, 139, 253, 230, 80, 50, 2, 122, 212, 163, 100, 180, 114, 12, 113, 52, 56, 99, 188, 12679 42, 198, 212, 23, 182, 222, 56, 221, 200, 79, 96, 239, 221, 135, 10, 17, 106, 183, 56, 12680 104, 68, 94, 198, 196, 35, 200, 83, 204, 26, 185, 204, 212, 31, 183, 19, 111, 233, 13, 12681 72, 93, 53, 65, 111, 59, 242, 122, 160, 244, 162, 126, 38, 235, 156, 47, 88, 39, 132, 12682 153, 79, 0, 133, 78, 7, 218, 165, 241, 12683 ]; 12684 let e = 0x0001_0001u32; 12685 let d = [ 12686 145, 79, 21, 97, 233, 3, 192, 194, 177, 68, 181, 80, 120, 197, 23, 44, 185, 74, 144, 0, 12687 132, 149, 139, 11, 16, 224, 4, 112, 236, 94, 238, 97, 121, 124, 213, 145, 24, 253, 168, 12688 35, 190, 205, 132, 115, 33, 201, 38, 253, 246, 180, 66, 155, 165, 46, 3, 254, 68, 108, 12689 154, 247, 246, 45, 187, 0, 204, 96, 185, 157, 249, 174, 158, 38, 62, 244, 183, 76, 102, 12690 6, 219, 92, 212, 138, 59, 147, 163, 219, 111, 39, 105, 21, 236, 196, 38, 255, 114, 247, 12691 82, 104, 113, 204, 29, 152, 209, 219, 48, 239, 74, 129, 19, 247, 33, 239, 119, 166, 12692 216, 152, 94, 138, 238, 164, 242, 129, 50, 150, 57, 20, 53, 224, 56, 241, 138, 97, 111, 12693 215, 107, 212, 195, 146, 108, 143, 0, 229, 181, 171, 73, 152, 105, 146, 25, 243, 242, 12694 140, 252, 248, 162, 247, 63, 168, 180, 20, 153, 120, 10, 248, 211, 1, 71, 127, 212, 12695 249, 237, 203, 202, 48, 26, 216, 226, 228, 186, 13, 204, 70, 255, 240, 89, 255, 59, 83, 12696 31, 253, 55, 43, 158, 90, 248, 83, 32, 159, 105, 57, 134, 34, 96, 18, 255, 245, 153, 12697 162, 60, 91, 99, 220, 51, 44, 85, 114, 67, 125, 202, 65, 217, 245, 40, 8, 81, 165, 142, 12698 24, 245, 127, 122, 247, 152, 212, 75, 45, 59, 90, 184, 234, 31, 147, 36, 8, 212, 45, 12699 50, 23, 3, 25, 253, 87, 227, 79, 119, 161, 12700 ]; 12701 let p = BoxedUint::from_le_slice_vartime( 12702 [ 12703 215, 166, 5, 21, 11, 179, 41, 77, 198, 92, 165, 48, 77, 162, 42, 41, 206, 141, 60, 12704 69, 47, 164, 19, 92, 46, 72, 100, 238, 100, 53, 214, 197, 163, 185, 6, 140, 229, 12705 250, 195, 77, 8, 12, 5, 236, 178, 173, 86, 201, 43, 213, 165, 51, 108, 101, 161, 12706 99, 76, 240, 14, 234, 76, 197, 137, 53, 198, 168, 135, 205, 212, 198, 120, 29, 16, 12707 82, 98, 233, 236, 177, 12, 171, 141, 100, 107, 146, 33, 176, 125, 202, 172, 79, 12708 147, 179, 30, 62, 247, 206, 169, 19, 168, 114, 26, 73, 108, 178, 105, 84, 89, 191, 12709 168, 253, 228, 214, 54, 16, 212, 199, 111, 72, 3, 41, 247, 227, 165, 244, 32, 188, 12710 24, 247, 12711 ] 12712 .as_slice(), 12713 ); 12714 let p_2 = BoxedUint::from_le_slice_vartime( 12715 [ 12716 41, 25, 198, 240, 134, 206, 121, 57, 11, 5, 134, 192, 212, 77, 229, 197, 14, 78, 12717 85, 212, 190, 114, 179, 188, 21, 171, 174, 12, 104, 74, 15, 164, 136, 173, 62, 177, 12718 141, 213, 93, 102, 147, 83, 59, 124, 146, 59, 175, 213, 55, 27, 25, 248, 154, 29, 12719 39, 85, 50, 235, 134, 60, 203, 106, 186, 195, 190, 185, 71, 169, 142, 236, 92, 11, 12720 250, 187, 198, 8, 201, 184, 120, 178, 227, 87, 63, 243, 89, 227, 234, 184, 28, 252, 12721 112, 211, 193, 69, 23, 92, 5, 72, 93, 53, 69, 159, 73, 160, 105, 244, 249, 94, 214, 12722 173, 9, 236, 4, 255, 129, 11, 224, 140, 252, 168, 57, 143, 176, 241, 60, 219, 90, 12723 250, 12724 ] 12725 .as_slice(), 12726 ); 12727 let key = RsaPrivateKey::from_components( 12728 BoxedUint::from_le_slice_vartime(n.as_slice()), 12729 e.into(), 12730 BoxedUint::from_le_slice_vartime(d.as_slice()), 12731 vec![p, p_2], 12732 ) 12733 .unwrap() 12734 .to_public_key(); 12735 let pub_key = key.to_public_key_der().unwrap(); 12736 let att_obj_len = att_obj.len(); 12737 let n_start_idx = att_obj_len - 261; 12738 let e_meta_start_idx = n_start_idx + 256; 12739 // Correct and won't `panic`. 12740 att_obj[n_start_idx..e_meta_start_idx] 12741 .copy_from_slice(key.n().to_be_bytes_trimmed_vartime().as_ref()); 12742 let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes()); 12743 // Won't `panic`. 12744 let b64_adata = base64url_nopad::encode(&att_obj[31..]); 12745 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 12746 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 12747 // Base case is valid. 12748 assert!( 12749 serde_json::from_str::<Registration>( 12750 serde_json::json!({ 12751 "id": "AAAAAAAAAAAAAAAAAAAAAA", 12752 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 12753 "response": { 12754 "clientDataJSON": b64_cdata_json, 12755 "authenticatorData": b64_adata, 12756 "transports": [], 12757 "publicKey": b64_key, 12758 "publicKeyAlgorithm": -257i16, 12759 "attestationObject": b64_aobj, 12760 }, 12761 "clientExtensionResults": {}, 12762 "type": "public-key" 12763 }) 12764 .to_string() 12765 .as_str() 12766 ) 12767 .is_ok_and( 12768 |reg| reg.response.client_data_json == c_data_json.as_bytes() 12769 && reg.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 12770 && reg.response.attestation_object_and_c_data_hash[att_obj_len..] 12771 == *Sha256::digest(c_data_json.as_bytes()) 12772 && reg.response.transports.is_empty() 12773 && matches!(reg.authenticator_attachment, AuthenticatorAttachment::None) 12774 && reg.client_extension_results.cred_props.is_none() 12775 && reg.client_extension_results.prf.is_none() 12776 ) 12777 ); 12778 // `publicKeyAlgorithm` mismatch. 12779 let mut err = Error::invalid_value( 12780 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()), 12781 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Rs256).as_str() 12782 ) 12783 .to_string().into_bytes(); 12784 assert_eq!( 12785 serde_json::from_str::<Registration>( 12786 serde_json::json!({ 12787 "id": "AAAAAAAAAAAAAAAAAAAAAA", 12788 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 12789 "response": { 12790 "clientDataJSON": b64_cdata_json, 12791 "authenticatorData": b64_adata, 12792 "transports": [], 12793 "publicKey": b64_key, 12794 "publicKeyAlgorithm": -8i8, 12795 "attestationObject": b64_aobj, 12796 }, 12797 "clientExtensionResults": {}, 12798 "type": "public-key" 12799 }) 12800 .to_string() 12801 .as_str() 12802 ) 12803 .unwrap_err() 12804 .to_string() 12805 .into_bytes() 12806 .get(..err.len()), 12807 Some(err.as_slice()) 12808 ); 12809 // Missing `publicKeyAlgorithm`. 12810 err = Error::missing_field("publicKeyAlgorithm") 12811 .to_string() 12812 .into_bytes(); 12813 assert_eq!( 12814 serde_json::from_str::<Registration>( 12815 serde_json::json!({ 12816 "id": "AAAAAAAAAAAAAAAAAAAAAA", 12817 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 12818 "response": { 12819 "clientDataJSON": b64_cdata_json, 12820 "authenticatorData": b64_adata, 12821 "transports": [], 12822 "publicKey": b64_key, 12823 "attestationObject": b64_aobj, 12824 }, 12825 "clientExtensionResults": {}, 12826 "type": "public-key" 12827 }) 12828 .to_string() 12829 .as_str() 12830 ) 12831 .unwrap_err() 12832 .to_string() 12833 .into_bytes() 12834 .get(..err.len()), 12835 Some(err.as_slice()) 12836 ); 12837 // `null` `publicKeyAlgorithm`. 12838 err = Error::invalid_type(Unexpected::Other("null"), &"publicKeyAlgorithm") 12839 .to_string() 12840 .into_bytes(); 12841 assert_eq!( 12842 serde_json::from_str::<Registration>( 12843 serde_json::json!({ 12844 "id": "AAAAAAAAAAAAAAAAAAAAAA", 12845 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 12846 "response": { 12847 "clientDataJSON": b64_cdata_json, 12848 "authenticatorData": b64_adata, 12849 "transports": [], 12850 "publicKey": b64_key, 12851 "publicKeyAlgorithm": null, 12852 "attestationObject": b64_aobj, 12853 }, 12854 "clientExtensionResults": {}, 12855 "type": "public-key" 12856 }) 12857 .to_string() 12858 .as_str() 12859 ) 12860 .unwrap_err() 12861 .to_string() 12862 .into_bytes() 12863 .get(..err.len()), 12864 Some(err.as_slice()) 12865 ); 12866 // `publicKey` mismatch. 12867 let bad_pub_key = RsaPrivateKey::from_components( 12868 BoxedUint::from_le_slice_vartime( 12869 [ 12870 175, 161, 161, 75, 52, 244, 72, 168, 29, 119, 33, 120, 3, 222, 231, 152, 222, 12871 119, 112, 83, 221, 237, 74, 174, 79, 216, 147, 251, 245, 94, 234, 114, 254, 21, 12872 17, 254, 8, 115, 75, 127, 150, 87, 59, 109, 230, 116, 85, 90, 11, 160, 63, 217, 12873 9, 38, 187, 250, 226, 183, 38, 164, 182, 218, 22, 19, 58, 189, 83, 219, 11, 12874 144, 15, 99, 151, 166, 46, 57, 17, 111, 189, 131, 142, 113, 85, 122, 188, 238, 12875 52, 21, 116, 125, 102, 195, 182, 165, 29, 156, 213, 182, 125, 156, 88, 56, 221, 12876 2, 98, 43, 210, 115, 32, 4, 105, 88, 181, 158, 207, 236, 162, 250, 253, 240, 12877 72, 8, 253, 50, 220, 247, 76, 170, 143, 68, 225, 231, 113, 64, 244, 17, 138, 12878 162, 233, 33, 2, 67, 11, 223, 188, 232, 152, 193, 20, 32, 243, 52, 64, 43, 2, 12879 243, 8, 77, 150, 232, 109, 148, 95, 127, 55, 71, 162, 34, 54, 83, 135, 52, 172, 12880 191, 32, 42, 106, 43, 211, 206, 100, 104, 110, 232, 5, 43, 120, 180, 166, 40, 12881 144, 233, 239, 103, 134, 103, 255, 224, 138, 184, 208, 137, 127, 36, 189, 143, 12882 248, 201, 2, 218, 51, 232, 96, 30, 83, 124, 109, 241, 23, 179, 247, 151, 238, 12883 212, 204, 44, 43, 223, 148, 241, 172, 10, 235, 155, 94, 68, 116, 24, 116, 191, 12884 86, 53, 127, 35, 133, 198, 204, 59, 76, 110, 16, 1, 15, 148, 135, 157, 12885 ] 12886 .as_slice(), 12887 ), 12888 0x0001_0001u32.into(), 12889 BoxedUint::from_le_slice_vartime( 12890 [ 12891 129, 93, 123, 251, 104, 29, 84, 203, 116, 100, 75, 237, 111, 160, 12, 100, 172, 12892 76, 57, 178, 144, 235, 81, 61, 115, 243, 28, 40, 183, 22, 56, 150, 68, 38, 220, 12893 62, 233, 110, 48, 174, 35, 197, 244, 109, 148, 109, 36, 69, 69, 82, 225, 113, 12894 175, 6, 239, 27, 193, 101, 50, 239, 122, 102, 7, 46, 98, 79, 195, 116, 155, 12895 158, 138, 147, 51, 93, 24, 237, 246, 82, 14, 109, 144, 250, 239, 93, 63, 214, 12896 96, 130, 226, 134, 198, 145, 161, 11, 231, 97, 214, 180, 255, 95, 158, 88, 108, 12897 254, 243, 177, 133, 184, 92, 95, 148, 88, 55, 124, 245, 244, 84, 86, 4, 121, 12898 44, 231, 97, 176, 190, 29, 155, 40, 57, 69, 165, 80, 168, 9, 56, 43, 233, 6, 12899 14, 157, 112, 223, 64, 88, 141, 7, 65, 23, 64, 208, 6, 83, 61, 8, 182, 248, 12900 126, 84, 179, 163, 80, 238, 90, 133, 4, 14, 71, 177, 175, 27, 29, 151, 211, 12901 108, 162, 195, 7, 157, 167, 86, 169, 3, 87, 235, 89, 158, 237, 216, 31, 243, 12902 197, 62, 5, 84, 131, 230, 186, 248, 49, 12, 93, 244, 61, 135, 180, 17, 162, 12903 241, 13, 115, 241, 138, 219, 98, 155, 166, 191, 63, 12, 37, 1, 165, 178, 84, 12904 200, 72, 80, 41, 77, 136, 217, 141, 246, 209, 31, 243, 159, 71, 43, 246, 159, 12905 182, 171, 116, 12, 3, 142, 235, 218, 164, 70, 90, 147, 238, 42, 75, 12906 ] 12907 .as_slice(), 12908 ), 12909 vec![ 12910 BoxedUint::from_le_slice_vartime( 12911 [ 12912 215, 199, 110, 28, 64, 16, 16, 109, 106, 152, 150, 124, 52, 166, 121, 92, 12913 242, 13, 0, 69, 7, 152, 72, 172, 118, 63, 156, 180, 140, 39, 53, 29, 197, 12914 224, 177, 48, 41, 221, 102, 65, 17, 185, 55, 62, 219, 152, 227, 7, 78, 219, 12915 14, 139, 71, 204, 144, 152, 14, 39, 247, 244, 165, 224, 234, 60, 213, 74, 12916 237, 30, 102, 177, 242, 138, 168, 31, 122, 47, 206, 155, 225, 113, 103, 12917 175, 152, 244, 27, 233, 112, 223, 248, 38, 215, 178, 20, 244, 8, 121, 26, 12918 11, 70, 122, 16, 85, 167, 87, 64, 216, 228, 227, 173, 57, 250, 8, 221, 38, 12919 12, 203, 212, 1, 112, 43, 72, 91, 225, 97, 228, 57, 154, 193, 12920 ] 12921 .as_slice(), 12922 ), 12923 BoxedUint::from_le_slice_vartime( 12924 [ 12925 233, 89, 204, 152, 31, 242, 8, 110, 38, 190, 111, 159, 105, 105, 45, 85, 12926 15, 244, 30, 250, 174, 226, 219, 111, 107, 191, 196, 135, 17, 123, 186, 12927 167, 85, 13, 120, 197, 159, 129, 78, 237, 152, 31, 230, 26, 229, 253, 197, 12928 211, 105, 204, 126, 142, 250, 55, 26, 172, 65, 160, 45, 6, 99, 86, 66, 238, 12929 107, 6, 98, 171, 93, 224, 201, 160, 31, 204, 82, 120, 228, 158, 238, 6, 12930 190, 12, 150, 153, 239, 95, 57, 71, 100, 239, 235, 155, 73, 200, 5, 225, 12931 127, 185, 46, 48, 243, 84, 33, 142, 17, 19, 20, 23, 215, 16, 114, 58, 211, 12932 14, 73, 148, 168, 252, 159, 252, 125, 57, 101, 211, 188, 12, 77, 208, 12933 ] 12934 .as_slice(), 12935 ), 12936 ], 12937 ) 12938 .unwrap() 12939 .to_public_key(); 12940 err = Error::invalid_value( 12941 Unexpected::Bytes([0; 32].as_slice()), 12942 &format!( 12943 "DER-encoded public key to match the public key within the attestation object: Rsa(RsaPubKey({:?}, 65537))", 12944 // Correct and won't `panic`. 12945 &att_obj[n_start_idx..e_meta_start_idx], 12946 ) 12947 .as_str(), 12948 ) 12949 .to_string().into_bytes(); 12950 assert_eq!(serde_json::from_str::<Registration>( 12951 serde_json::json!({ 12952 "id": "AAAAAAAAAAAAAAAAAAAAAA", 12953 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 12954 "response": { 12955 "clientDataJSON": b64_cdata_json, 12956 "authenticatorData": b64_adata, 12957 "transports": [], 12958 "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()), 12959 "publicKeyAlgorithm": -257i16, 12960 "attestationObject": b64_aobj, 12961 }, 12962 "clientExtensionResults": {}, 12963 "type": "public-key" 12964 }) 12965 .to_string() 12966 .as_str() 12967 ) 12968 .unwrap_err().to_string().into_bytes().get(..err.len()), 12969 Some(err.as_slice()) 12970 ); 12971 // Missing `publicKey` when using EdDSA, ES256, or RS256. 12972 err = Error::missing_field("publicKey").to_string().into_bytes(); 12973 assert_eq!( 12974 serde_json::from_str::<Registration>( 12975 serde_json::json!({ 12976 "id": "AAAAAAAAAAAAAAAAAAAAAA", 12977 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 12978 "response": { 12979 "clientDataJSON": b64_cdata_json, 12980 "authenticatorData": b64_adata, 12981 "transports": [], 12982 "publicKeyAlgorithm": -257i16, 12983 "attestationObject": b64_aobj, 12984 }, 12985 "clientExtensionResults": {}, 12986 "type": "public-key" 12987 }) 12988 .to_string() 12989 .as_str() 12990 ) 12991 .unwrap_err() 12992 .to_string() 12993 .into_bytes() 12994 .get(..err.len()), 12995 Some(err.as_slice()) 12996 ); 12997 // `null` `publicKey` when using EdDSA, ES256, or RS256. 12998 err = Error::invalid_type(Unexpected::Other("null"), &"publicKey") 12999 .to_string() 13000 .into_bytes(); 13001 assert_eq!( 13002 serde_json::from_str::<Registration>( 13003 serde_json::json!({ 13004 "id": "AAAAAAAAAAAAAAAAAAAAAA", 13005 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 13006 "response": { 13007 "clientDataJSON": b64_cdata_json, 13008 "authenticatorData": b64_adata, 13009 "transports": [], 13010 "publicKey": null, 13011 "publicKeyAlgorithm": -257i16, 13012 "attestationObject": b64_aobj, 13013 }, 13014 "clientExtensionResults": {}, 13015 "type": "public-key" 13016 }) 13017 .to_string() 13018 .as_str() 13019 ) 13020 .unwrap_err() 13021 .to_string() 13022 .into_bytes() 13023 .get(..err.len()), 13024 Some(err.as_slice()) 13025 ); 13026 } 13027 }