tests.rs (233669B)
1 use super::{ 2 super::{ 3 super::super::request::register::CoseAlgorithmIdentifier, AKP, ALG, 4 AuthenticatorAttachment, EC2, EDDSA, ES256, ES384, KTY, MLDSA44, MLDSA65, MLDSA87, OKP, 5 RSA, cbor, 6 }, 7 CustomRegistration, RegistrationRelaxed, 8 }; 9 use ed25519_dalek::{VerifyingKey, pkcs8::EncodePublicKey as _}; 10 use ml_dsa::{MlDsa44, MlDsa65, MlDsa87, VerifyingKey as MlDsaVerKey}; 11 use p256::{ 12 PublicKey as P256PubKey, Sec1Point as P256Pt, SecretKey as P256Key, 13 elliptic_curve::sec1::{FromSec1Point as _, ToSec1Point as _}, 14 }; 15 use p384::{PublicKey as P384PubKey, Sec1Point as P384Pt, SecretKey as P384Key}; 16 use rsa::{ 17 BoxedUint, RsaPrivateKey, 18 sha2::{Digest as _, Sha256}, 19 traits::PublicKeyParts as _, 20 }; 21 use serde::de::{Error as _, Unexpected}; 22 use serde_json::Error; 23 #[expect(clippy::unwrap_used, reason = "OK in tests")] 24 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 25 #[expect( 26 clippy::cognitive_complexity, 27 clippy::too_many_lines, 28 reason = "a lot to test" 29 )] 30 #[test] 31 fn eddsa_registration_deserialize_data_mismatch() { 32 let c_data_json = serde_json::json!({}).to_string(); 33 let att_obj: [u8; 143] = [ 34 cbor::MAP_3, 35 cbor::TEXT_3, 36 b'f', 37 b'm', 38 b't', 39 cbor::TEXT_4, 40 b'n', 41 b'o', 42 b'n', 43 b'e', 44 cbor::TEXT_7, 45 b'a', 46 b't', 47 b't', 48 b'S', 49 b't', 50 b'm', 51 b't', 52 cbor::MAP_0, 53 cbor::TEXT_8, 54 b'a', 55 b'u', 56 b't', 57 b'h', 58 b'D', 59 b'a', 60 b't', 61 b'a', 62 cbor::BYTES_INFO_24, 63 113, 64 // `rpIdHash`. 65 0, 66 0, 67 0, 68 0, 69 0, 70 0, 71 0, 72 0, 73 0, 74 0, 75 0, 76 0, 77 0, 78 0, 79 0, 80 0, 81 0, 82 0, 83 0, 84 0, 85 0, 86 0, 87 0, 88 0, 89 0, 90 0, 91 0, 92 0, 93 0, 94 0, 95 0, 96 0, 97 // `flags`. 98 0b0100_0101, 99 // `signCount`. 100 0, 101 0, 102 0, 103 0, 104 // `aaguid`. 105 0, 106 0, 107 0, 108 0, 109 0, 110 0, 111 0, 112 0, 113 0, 114 0, 115 0, 116 0, 117 0, 118 0, 119 0, 120 0, 121 // `credentialIdLength`. 122 0, 123 16, 124 // `credentialId`. 125 0, 126 0, 127 0, 128 0, 129 0, 130 0, 131 0, 132 0, 133 0, 134 0, 135 0, 136 0, 137 0, 138 0, 139 0, 140 0, 141 // Ed25519 COSE key. 142 cbor::MAP_4, 143 KTY, 144 OKP, 145 ALG, 146 EDDSA, 147 // `crv`. 148 cbor::NEG_ONE, 149 // `Ed25519`. 150 cbor::SIX, 151 // `x`. 152 cbor::NEG_TWO, 153 cbor::BYTES_INFO_24, 154 32, 155 // Compressed y-coordinate. 156 1, 157 1, 158 1, 159 1, 160 1, 161 1, 162 1, 163 1, 164 1, 165 1, 166 1, 167 1, 168 1, 169 1, 170 1, 171 1, 172 1, 173 1, 174 1, 175 1, 176 1, 177 1, 178 1, 179 1, 180 1, 181 1, 182 1, 183 1, 184 1, 185 1, 186 1, 187 1, 188 ]; 189 let pub_key = VerifyingKey::from_bytes(&[1; 32]) 190 .unwrap() 191 .to_public_key_der() 192 .unwrap(); 193 let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes()); 194 let att_obj_len = att_obj.len(); 195 let auth_data_start = att_obj_len - 113; 196 let b64_adata = base64url_nopad::encode(&att_obj[auth_data_start..]); 197 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 198 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 199 // Base case is valid. 200 assert!( 201 serde_json::from_str::<RegistrationRelaxed>( 202 serde_json::json!({ 203 "id": "AAAAAAAAAAAAAAAAAAAAAA", 204 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 205 "response": { 206 "clientDataJSON": b64_cdata_json, 207 "authenticatorData": b64_adata, 208 "transports": ["ble", "usb", "hybrid", "internal", "nfc", "smart-card"], 209 "publicKey": b64_key, 210 "publicKeyAlgorithm": -8i8, 211 "attestationObject": b64_aobj, 212 }, 213 "authenticatorAttachment": "cross-platform", 214 "clientExtensionResults": {}, 215 "type": "public-key" 216 }) 217 .to_string() 218 .as_str() 219 ) 220 .is_ok_and( 221 |reg| reg.0.response.client_data_json == c_data_json.as_bytes() 222 && reg.0.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 223 && reg.0.response.attestation_object_and_c_data_hash[att_obj_len..] 224 == *Sha256::digest(c_data_json.as_bytes()) 225 && reg.0.response.transports.count() == 6 226 && matches!( 227 reg.0.authenticator_attachment, 228 AuthenticatorAttachment::CrossPlatform 229 ) 230 && reg.0.client_extension_results.cred_props.is_none() 231 && reg.0.client_extension_results.prf.is_none() 232 ) 233 ); 234 // `id` and `rawId` mismatch. 235 let mut err = Error::invalid_value( 236 Unexpected::Bytes( 237 base64url_nopad::decode(b"ABABABABABABABABABABAA") 238 .unwrap() 239 .as_slice(), 240 ), 241 &format!("id and rawId to match: CredentialId({:?})", [0u8; 16]).as_str(), 242 ) 243 .to_string() 244 .into_bytes(); 245 assert_eq!( 246 serde_json::from_str::<RegistrationRelaxed>( 247 serde_json::json!({ 248 "id": "AAAAAAAAAAAAAAAAAAAAAA", 249 "rawId": "ABABABABABABABABABABAA", 250 "response": { 251 "clientDataJSON": b64_cdata_json, 252 "authenticatorData": b64_adata, 253 "transports": [], 254 "publicKey": b64_key, 255 "publicKeyAlgorithm": -8i8, 256 "attestationObject": b64_aobj, 257 }, 258 "clientExtensionResults": {}, 259 "type": "public-key" 260 }) 261 .to_string() 262 .as_str() 263 ) 264 .unwrap_err() 265 .to_string() 266 .into_bytes() 267 .get(..err.len()), 268 Some(err.as_slice()) 269 ); 270 // missing `id`. 271 drop( 272 serde_json::from_str::<RegistrationRelaxed>( 273 serde_json::json!({ 274 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 275 "response": { 276 "clientDataJSON": b64_cdata_json, 277 "authenticatorData": b64_adata, 278 "transports": [], 279 "publicKey": b64_key, 280 "publicKeyAlgorithm": -8i8, 281 "attestationObject": b64_aobj, 282 }, 283 "clientExtensionResults": {}, 284 "type": "public-key" 285 }) 286 .to_string() 287 .as_str(), 288 ) 289 .unwrap(), 290 ); 291 // `null` `id`. 292 err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId") 293 .to_string() 294 .into_bytes(); 295 assert_eq!( 296 serde_json::from_str::<RegistrationRelaxed>( 297 serde_json::json!({ 298 "id": null, 299 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 300 "response": { 301 "clientDataJSON": null, 302 "authenticatorData": b64_adata, 303 "transports": [], 304 "publicKey": b64_key, 305 "publicKeyAlgorithm": -8i8, 306 "attestationObject": b64_aobj, 307 }, 308 "clientExtensionResults": {}, 309 "type": "public-key" 310 }) 311 .to_string() 312 .as_str() 313 ) 314 .unwrap_err() 315 .to_string() 316 .into_bytes() 317 .get(..err.len()), 318 Some(err.as_slice()) 319 ); 320 // Missing `rawId`. 321 drop( 322 serde_json::from_str::<RegistrationRelaxed>( 323 serde_json::json!({ 324 "id": "AAAAAAAAAAAAAAAAAAAAAA", 325 "response": { 326 "clientDataJSON": b64_cdata_json, 327 "authenticatorData": b64_adata, 328 "transports": [], 329 "publicKey": b64_key, 330 "publicKeyAlgorithm": -8i8, 331 "attestationObject": b64_aobj, 332 }, 333 "clientExtensionResults": {}, 334 "type": "public-key" 335 }) 336 .to_string() 337 .as_str(), 338 ) 339 .unwrap(), 340 ); 341 // `null` `rawId`. 342 err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId") 343 .to_string() 344 .into_bytes(); 345 assert_eq!( 346 serde_json::from_str::<RegistrationRelaxed>( 347 serde_json::json!({ 348 "id": "AAAAAAAAAAAAAAAAAAAAAA", 349 "rawId": null, 350 "response": { 351 "clientDataJSON": b64_cdata_json, 352 "authenticatorData": b64_adata, 353 "transports": [], 354 "publicKey": b64_key, 355 "publicKeyAlgorithm": -8i8, 356 "attestationObject": b64_aobj, 357 }, 358 "clientExtensionResults": {}, 359 "type": "public-key" 360 }) 361 .to_string() 362 .as_str() 363 ) 364 .unwrap_err() 365 .to_string() 366 .into_bytes() 367 .get(..err.len()), 368 Some(err.as_slice()) 369 ); 370 // `id` and the credential id in authenticator data mismatch. 371 err = Error::invalid_value( 372 Unexpected::Bytes( 373 base64url_nopad::decode(b"ABABABABABABABABABABAA") 374 .unwrap() 375 .as_slice(), 376 ), 377 &format!( 378 "id, rawId, and the credential id in the attested credential data to all match: {:?}", 379 [0u8; 16] 380 ) 381 .as_str(), 382 ) 383 .to_string() 384 .into_bytes(); 385 assert_eq!( 386 serde_json::from_str::<RegistrationRelaxed>( 387 serde_json::json!({ 388 "id": "ABABABABABABABABABABAA", 389 "rawId": "ABABABABABABABABABABAA", 390 "response": { 391 "clientDataJSON": b64_cdata_json, 392 "authenticatorData": b64_adata, 393 "transports": [], 394 "publicKey": b64_key, 395 "publicKeyAlgorithm": -8i8, 396 "attestationObject": b64_aobj, 397 }, 398 "clientExtensionResults": {}, 399 "type": "public-key" 400 }) 401 .to_string() 402 .as_str() 403 ) 404 .unwrap_err() 405 .to_string() 406 .into_bytes() 407 .get(..err.len()), 408 Some(err.as_slice()) 409 ); 410 // `authenticatorData` mismatches `authData` in attestation object. 411 let mut bad_auth = [0; 113]; 412 let bad_auth_len = bad_auth.len(); 413 bad_auth.copy_from_slice(&att_obj[auth_data_start..]); 414 bad_auth[bad_auth_len - 32..].copy_from_slice([0; 32].as_slice()); 415 err = Error::invalid_value( 416 Unexpected::Bytes(bad_auth.as_slice()), 417 &format!("authenticator data to match the authenticator data portion of attestation object: {:?}", &att_obj[att_obj_len - bad_auth_len..]).as_str(), 418 ) 419 .to_string().into_bytes(); 420 assert_eq!( 421 serde_json::from_str::<RegistrationRelaxed>( 422 serde_json::json!({ 423 "id": "AAAAAAAAAAAAAAAAAAAAAA", 424 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 425 "response": { 426 "clientDataJSON": b64_cdata_json, 427 "authenticatorData": base64url_nopad::encode(bad_auth.as_slice()), 428 "transports": [], 429 "publicKey": b64_key, 430 "publicKeyAlgorithm": -8i8, 431 "attestationObject": b64_aobj, 432 }, 433 "clientExtensionResults": {}, 434 "type": "public-key" 435 }) 436 .to_string() 437 .as_str() 438 ) 439 .unwrap_err() 440 .to_string() 441 .into_bytes() 442 .get(..err.len()), 443 Some(err.as_slice()) 444 ); 445 // Missing `authenticatorData`. 446 drop( 447 serde_json::from_str::<RegistrationRelaxed>( 448 serde_json::json!({ 449 "id": "AAAAAAAAAAAAAAAAAAAAAA", 450 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 451 "response": { 452 "clientDataJSON": b64_cdata_json, 453 "transports": [], 454 "publicKey": b64_key, 455 "publicKeyAlgorithm": -8i8, 456 "attestationObject": b64_aobj, 457 }, 458 "clientExtensionResults": {}, 459 "type": "public-key" 460 }) 461 .to_string() 462 .as_str(), 463 ) 464 .unwrap(), 465 ); 466 // `null `authenticatorData`. 467 drop( 468 serde_json::from_str::<RegistrationRelaxed>( 469 serde_json::json!({ 470 "id": "AAAAAAAAAAAAAAAAAAAAAA", 471 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 472 "response": { 473 "clientDataJSON": b64_cdata_json, 474 "transports": [], 475 "authenticatorData": null, 476 "publicKey": b64_key, 477 "publicKeyAlgorithm": -8i8, 478 "attestationObject": b64_aobj, 479 }, 480 "clientExtensionResults": {}, 481 "type": "public-key" 482 }) 483 .to_string() 484 .as_str(), 485 ) 486 .unwrap(), 487 ); 488 // `publicKeyAlgorithm` mismatch. 489 err = Error::invalid_value( 490 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 491 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Eddsa).as_str() 492 ) 493 .to_string().into_bytes(); 494 assert_eq!( 495 serde_json::from_str::<RegistrationRelaxed>( 496 serde_json::json!({ 497 "id": "AAAAAAAAAAAAAAAAAAAAAA", 498 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 499 "response": { 500 "clientDataJSON": b64_cdata_json, 501 "authenticatorData": b64_adata, 502 "transports": [], 503 "publicKey": b64_key, 504 "publicKeyAlgorithm": -7i8, 505 "attestationObject": b64_aobj, 506 }, 507 "clientExtensionResults": {}, 508 "type": "public-key" 509 }) 510 .to_string() 511 .as_str() 512 ) 513 .unwrap_err() 514 .to_string() 515 .into_bytes() 516 .get(..err.len()), 517 Some(err.as_slice()) 518 ); 519 // Missing `publicKeyAlgorithm`. 520 drop( 521 serde_json::from_str::<RegistrationRelaxed>( 522 serde_json::json!({ 523 "id": "AAAAAAAAAAAAAAAAAAAAAA", 524 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 525 "response": { 526 "clientDataJSON": b64_cdata_json, 527 "authenticatorData": b64_adata, 528 "transports": [], 529 "publicKey": b64_key, 530 "attestationObject": b64_aobj, 531 }, 532 "clientExtensionResults": {}, 533 "type": "public-key" 534 }) 535 .to_string() 536 .as_str(), 537 ) 538 .unwrap(), 539 ); 540 // `null` `publicKeyAlgorithm`. 541 drop( 542 serde_json::from_str::<RegistrationRelaxed>( 543 serde_json::json!({ 544 "id": "AAAAAAAAAAAAAAAAAAAAAA", 545 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 546 "response": { 547 "clientDataJSON": b64_cdata_json, 548 "authenticatorData": b64_adata, 549 "transports": [], 550 "publicKey": b64_key, 551 "publicKeyAlgorithm": null, 552 "attestationObject": b64_aobj, 553 }, 554 "clientExtensionResults": {}, 555 "type": "public-key" 556 }) 557 .to_string() 558 .as_str(), 559 ) 560 .unwrap(), 561 ); 562 // `publicKey` mismatch. 563 err = Error::invalid_value( 564 Unexpected::Bytes([0; 32].as_slice()), 565 &format!( 566 "DER-encoded public key to match the public key within the attestation object: Ed25519(Ed25519PubKey({:?}))", 567 &att_obj[att_obj_len - 32..], 568 ) 569 .as_str(), 570 ) 571 .to_string().into_bytes(); 572 assert_eq!(serde_json::from_str::<RegistrationRelaxed>( 573 serde_json::json!({ 574 "id": "AAAAAAAAAAAAAAAAAAAAAA", 575 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 576 "response": { 577 "clientDataJSON": b64_cdata_json, 578 "authenticatorData": b64_adata, 579 "transports": [], 580 "publicKey": base64url_nopad::encode(VerifyingKey::from_bytes(&[0; 32]).unwrap().to_public_key_der().unwrap().as_bytes()), 581 "publicKeyAlgorithm": -8i8, 582 "attestationObject": b64_aobj, 583 }, 584 "clientExtensionResults": {}, 585 "type": "public-key" 586 }) 587 .to_string() 588 .as_str() 589 ) 590 .unwrap_err().to_string().into_bytes().get(..err.len()), 591 Some(err.as_slice()) 592 ); 593 // Missing `publicKey`. 594 drop( 595 serde_json::from_str::<RegistrationRelaxed>( 596 serde_json::json!({ 597 "id": "AAAAAAAAAAAAAAAAAAAAAA", 598 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 599 "response": { 600 "clientDataJSON": b64_cdata_json, 601 "authenticatorData": b64_adata, 602 "transports": [], 603 "publicKeyAlgorithm": -8i8, 604 "attestationObject": b64_aobj, 605 }, 606 "clientExtensionResults": {}, 607 "type": "public-key" 608 }) 609 .to_string() 610 .as_str(), 611 ) 612 .unwrap(), 613 ); 614 // `null` `publicKey`. 615 drop( 616 serde_json::from_str::<RegistrationRelaxed>( 617 serde_json::json!({ 618 "id": "AAAAAAAAAAAAAAAAAAAAAA", 619 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 620 "response": { 621 "clientDataJSON": b64_cdata_json, 622 "authenticatorData": b64_adata, 623 "transports": [], 624 "publicKey": null, 625 "publicKeyAlgorithm": -8i8, 626 "attestationObject": b64_aobj, 627 }, 628 "clientExtensionResults": {}, 629 "type": "public-key" 630 }) 631 .to_string() 632 .as_str(), 633 ) 634 .unwrap(), 635 ); 636 // Missing `transports`. 637 drop( 638 serde_json::from_str::<RegistrationRelaxed>( 639 serde_json::json!({ 640 "id": "AAAAAAAAAAAAAAAAAAAAAA", 641 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 642 "response": { 643 "clientDataJSON": b64_cdata_json, 644 "authenticatorData": b64_adata, 645 "publicKey": b64_key, 646 "publicKeyAlgorithm": -8i8, 647 "attestationObject": b64_aobj, 648 }, 649 "clientExtensionResults": {}, 650 "type": "public-key" 651 }) 652 .to_string() 653 .as_str(), 654 ) 655 .unwrap(), 656 ); 657 // Duplicate `transports` are allowed. 658 assert!( 659 serde_json::from_str::<RegistrationRelaxed>( 660 serde_json::json!({ 661 "id": "AAAAAAAAAAAAAAAAAAAAAA", 662 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 663 "response": { 664 "clientDataJSON": b64_cdata_json, 665 "authenticatorData": b64_adata, 666 "transports": ["usb", "usb"], 667 "publicKey": b64_key, 668 "publicKeyAlgorithm": -8i8, 669 "attestationObject": b64_aobj, 670 }, 671 "clientExtensionResults": {}, 672 "type": "public-key" 673 }) 674 .to_string() 675 .as_str() 676 ) 677 .is_ok_and(|reg| reg.0.response.transports.count() == 1) 678 ); 679 // `null` `transports`. 680 drop( 681 serde_json::from_str::<RegistrationRelaxed>( 682 serde_json::json!({ 683 "id": "AAAAAAAAAAAAAAAAAAAAAA", 684 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 685 "response": { 686 "clientDataJSON": b64_cdata_json, 687 "authenticatorData": b64_adata, 688 "transports": null, 689 "publicKey": b64_key, 690 "publicKeyAlgorithm": -8i8, 691 "attestationObject": b64_aobj, 692 }, 693 "clientExtensionResults": {}, 694 "type": "public-key" 695 }) 696 .to_string() 697 .as_str(), 698 ) 699 .unwrap(), 700 ); 701 // Unknown `transports`. 702 err = Error::invalid_value( 703 Unexpected::Str("Usb"), 704 &"'ble', 'cable', 'hybrid', 'internal', 'nfc', 'smart-card', or 'usb'", 705 ) 706 .to_string() 707 .into_bytes(); 708 assert_eq!( 709 serde_json::from_str::<RegistrationRelaxed>( 710 serde_json::json!({ 711 "id": "AAAAAAAAAAAAAAAAAAAAAA", 712 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 713 "response": { 714 "clientDataJSON": b64_cdata_json, 715 "authenticatorData": b64_adata, 716 "transports": ["Usb"], 717 "publicKey": b64_key, 718 "publicKeyAlgorithm": -8i8, 719 "attestationObject": b64_aobj, 720 }, 721 "clientExtensionResults": {}, 722 "type": "public-key" 723 }) 724 .to_string() 725 .as_str() 726 ) 727 .unwrap_err() 728 .to_string() 729 .into_bytes() 730 .get(..err.len()), 731 Some(err.as_slice()) 732 ); 733 // `null` `authenticatorAttachment`. 734 assert!( 735 serde_json::from_str::<RegistrationRelaxed>( 736 serde_json::json!({ 737 "id": "AAAAAAAAAAAAAAAAAAAAAA", 738 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 739 "response": { 740 "clientDataJSON": b64_cdata_json, 741 "authenticatorData": b64_adata, 742 "transports": [], 743 "publicKey": b64_key, 744 "publicKeyAlgorithm": -8i8, 745 "attestationObject": b64_aobj, 746 }, 747 "authenticatorAttachment": null, 748 "clientExtensionResults": {}, 749 "type": "public-key" 750 }) 751 .to_string() 752 .as_str() 753 ) 754 .is_ok_and(|reg| matches!( 755 reg.0.authenticator_attachment, 756 AuthenticatorAttachment::None 757 )) 758 ); 759 // Unknown `authenticatorAttachment`. 760 err = Error::invalid_value( 761 Unexpected::Str("Platform"), 762 &"'platform' or 'cross-platform'", 763 ) 764 .to_string() 765 .into_bytes(); 766 assert_eq!( 767 serde_json::from_str::<RegistrationRelaxed>( 768 serde_json::json!({ 769 "id": "AAAAAAAAAAAAAAAAAAAAAA", 770 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 771 "response": { 772 "clientDataJSON": b64_cdata_json, 773 "authenticatorData": b64_adata, 774 "transports": [], 775 "publicKey": b64_key, 776 "publicKeyAlgorithm": -8i8, 777 "attestationObject": b64_aobj, 778 }, 779 "authenticatorAttachment": "Platform", 780 "clientExtensionResults": {}, 781 "type": "public-key" 782 }) 783 .to_string() 784 .as_str() 785 ) 786 .unwrap_err() 787 .to_string() 788 .into_bytes() 789 .get(..err.len()), 790 Some(err.as_slice()) 791 ); 792 // Missing `clientDataJSON`. 793 err = Error::missing_field("clientDataJSON") 794 .to_string() 795 .into_bytes(); 796 assert_eq!( 797 serde_json::from_str::<RegistrationRelaxed>( 798 serde_json::json!({ 799 "id": "AAAAAAAAAAAAAAAAAAAAAA", 800 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 801 "response": { 802 "authenticatorData": b64_adata, 803 "transports": [], 804 "publicKey": b64_key, 805 "publicKeyAlgorithm": -8i8, 806 "attestationObject": b64_aobj, 807 }, 808 "clientExtensionResults": {}, 809 "type": "public-key" 810 }) 811 .to_string() 812 .as_str() 813 ) 814 .unwrap_err() 815 .to_string() 816 .into_bytes() 817 .get(..err.len()), 818 Some(err.as_slice()) 819 ); 820 // `null` `clientDataJSON`. 821 err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data") 822 .to_string() 823 .into_bytes(); 824 assert_eq!( 825 serde_json::from_str::<RegistrationRelaxed>( 826 serde_json::json!({ 827 "id": "AAAAAAAAAAAAAAAAAAAAAA", 828 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 829 "response": { 830 "clientDataJSON": null, 831 "authenticatorData": b64_adata, 832 "transports": [], 833 "publicKey": b64_key, 834 "publicKeyAlgorithm": -8i8, 835 "attestationObject": b64_aobj, 836 }, 837 "clientExtensionResults": {}, 838 "type": "public-key" 839 }) 840 .to_string() 841 .as_str() 842 ) 843 .unwrap_err() 844 .to_string() 845 .into_bytes() 846 .get(..err.len()), 847 Some(err.as_slice()) 848 ); 849 // Missing `attestationObject`. 850 err = Error::missing_field("attestationObject") 851 .to_string() 852 .into_bytes(); 853 assert_eq!( 854 serde_json::from_str::<RegistrationRelaxed>( 855 serde_json::json!({ 856 "id": "AAAAAAAAAAAAAAAAAAAAAA", 857 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 858 "response": { 859 "clientDataJSON": b64_cdata_json, 860 "authenticatorData": b64_adata, 861 "transports": [], 862 "publicKey": b64_key, 863 "publicKeyAlgorithm": -8i8, 864 }, 865 "clientExtensionResults": {}, 866 "type": "public-key" 867 }) 868 .to_string() 869 .as_str() 870 ) 871 .unwrap_err() 872 .to_string() 873 .into_bytes() 874 .get(..err.len()), 875 Some(err.as_slice()) 876 ); 877 // `null` `attestationObject`. 878 err = Error::invalid_type( 879 Unexpected::Other("null"), 880 &"base64url-encoded attestation object", 881 ) 882 .to_string() 883 .into_bytes(); 884 assert_eq!( 885 serde_json::from_str::<RegistrationRelaxed>( 886 serde_json::json!({ 887 "id": "AAAAAAAAAAAAAAAAAAAAAA", 888 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 889 "response": { 890 "clientDataJSON": b64_cdata_json, 891 "authenticatorData": b64_adata, 892 "transports": [], 893 "publicKey": b64_key, 894 "publicKeyAlgorithm": -8i8, 895 "attestationObject": null, 896 }, 897 "clientExtensionResults": {}, 898 "type": "public-key" 899 }) 900 .to_string() 901 .as_str() 902 ) 903 .unwrap_err() 904 .to_string() 905 .into_bytes() 906 .get(..err.len()), 907 Some(err.as_slice()) 908 ); 909 // Missing `response`. 910 err = Error::missing_field("response").to_string().into_bytes(); 911 assert_eq!( 912 serde_json::from_str::<RegistrationRelaxed>( 913 serde_json::json!({ 914 "id": "AAAAAAAAAAAAAAAAAAAAAA", 915 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 916 "clientExtensionResults": {}, 917 "type": "public-key" 918 }) 919 .to_string() 920 .as_str() 921 ) 922 .unwrap_err() 923 .to_string() 924 .into_bytes() 925 .get(..err.len()), 926 Some(err.as_slice()) 927 ); 928 // `null` `response`. 929 err = Error::invalid_type(Unexpected::Other("null"), &"AuthenticatorAttestation") 930 .to_string() 931 .into_bytes(); 932 assert_eq!( 933 serde_json::from_str::<RegistrationRelaxed>( 934 serde_json::json!({ 935 "id": "AAAAAAAAAAAAAAAAAAAAAA", 936 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 937 "response": null, 938 "clientExtensionResults": {}, 939 "type": "public-key" 940 }) 941 .to_string() 942 .as_str() 943 ) 944 .unwrap_err() 945 .to_string() 946 .into_bytes() 947 .get(..err.len()), 948 Some(err.as_slice()) 949 ); 950 // Empty `response`. 951 err = Error::missing_field("clientDataJSON") 952 .to_string() 953 .into_bytes(); 954 assert_eq!( 955 serde_json::from_str::<RegistrationRelaxed>( 956 serde_json::json!({ 957 "id": "AAAAAAAAAAAAAAAAAAAAAA", 958 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 959 "response": {}, 960 "clientExtensionResults": {}, 961 "type": "public-key" 962 }) 963 .to_string() 964 .as_str() 965 ) 966 .unwrap_err() 967 .to_string() 968 .into_bytes() 969 .get(..err.len()), 970 Some(err.as_slice()) 971 ); 972 // Missing `clientExtensionResults`. 973 drop( 974 serde_json::from_str::<RegistrationRelaxed>( 975 serde_json::json!({ 976 "id": "AAAAAAAAAAAAAAAAAAAAAA", 977 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 978 "response": { 979 "clientDataJSON": b64_cdata_json, 980 "authenticatorData": b64_adata, 981 "transports": [], 982 "publicKey": b64_key, 983 "publicKeyAlgorithm": -8i8, 984 "attestationObject": b64_aobj, 985 }, 986 "type": "public-key" 987 }) 988 .to_string() 989 .as_str(), 990 ) 991 .unwrap(), 992 ); 993 // `null` `clientExtensionResults`. 994 drop( 995 serde_json::from_str::<RegistrationRelaxed>( 996 serde_json::json!({ 997 "id": "AAAAAAAAAAAAAAAAAAAAAA", 998 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 999 "response": { 1000 "clientDataJSON": b64_cdata_json, 1001 "authenticatorData": b64_adata, 1002 "transports": [], 1003 "publicKey": b64_key, 1004 "publicKeyAlgorithm": -8i8, 1005 "attestationObject": b64_aobj, 1006 }, 1007 "clientExtensionResults": null, 1008 "type": "public-key" 1009 }) 1010 .to_string() 1011 .as_str(), 1012 ) 1013 .unwrap(), 1014 ); 1015 // Missing `type`. 1016 drop( 1017 serde_json::from_str::<RegistrationRelaxed>( 1018 serde_json::json!({ 1019 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1020 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1021 "response": { 1022 "clientDataJSON": b64_cdata_json, 1023 "authenticatorData": b64_adata, 1024 "transports": [], 1025 "publicKey": b64_key, 1026 "publicKeyAlgorithm": -8i8, 1027 "attestationObject": b64_aobj, 1028 }, 1029 "clientExtensionResults": {}, 1030 }) 1031 .to_string() 1032 .as_str(), 1033 ) 1034 .unwrap(), 1035 ); 1036 // `null` `type`. 1037 err = Error::invalid_type(Unexpected::Other("null"), &"public-key") 1038 .to_string() 1039 .into_bytes(); 1040 assert_eq!( 1041 serde_json::from_str::<RegistrationRelaxed>( 1042 serde_json::json!({ 1043 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1044 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1045 "response": { 1046 "clientDataJSON": b64_cdata_json, 1047 "authenticatorData": b64_adata, 1048 "transports": [], 1049 "publicKey": b64_key, 1050 "publicKeyAlgorithm": -8i8, 1051 "attestationObject": b64_aobj, 1052 }, 1053 "clientExtensionResults": {}, 1054 "type": null 1055 }) 1056 .to_string() 1057 .as_str() 1058 ) 1059 .unwrap_err() 1060 .to_string() 1061 .into_bytes() 1062 .get(..err.len()), 1063 Some(err.as_slice()) 1064 ); 1065 // Not exactly `public-type` `type`. 1066 err = Error::invalid_value(Unexpected::Str("Public-key"), &"public-key") 1067 .to_string() 1068 .into_bytes(); 1069 assert_eq!( 1070 serde_json::from_str::<RegistrationRelaxed>( 1071 serde_json::json!({ 1072 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1073 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1074 "response": { 1075 "clientDataJSON": b64_cdata_json, 1076 "authenticatorData": b64_adata, 1077 "transports": [], 1078 "publicKey": b64_key, 1079 "publicKeyAlgorithm": -8i8, 1080 "attestationObject": b64_aobj, 1081 }, 1082 "clientExtensionResults": {}, 1083 "type": "Public-key" 1084 }) 1085 .to_string() 1086 .as_str() 1087 ) 1088 .unwrap_err() 1089 .to_string() 1090 .into_bytes() 1091 .get(..err.len()), 1092 Some(err.as_slice()) 1093 ); 1094 // `null`. 1095 err = Error::invalid_type(Unexpected::Other("null"), &"PublicKeyCredential") 1096 .to_string() 1097 .into_bytes(); 1098 assert_eq!( 1099 serde_json::from_str::<RegistrationRelaxed>(serde_json::json!(null).to_string().as_str()) 1100 .unwrap_err() 1101 .to_string() 1102 .into_bytes() 1103 .get(..err.len()), 1104 Some(err.as_slice()) 1105 ); 1106 // Empty. 1107 err = Error::missing_field("response").to_string().into_bytes(); 1108 assert_eq!( 1109 serde_json::from_str::<RegistrationRelaxed>(serde_json::json!({}).to_string().as_str()) 1110 .unwrap_err() 1111 .to_string() 1112 .into_bytes() 1113 .get(..err.len()), 1114 Some(err.as_slice()) 1115 ); 1116 // Unknown field in `response`. 1117 drop( 1118 serde_json::from_str::<RegistrationRelaxed>( 1119 serde_json::json!({ 1120 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1121 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1122 "response": { 1123 "clientDataJSON": b64_cdata_json, 1124 "authenticatorData": b64_adata, 1125 "transports": [], 1126 "publicKey": b64_key, 1127 "publicKeyAlgorithm": -8i8, 1128 "attestationObject": b64_aobj, 1129 "foo": true, 1130 }, 1131 "clientExtensionResults": {}, 1132 "type": "public-key" 1133 }) 1134 .to_string() 1135 .as_str(), 1136 ) 1137 .unwrap(), 1138 ); 1139 // Duplicate field in `response`. 1140 err = Error::duplicate_field("transports") 1141 .to_string() 1142 .into_bytes(); 1143 assert_eq!( 1144 serde_json::from_str::<RegistrationRelaxed>( 1145 format!( 1146 "{{ 1147 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1148 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1149 \"response\": {{ 1150 \"clientDataJSON\": \"{b64_cdata_json}\", 1151 \"authenticatorData\": \"{b64_adata}\", 1152 \"transports\": [], 1153 \"publicKey\": \"{b64_key}\", 1154 \"publicKeyAlgorithm\": -8, 1155 \"attestationObject\": \"{b64_aobj}\", 1156 \"transports\": [] 1157 }}, 1158 \"clientExtensionResults\": {{}}, 1159 \"type\": \"public-key\" 1160 }}" 1161 ) 1162 .as_str() 1163 ) 1164 .unwrap_err() 1165 .to_string() 1166 .into_bytes() 1167 .get(..err.len()), 1168 Some(err.as_slice()) 1169 ); 1170 // Unknown field in `PublicKeyCredential`. 1171 drop( 1172 serde_json::from_str::<RegistrationRelaxed>( 1173 serde_json::json!({ 1174 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1175 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1176 "response": { 1177 "clientDataJSON": b64_cdata_json, 1178 "authenticatorData": b64_adata, 1179 "transports": [], 1180 "publicKey": b64_key, 1181 "publicKeyAlgorithm": -8i8, 1182 "attestationObject": b64_aobj 1183 }, 1184 "clientExtensionResults": {}, 1185 "type": "public-key", 1186 "foo": true, 1187 }) 1188 .to_string() 1189 .as_str(), 1190 ) 1191 .unwrap(), 1192 ); 1193 // Duplicate field in `PublicKeyCredential`. 1194 err = Error::duplicate_field("id").to_string().into_bytes(); 1195 assert_eq!( 1196 serde_json::from_str::<RegistrationRelaxed>( 1197 format!( 1198 "{{ 1199 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1200 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1201 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1202 \"response\": {{ 1203 \"clientDataJSON\": \"{b64_cdata_json}\", 1204 \"authenticatorData\": \"{b64_adata}\", 1205 \"transports\": [], 1206 \"publicKey\": \"{b64_key}\", 1207 \"publicKeyAlgorithm\": -8, 1208 \"attestationObject\": \"{b64_aobj}\" 1209 }}, 1210 \"clientExtensionResults\": {{}}, 1211 \"type\": \"public-key\" 1212 }}" 1213 ) 1214 .as_str() 1215 ) 1216 .unwrap_err() 1217 .to_string() 1218 .into_bytes() 1219 .get(..err.len()), 1220 Some(err.as_slice()) 1221 ); 1222 // Base case is correct. 1223 assert!( 1224 serde_json::from_str::<CustomRegistration>( 1225 serde_json::json!({ 1226 "attestationObject": b64_aobj, 1227 "authenticatorAttachment": "cross-platform", 1228 "clientDataJSON": b64_cdata_json, 1229 "clientExtensionResults": {}, 1230 "transports": ["ble", "usb", "hybrid", "internal", "nfc", "smart-card"], 1231 "type": "public-key" 1232 }) 1233 .to_string() 1234 .as_str() 1235 ) 1236 .is_ok_and( 1237 |reg| reg.0.response.client_data_json == c_data_json.as_bytes() 1238 && reg.0.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 1239 && reg.0.response.attestation_object_and_c_data_hash[att_obj_len..] 1240 == *Sha256::digest(c_data_json.as_bytes()) 1241 && reg.0.response.transports.count() == 6 1242 && matches!( 1243 reg.0.authenticator_attachment, 1244 AuthenticatorAttachment::CrossPlatform 1245 ) 1246 && reg.0.client_extension_results.cred_props.is_none() 1247 && reg.0.client_extension_results.prf.is_none() 1248 ) 1249 ); 1250 // Missing `transports`. 1251 err = Error::missing_field("transports").to_string().into_bytes(); 1252 assert_eq!( 1253 serde_json::from_str::<CustomRegistration>( 1254 serde_json::json!({ 1255 "attestationObject": b64_aobj, 1256 "authenticatorAttachment": "cross-platform", 1257 "clientDataJSON": b64_cdata_json, 1258 "clientExtensionResults": {}, 1259 "type": "public-key" 1260 }) 1261 .to_string() 1262 .as_str() 1263 ) 1264 .unwrap_err() 1265 .to_string() 1266 .into_bytes() 1267 .get(..err.len()), 1268 Some(err.as_slice()) 1269 ); 1270 // Duplicate `transports` are allowed. 1271 assert!( 1272 serde_json::from_str::<CustomRegistration>( 1273 serde_json::json!({ 1274 "attestationObject": b64_aobj, 1275 "authenticatorAttachment": "cross-platform", 1276 "clientDataJSON": b64_cdata_json, 1277 "clientExtensionResults": {}, 1278 "transports": ["usb", "usb"], 1279 "type": "public-key" 1280 }) 1281 .to_string() 1282 .as_str() 1283 ) 1284 .is_ok_and(|reg| reg.0.response.transports.count() == 1) 1285 ); 1286 // `null` `transports`. 1287 err = Error::invalid_type(Unexpected::Other("null"), &"AuthTransports") 1288 .to_string() 1289 .into_bytes(); 1290 assert_eq!( 1291 serde_json::from_str::<CustomRegistration>( 1292 serde_json::json!({ 1293 "clientDataJSON": b64_cdata_json, 1294 "transports": null, 1295 "attestationObject": b64_aobj, 1296 "clientExtensionResults": {}, 1297 "type": "public-key" 1298 }) 1299 .to_string() 1300 .as_str() 1301 ) 1302 .unwrap_err() 1303 .to_string() 1304 .into_bytes() 1305 .get(..err.len()), 1306 Some(err.as_slice()) 1307 ); 1308 // Unknown `transports`. 1309 err = Error::invalid_value( 1310 Unexpected::Str("Usb"), 1311 &"'ble', 'cable', 'hybrid', 'internal', 'nfc', 'smart-card', or 'usb'", 1312 ) 1313 .to_string() 1314 .into_bytes(); 1315 assert_eq!( 1316 serde_json::from_str::<CustomRegistration>( 1317 serde_json::json!({ 1318 "attestationObject": b64_aobj, 1319 "authenticatorAttachment": "cross-platform", 1320 "clientDataJSON": b64_cdata_json, 1321 "clientExtensionResults": {}, 1322 "transports": ["Usb"], 1323 "type": "public-key" 1324 }) 1325 .to_string() 1326 .as_str() 1327 ) 1328 .unwrap_err() 1329 .to_string() 1330 .into_bytes() 1331 .get(..err.len()), 1332 Some(err.as_slice()) 1333 ); 1334 // `null` `authenticatorAttachment`. 1335 assert!( 1336 serde_json::from_str::<CustomRegistration>( 1337 serde_json::json!({ 1338 "attestationObject": b64_aobj, 1339 "authenticatorAttachment": null, 1340 "clientDataJSON": b64_cdata_json, 1341 "clientExtensionResults": {}, 1342 "transports": [], 1343 "type": "public-key" 1344 }) 1345 .to_string() 1346 .as_str() 1347 ) 1348 .is_ok_and(|reg| matches!( 1349 reg.0.authenticator_attachment, 1350 AuthenticatorAttachment::None 1351 )) 1352 ); 1353 // Unknown `authenticatorAttachment`. 1354 err = Error::invalid_value( 1355 Unexpected::Str("Platform"), 1356 &"'platform' or 'cross-platform'", 1357 ) 1358 .to_string() 1359 .into_bytes(); 1360 assert_eq!( 1361 serde_json::from_str::<CustomRegistration>( 1362 serde_json::json!({ 1363 "attestationObject": b64_aobj, 1364 "authenticatorAttachment": "Platform", 1365 "clientDataJSON": b64_cdata_json, 1366 "clientExtensionResults": {}, 1367 "transports": [], 1368 "type": "public-key" 1369 }) 1370 .to_string() 1371 .as_str() 1372 ) 1373 .unwrap_err() 1374 .to_string() 1375 .into_bytes() 1376 .get(..err.len()), 1377 Some(err.as_slice()) 1378 ); 1379 // Missing `clientDataJSON`. 1380 err = Error::missing_field("clientDataJSON") 1381 .to_string() 1382 .into_bytes(); 1383 assert_eq!( 1384 serde_json::from_str::<CustomRegistration>( 1385 serde_json::json!({ 1386 "transports": [], 1387 "attestationObject": b64_aobj, 1388 "clientExtensionResults": {}, 1389 "type": "public-key" 1390 }) 1391 .to_string() 1392 .as_str() 1393 ) 1394 .unwrap_err() 1395 .to_string() 1396 .into_bytes() 1397 .get(..err.len()), 1398 Some(err.as_slice()) 1399 ); 1400 // `null` `clientDataJSON`. 1401 err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data") 1402 .to_string() 1403 .into_bytes(); 1404 assert_eq!( 1405 serde_json::from_str::<CustomRegistration>( 1406 serde_json::json!({ 1407 "clientDataJSON": null, 1408 "transports": [], 1409 "attestationObject": b64_aobj, 1410 }) 1411 .to_string() 1412 .as_str() 1413 ) 1414 .unwrap_err() 1415 .to_string() 1416 .into_bytes() 1417 .get(..err.len()), 1418 Some(err.as_slice()) 1419 ); 1420 // Missing `attestationObject`. 1421 err = Error::missing_field("attestationObject") 1422 .to_string() 1423 .into_bytes(); 1424 assert_eq!( 1425 serde_json::from_str::<CustomRegistration>( 1426 serde_json::json!({ 1427 "clientDataJSON": b64_cdata_json, 1428 "transports": [], 1429 "clientExtensionResults": {}, 1430 "type": "public-key" 1431 }) 1432 .to_string() 1433 .as_str() 1434 ) 1435 .unwrap_err() 1436 .to_string() 1437 .into_bytes() 1438 .get(..err.len()), 1439 Some(err.as_slice()) 1440 ); 1441 // `null` `attestationObject`. 1442 err = Error::invalid_type( 1443 Unexpected::Other("null"), 1444 &"base64url-encoded attestation object", 1445 ) 1446 .to_string() 1447 .into_bytes(); 1448 assert_eq!( 1449 serde_json::from_str::<CustomRegistration>( 1450 serde_json::json!({ 1451 "clientDataJSON": b64_cdata_json, 1452 "transports": [], 1453 "attestationObject": null, 1454 "clientExtensionResults": {}, 1455 "type": "public-key" 1456 }) 1457 .to_string() 1458 .as_str() 1459 ) 1460 .unwrap_err() 1461 .to_string() 1462 .into_bytes() 1463 .get(..err.len()), 1464 Some(err.as_slice()) 1465 ); 1466 // Missing `clientExtensionResults`. 1467 err = Error::missing_field("clientExtensionResults") 1468 .to_string() 1469 .into_bytes(); 1470 assert_eq!( 1471 serde_json::from_str::<CustomRegistration>( 1472 serde_json::json!({ 1473 "clientDataJSON": b64_cdata_json, 1474 "transports": [], 1475 "attestationObject": b64_aobj, 1476 "type": "public-key" 1477 }) 1478 .to_string() 1479 .as_str() 1480 ) 1481 .unwrap_err() 1482 .to_string() 1483 .into_bytes() 1484 .get(..err.len()), 1485 Some(err.as_slice()) 1486 ); 1487 // `null` `clientExtensionResults`. 1488 err = Error::invalid_type(Unexpected::Other("null"), &"ClientExtensionsOutputs") 1489 .to_string() 1490 .into_bytes(); 1491 assert_eq!( 1492 serde_json::from_str::<CustomRegistration>( 1493 serde_json::json!({ 1494 "clientDataJSON": b64_cdata_json, 1495 "transports": [], 1496 "attestationObject": b64_aobj, 1497 "clientExtensionResults": null, 1498 "type": "public-key" 1499 }) 1500 .to_string() 1501 .as_str() 1502 ) 1503 .unwrap_err() 1504 .to_string() 1505 .into_bytes() 1506 .get(..err.len()), 1507 Some(err.as_slice()) 1508 ); 1509 // Missing `type`. 1510 assert!( 1511 serde_json::from_str::<CustomRegistration>( 1512 serde_json::json!({ 1513 "attestationObject": b64_aobj, 1514 "clientDataJSON": b64_cdata_json, 1515 "clientExtensionResults": {}, 1516 "transports": [] 1517 }) 1518 .to_string() 1519 .as_str() 1520 ) 1521 .is_ok_and(|_| true) 1522 ); 1523 // `null` `type`. 1524 err = Error::invalid_type(Unexpected::Other("null"), &"public-key") 1525 .to_string() 1526 .into_bytes(); 1527 assert_eq!( 1528 serde_json::from_str::<CustomRegistration>( 1529 serde_json::json!({ 1530 "attestationObject": b64_aobj, 1531 "clientDataJSON": b64_cdata_json, 1532 "clientExtensionResults": {}, 1533 "transports": [], 1534 "type": null 1535 }) 1536 .to_string() 1537 .as_str() 1538 ) 1539 .unwrap_err() 1540 .to_string() 1541 .into_bytes() 1542 .get(..err.len()), 1543 Some(err.as_slice()) 1544 ); 1545 // Not exactly `public-type` `type`. 1546 err = Error::invalid_value(Unexpected::Str("Public-key"), &"public-key") 1547 .to_string() 1548 .into_bytes(); 1549 assert_eq!( 1550 serde_json::from_str::<CustomRegistration>( 1551 serde_json::json!({ 1552 "clientDataJSON": b64_cdata_json, 1553 "transports": [], 1554 "attestationObject": b64_aobj, 1555 "clientExtensionResults": {}, 1556 "type": "Public-key" 1557 }) 1558 .to_string() 1559 .as_str() 1560 ) 1561 .unwrap_err() 1562 .to_string() 1563 .into_bytes() 1564 .get(..err.len()), 1565 Some(err.as_slice()) 1566 ); 1567 // `null`. 1568 err = Error::invalid_type(Unexpected::Other("null"), &"CustomRegistration") 1569 .to_string() 1570 .into_bytes(); 1571 assert_eq!( 1572 serde_json::from_str::<CustomRegistration>(serde_json::json!(null).to_string().as_str()) 1573 .unwrap_err() 1574 .to_string() 1575 .into_bytes() 1576 .get(..err.len()), 1577 Some(err.as_slice()) 1578 ); 1579 // Empty. 1580 err = Error::missing_field("attestationObject") 1581 .to_string() 1582 .into_bytes(); 1583 assert_eq!( 1584 serde_json::from_str::<CustomRegistration>(serde_json::json!({}).to_string().as_str()) 1585 .unwrap_err() 1586 .to_string() 1587 .into_bytes() 1588 .get(..err.len()), 1589 Some(err.as_slice()) 1590 ); 1591 // Unknown field. 1592 err = Error::unknown_field( 1593 "foo", 1594 [ 1595 "attestationObject", 1596 "authenticatorAttachment", 1597 "clientDataJSON", 1598 "clientExtensionResults", 1599 "transports", 1600 "type", 1601 ] 1602 .as_slice(), 1603 ) 1604 .to_string() 1605 .into_bytes(); 1606 assert_eq!( 1607 serde_json::from_str::<CustomRegistration>( 1608 serde_json::json!({ 1609 "clientDataJSON": b64_cdata_json, 1610 "transports": [], 1611 "attestationObject": b64_aobj, 1612 "foo": true, 1613 "clientExtensionResults": {}, 1614 "type": "public-key" 1615 }) 1616 .to_string() 1617 .as_str() 1618 ) 1619 .unwrap_err() 1620 .to_string() 1621 .into_bytes() 1622 .get(..err.len()), 1623 Some(err.as_slice()) 1624 ); 1625 // Duplicate field. 1626 err = Error::duplicate_field("transports") 1627 .to_string() 1628 .into_bytes(); 1629 assert_eq!( 1630 serde_json::from_str::<CustomRegistration>( 1631 format!( 1632 "{{ 1633 \"clientDataJSON\": \"{b64_cdata_json}\", 1634 \"transports\": [], 1635 \"attestationObject\": \"{b64_aobj}\", 1636 \"transports\": [] 1637 \"clientExtensionResults\": {{}}, 1638 \"type\": \"public-key\" 1639 }}" 1640 ) 1641 .as_str() 1642 ) 1643 .unwrap_err() 1644 .to_string() 1645 .into_bytes() 1646 .get(..err.len()), 1647 Some(err.as_slice()) 1648 ); 1649 } 1650 #[expect(clippy::unwrap_used, reason = "OK in tests")] 1651 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 1652 #[expect(clippy::too_many_lines, reason = "a lot to test")] 1653 #[test] 1654 fn client_extensions() { 1655 let c_data_json = serde_json::json!({}).to_string(); 1656 let att_obj: [u8; 143] = [ 1657 cbor::MAP_3, 1658 cbor::TEXT_3, 1659 b'f', 1660 b'm', 1661 b't', 1662 cbor::TEXT_4, 1663 b'n', 1664 b'o', 1665 b'n', 1666 b'e', 1667 cbor::TEXT_7, 1668 b'a', 1669 b't', 1670 b't', 1671 b'S', 1672 b't', 1673 b'm', 1674 b't', 1675 cbor::MAP_0, 1676 cbor::TEXT_8, 1677 b'a', 1678 b'u', 1679 b't', 1680 b'h', 1681 b'D', 1682 b'a', 1683 b't', 1684 b'a', 1685 cbor::BYTES_INFO_24, 1686 113, 1687 // `rpIdHash`. 1688 0, 1689 0, 1690 0, 1691 0, 1692 0, 1693 0, 1694 0, 1695 0, 1696 0, 1697 0, 1698 0, 1699 0, 1700 0, 1701 0, 1702 0, 1703 0, 1704 0, 1705 0, 1706 0, 1707 0, 1708 0, 1709 0, 1710 0, 1711 0, 1712 0, 1713 0, 1714 0, 1715 0, 1716 0, 1717 0, 1718 0, 1719 0, 1720 // `flags`. 1721 0b0100_0101, 1722 // `signCount`. 1723 0, 1724 0, 1725 0, 1726 0, 1727 // `aaguid`. 1728 0, 1729 0, 1730 0, 1731 0, 1732 0, 1733 0, 1734 0, 1735 0, 1736 0, 1737 0, 1738 0, 1739 0, 1740 0, 1741 0, 1742 0, 1743 0, 1744 // `credentialIdLength`. 1745 0, 1746 16, 1747 // `credentialId`. 1748 0, 1749 0, 1750 0, 1751 0, 1752 0, 1753 0, 1754 0, 1755 0, 1756 0, 1757 0, 1758 0, 1759 0, 1760 0, 1761 0, 1762 0, 1763 0, 1764 // Ed25519 COSE key. 1765 cbor::MAP_4, 1766 KTY, 1767 OKP, 1768 ALG, 1769 EDDSA, 1770 // `crv`. 1771 cbor::NEG_ONE, 1772 // `Ed25519`. 1773 cbor::SIX, 1774 // `x`. 1775 cbor::NEG_TWO, 1776 cbor::BYTES_INFO_24, 1777 32, 1778 // Compressed y-coordinate. 1779 1, 1780 1, 1781 1, 1782 1, 1783 1, 1784 1, 1785 1, 1786 1, 1787 1, 1788 1, 1789 1, 1790 1, 1791 1, 1792 1, 1793 1, 1794 1, 1795 1, 1796 1, 1797 1, 1798 1, 1799 1, 1800 1, 1801 1, 1802 1, 1803 1, 1804 1, 1805 1, 1806 1, 1807 1, 1808 1, 1809 1, 1810 1, 1811 ]; 1812 let pub_key = VerifyingKey::from_bytes(&[1; 32]) 1813 .unwrap() 1814 .to_public_key_der() 1815 .unwrap(); 1816 let att_obj_len = att_obj.len(); 1817 let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes()); 1818 let auth_data_start = att_obj_len - 113; 1819 let b64_adata = base64url_nopad::encode(&att_obj[auth_data_start..]); 1820 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 1821 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 1822 // Base case is valid. 1823 assert!( 1824 serde_json::from_str::<RegistrationRelaxed>( 1825 serde_json::json!({ 1826 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1827 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1828 "response": { 1829 "clientDataJSON": b64_cdata_json, 1830 "authenticatorData": b64_adata, 1831 "transports": [], 1832 "publicKey": b64_key, 1833 "publicKeyAlgorithm": -8i8, 1834 "attestationObject": b64_aobj, 1835 }, 1836 "clientExtensionResults": {}, 1837 "type": "public-key" 1838 }) 1839 .to_string() 1840 .as_str() 1841 ) 1842 .is_ok_and( 1843 |reg| reg.0.response.client_data_json == c_data_json.as_bytes() 1844 && reg.0.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 1845 && reg.0.response.attestation_object_and_c_data_hash[att_obj_len..] 1846 == *Sha256::digest(c_data_json.as_bytes()) 1847 && reg.0.response.transports.is_empty() 1848 && matches!( 1849 reg.0.authenticator_attachment, 1850 AuthenticatorAttachment::None 1851 ) 1852 && reg.0.client_extension_results.cred_props.is_none() 1853 && reg.0.client_extension_results.prf.is_none() 1854 ) 1855 ); 1856 // `null` `credProps`. 1857 assert!( 1858 serde_json::from_str::<RegistrationRelaxed>( 1859 serde_json::json!({ 1860 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1861 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1862 "response": { 1863 "clientDataJSON": b64_cdata_json, 1864 "authenticatorData": b64_adata, 1865 "transports": [], 1866 "publicKey": b64_key, 1867 "publicKeyAlgorithm": -8i8, 1868 "attestationObject": b64_aobj, 1869 }, 1870 "clientExtensionResults": { 1871 "credProps": null 1872 }, 1873 "type": "public-key" 1874 }) 1875 .to_string() 1876 .as_str() 1877 ) 1878 .is_ok_and(|reg| reg.0.client_extension_results.cred_props.is_none() 1879 && reg.0.client_extension_results.prf.is_none()) 1880 ); 1881 // `null` `prf`. 1882 assert!( 1883 serde_json::from_str::<RegistrationRelaxed>( 1884 serde_json::json!({ 1885 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1886 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1887 "response": { 1888 "clientDataJSON": b64_cdata_json, 1889 "authenticatorData": b64_adata, 1890 "transports": [], 1891 "publicKey": b64_key, 1892 "publicKeyAlgorithm": -8i8, 1893 "attestationObject": b64_aobj, 1894 }, 1895 "clientExtensionResults": { 1896 "prf": null 1897 }, 1898 "type": "public-key" 1899 }) 1900 .to_string() 1901 .as_str() 1902 ) 1903 .is_ok_and(|reg| reg.0.client_extension_results.cred_props.is_none() 1904 && reg.0.client_extension_results.prf.is_none()) 1905 ); 1906 // Unknown `clientExtensionResults`. 1907 drop( 1908 serde_json::from_str::<RegistrationRelaxed>( 1909 serde_json::json!({ 1910 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1911 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1912 "response": { 1913 "clientDataJSON": b64_cdata_json, 1914 "authenticatorData": b64_adata, 1915 "transports": [], 1916 "publicKey": b64_key, 1917 "publicKeyAlgorithm": -8i8, 1918 "attestationObject": b64_aobj, 1919 }, 1920 "clientExtensionResults": { 1921 "CredProps": { 1922 "rk": true 1923 } 1924 }, 1925 "type": "public-key" 1926 }) 1927 .to_string() 1928 .as_str(), 1929 ) 1930 .unwrap(), 1931 ); 1932 // Duplicate field. 1933 let mut err = Error::duplicate_field("credProps").to_string().into_bytes(); 1934 assert_eq!( 1935 serde_json::from_str::<RegistrationRelaxed>( 1936 format!( 1937 "{{ 1938 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1939 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1940 \"response\": {{ 1941 \"clientDataJSON\": \"{b64_cdata_json}\", 1942 \"authenticatorData\": \"{b64_adata}\", 1943 \"transports\": [], 1944 \"publicKey\": \"{b64_key}\", 1945 \"publicKeyAlgorithm\": -8, 1946 \"attestationObject\": \"{b64_aobj}\" 1947 }}, 1948 \"clientExtensionResults\": {{ 1949 \"credProps\": null, 1950 \"credProps\": null 1951 }}, 1952 \"type\": \"public-key\" 1953 }}" 1954 ) 1955 .as_str() 1956 ) 1957 .unwrap_err() 1958 .to_string() 1959 .into_bytes() 1960 .get(..err.len()), 1961 Some(err.as_slice()) 1962 ); 1963 // `null` `rk`. 1964 assert!( 1965 serde_json::from_str::<RegistrationRelaxed>( 1966 serde_json::json!({ 1967 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1968 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1969 "response": { 1970 "clientDataJSON": b64_cdata_json, 1971 "authenticatorData": b64_adata, 1972 "transports": [], 1973 "publicKey": b64_key, 1974 "publicKeyAlgorithm": -8i8, 1975 "attestationObject": b64_aobj, 1976 }, 1977 "clientExtensionResults": { 1978 "credProps": { 1979 "rk": null 1980 } 1981 }, 1982 "type": "public-key" 1983 }) 1984 .to_string() 1985 .as_str() 1986 ) 1987 .is_ok_and(|reg| reg 1988 .0 1989 .client_extension_results 1990 .cred_props 1991 .is_some_and(|props| props.rk.is_none()) 1992 && reg.0.client_extension_results.prf.is_none()) 1993 ); 1994 // Missing `rk`. 1995 assert!( 1996 serde_json::from_str::<RegistrationRelaxed>( 1997 serde_json::json!({ 1998 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1999 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2000 "response": { 2001 "clientDataJSON": b64_cdata_json, 2002 "authenticatorData": b64_adata, 2003 "transports": [], 2004 "publicKey": b64_key, 2005 "publicKeyAlgorithm": -8i8, 2006 "attestationObject": b64_aobj, 2007 }, 2008 "clientExtensionResults": { 2009 "credProps": {} 2010 }, 2011 "type": "public-key" 2012 }) 2013 .to_string() 2014 .as_str() 2015 ) 2016 .is_ok_and(|reg| reg 2017 .0 2018 .client_extension_results 2019 .cred_props 2020 .is_some_and(|props| props.rk.is_none()) 2021 && reg.0.client_extension_results.prf.is_none()) 2022 ); 2023 // `true` rk`. 2024 assert!( 2025 serde_json::from_str::<RegistrationRelaxed>( 2026 serde_json::json!({ 2027 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2028 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2029 "response": { 2030 "clientDataJSON": b64_cdata_json, 2031 "authenticatorData": b64_adata, 2032 "transports": [], 2033 "publicKey": b64_key, 2034 "publicKeyAlgorithm": -8i8, 2035 "attestationObject": b64_aobj, 2036 }, 2037 "clientExtensionResults": { 2038 "credProps": { 2039 "rk": true 2040 } 2041 }, 2042 "type": "public-key" 2043 }) 2044 .to_string() 2045 .as_str() 2046 ) 2047 .is_ok_and(|reg| reg 2048 .0 2049 .client_extension_results 2050 .cred_props 2051 .is_some_and(|props| props.rk.unwrap_or_default()) 2052 && reg.0.client_extension_results.prf.is_none()) 2053 ); 2054 // `false` rk`. 2055 assert!( 2056 serde_json::from_str::<RegistrationRelaxed>( 2057 serde_json::json!({ 2058 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2059 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2060 "response": { 2061 "clientDataJSON": b64_cdata_json, 2062 "authenticatorData": b64_adata, 2063 "transports": [], 2064 "publicKey": b64_key, 2065 "publicKeyAlgorithm": -8i8, 2066 "attestationObject": b64_aobj, 2067 }, 2068 "clientExtensionResults": { 2069 "credProps": { 2070 "rk": false 2071 } 2072 }, 2073 "type": "public-key" 2074 }) 2075 .to_string() 2076 .as_str() 2077 ) 2078 .is_ok_and(|reg| reg 2079 .0 2080 .client_extension_results 2081 .cred_props 2082 .is_some_and(|props| props.rk.is_some_and(|rk| !rk)) 2083 && reg.0.client_extension_results.prf.is_none()) 2084 ); 2085 // Invalid `rk`. 2086 err = Error::invalid_type(Unexpected::Unsigned(3), &"a boolean") 2087 .to_string() 2088 .into_bytes(); 2089 assert_eq!( 2090 serde_json::from_str::<RegistrationRelaxed>( 2091 serde_json::json!({ 2092 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2093 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2094 "response": { 2095 "clientDataJSON": b64_cdata_json, 2096 "authenticatorData": b64_adata, 2097 "transports": [], 2098 "publicKey": b64_key, 2099 "publicKeyAlgorithm": -8i8, 2100 "attestationObject": b64_aobj, 2101 }, 2102 "clientExtensionResults": { 2103 "credProps": { 2104 "rk": 3u8 2105 } 2106 }, 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 // Unknown `credProps` field. 2119 drop( 2120 serde_json::from_str::<RegistrationRelaxed>( 2121 serde_json::json!({ 2122 "id": "AAAAAAAAAAAAAAAAAAAAAA", 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 "credProps": { 2134 "Rk": true, 2135 } 2136 }, 2137 "type": "public-key" 2138 }) 2139 .to_string() 2140 .as_str(), 2141 ) 2142 .unwrap(), 2143 ); 2144 // Duplicate field in `credProps`. 2145 err = Error::duplicate_field("rk").to_string().into_bytes(); 2146 assert_eq!( 2147 serde_json::from_str::<RegistrationRelaxed>( 2148 format!( 2149 "{{ 2150 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 2151 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 2152 \"response\": {{ 2153 \"clientDataJSON\": \"{b64_cdata_json}\", 2154 \"authenticatorData\": \"{b64_adata}\", 2155 \"transports\": [], 2156 \"publicKey\": \"{b64_key}\", 2157 \"publicKeyAlgorithm\": -8, 2158 \"attestationObject\": \"{b64_aobj}\" 2159 }}, 2160 \"clientExtensionResults\": {{ 2161 \"credProps\": {{ 2162 \"rk\": true, 2163 \"rk\": true 2164 }} 2165 }}, 2166 \"type\": \"public-key\" 2167 }}" 2168 ) 2169 .as_str() 2170 ) 2171 .unwrap_err() 2172 .to_string() 2173 .into_bytes() 2174 .get(..err.len()), 2175 Some(err.as_slice()) 2176 ); 2177 // `null` `enabled`. 2178 err = Error::invalid_type(Unexpected::Other("null"), &"a boolean") 2179 .to_string() 2180 .into_bytes(); 2181 assert_eq!( 2182 serde_json::from_str::<RegistrationRelaxed>( 2183 serde_json::json!({ 2184 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2185 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2186 "response": { 2187 "clientDataJSON": b64_cdata_json, 2188 "authenticatorData": b64_adata, 2189 "transports": [], 2190 "publicKey": b64_key, 2191 "publicKeyAlgorithm": -8i8, 2192 "attestationObject": b64_aobj, 2193 }, 2194 "clientExtensionResults": { 2195 "prf": { 2196 "enabled": null 2197 } 2198 }, 2199 "type": "public-key" 2200 }) 2201 .to_string() 2202 .as_str() 2203 ) 2204 .unwrap_err() 2205 .to_string() 2206 .into_bytes() 2207 .get(..err.len()), 2208 Some(err.as_slice()) 2209 ); 2210 // Missing `enabled`. 2211 err = Error::missing_field("enabled").to_string().into_bytes(); 2212 assert_eq!( 2213 serde_json::from_str::<RegistrationRelaxed>( 2214 serde_json::json!({ 2215 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2216 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2217 "response": { 2218 "clientDataJSON": b64_cdata_json, 2219 "authenticatorData": b64_adata, 2220 "transports": [], 2221 "publicKey": b64_key, 2222 "publicKeyAlgorithm": -8i8, 2223 "attestationObject": b64_aobj, 2224 }, 2225 "clientExtensionResults": { 2226 "prf": {} 2227 }, 2228 "type": "public-key" 2229 }) 2230 .to_string() 2231 .as_str() 2232 ) 2233 .unwrap_err() 2234 .to_string() 2235 .into_bytes() 2236 .get(..err.len()), 2237 Some(err.as_slice()) 2238 ); 2239 // `true` `enabled`. 2240 assert!( 2241 serde_json::from_str::<RegistrationRelaxed>( 2242 serde_json::json!({ 2243 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2244 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2245 "response": { 2246 "clientDataJSON": b64_cdata_json, 2247 "authenticatorData": b64_adata, 2248 "transports": [], 2249 "publicKey": b64_key, 2250 "publicKeyAlgorithm": -8i8, 2251 "attestationObject": b64_aobj, 2252 }, 2253 "clientExtensionResults": { 2254 "prf": { 2255 "enabled": true 2256 } 2257 }, 2258 "type": "public-key" 2259 }) 2260 .to_string() 2261 .as_str() 2262 ) 2263 .is_ok_and(|reg| reg.0.client_extension_results.cred_props.is_none() 2264 && reg 2265 .0 2266 .client_extension_results 2267 .prf 2268 .is_some_and(|prf| prf.enabled)) 2269 ); 2270 // `false` `enabled`. 2271 assert!( 2272 serde_json::from_str::<RegistrationRelaxed>( 2273 serde_json::json!({ 2274 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2275 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2276 "response": { 2277 "clientDataJSON": b64_cdata_json, 2278 "authenticatorData": b64_adata, 2279 "transports": [], 2280 "publicKey": b64_key, 2281 "publicKeyAlgorithm": -8i8, 2282 "attestationObject": b64_aobj, 2283 }, 2284 "clientExtensionResults": { 2285 "prf": { 2286 "enabled": false, 2287 } 2288 }, 2289 "type": "public-key" 2290 }) 2291 .to_string() 2292 .as_str() 2293 ) 2294 .is_ok_and(|reg| reg.0.client_extension_results.cred_props.is_none() 2295 && reg 2296 .0 2297 .client_extension_results 2298 .prf 2299 .is_some_and(|prf| !prf.enabled)) 2300 ); 2301 // Invalid `enabled`. 2302 err = Error::invalid_type(Unexpected::Unsigned(3), &"a boolean") 2303 .to_string() 2304 .into_bytes(); 2305 assert_eq!( 2306 serde_json::from_str::<RegistrationRelaxed>( 2307 serde_json::json!({ 2308 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2309 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2310 "response": { 2311 "clientDataJSON": b64_cdata_json, 2312 "authenticatorData": b64_adata, 2313 "transports": [], 2314 "publicKey": b64_key, 2315 "publicKeyAlgorithm": -8i8, 2316 "attestationObject": b64_aobj, 2317 }, 2318 "clientExtensionResults": { 2319 "prf": { 2320 "enabled": 3u8 2321 } 2322 }, 2323 "type": "public-key" 2324 }) 2325 .to_string() 2326 .as_str() 2327 ) 2328 .unwrap_err() 2329 .to_string() 2330 .into_bytes() 2331 .get(..err.len()), 2332 Some(err.as_slice()) 2333 ); 2334 // `null` `results` with `enabled` `true`. 2335 assert!( 2336 serde_json::from_str::<RegistrationRelaxed>( 2337 serde_json::json!({ 2338 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2339 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2340 "response": { 2341 "clientDataJSON": b64_cdata_json, 2342 "authenticatorData": b64_adata, 2343 "transports": [], 2344 "publicKey": b64_key, 2345 "publicKeyAlgorithm": -8i8, 2346 "attestationObject": b64_aobj, 2347 }, 2348 "clientExtensionResults": { 2349 "prf": { 2350 "enabled": true, 2351 "results": null, 2352 } 2353 }, 2354 "type": "public-key" 2355 }) 2356 .to_string() 2357 .as_str() 2358 ) 2359 .is_ok_and(|reg| reg.0.client_extension_results.cred_props.is_none() 2360 && reg 2361 .0 2362 .client_extension_results 2363 .prf 2364 .is_some_and(|prf| prf.enabled)) 2365 ); 2366 // `null` `results` with `enabled` `false`. 2367 err = Error::custom( 2368 "prf must not have 'results', including a null 'results', if 'enabled' is false", 2369 ) 2370 .to_string() 2371 .into_bytes(); 2372 assert_eq!( 2373 serde_json::from_str::<RegistrationRelaxed>( 2374 serde_json::json!({ 2375 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2376 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2377 "response": { 2378 "clientDataJSON": b64_cdata_json, 2379 "authenticatorData": b64_adata, 2380 "transports": [], 2381 "publicKey": b64_key, 2382 "publicKeyAlgorithm": -8i8, 2383 "attestationObject": b64_aobj, 2384 }, 2385 "clientExtensionResults": { 2386 "prf": { 2387 "enabled": false, 2388 "results": null 2389 } 2390 }, 2391 "type": "public-key" 2392 }) 2393 .to_string() 2394 .as_str() 2395 ) 2396 .unwrap_err() 2397 .to_string() 2398 .into_bytes() 2399 .get(..err.len()), 2400 Some(err.as_slice()) 2401 ); 2402 // Duplicate field in `prf`. 2403 err = Error::duplicate_field("enabled").to_string().into_bytes(); 2404 assert_eq!( 2405 serde_json::from_str::<RegistrationRelaxed>( 2406 format!( 2407 "{{ 2408 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 2409 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 2410 \"response\": {{ 2411 \"clientDataJSON\": \"{b64_cdata_json}\", 2412 \"authenticatorData\": \"{b64_adata}\", 2413 \"transports\": [], 2414 \"publicKey\": \"{b64_key}\", 2415 \"publicKeyAlgorithm\": -8, 2416 \"attestationObject\": \"{b64_aobj}\" 2417 }}, 2418 \"clientExtensionResults\": {{ 2419 \"prf\": {{ 2420 \"enabled\": true, 2421 \"enabled\": true 2422 }} 2423 }}, 2424 \"type\": \"public-key\" 2425 }}" 2426 ) 2427 .as_str() 2428 ) 2429 .unwrap_err() 2430 .to_string() 2431 .into_bytes() 2432 .get(..err.len()), 2433 Some(err.as_slice()) 2434 ); 2435 // Missing `first`. 2436 drop( 2437 serde_json::from_str::<RegistrationRelaxed>( 2438 serde_json::json!({ 2439 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2440 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2441 "response": { 2442 "clientDataJSON": b64_cdata_json, 2443 "authenticatorData": b64_adata, 2444 "transports": [], 2445 "publicKey": b64_key, 2446 "publicKeyAlgorithm": -8i8, 2447 "attestationObject": b64_aobj, 2448 }, 2449 "clientExtensionResults": { 2450 "prf": { 2451 "enabled": true, 2452 "results": {}, 2453 } 2454 }, 2455 "type": "public-key" 2456 }) 2457 .to_string() 2458 .as_str(), 2459 ) 2460 .unwrap(), 2461 ); 2462 // `null` `first`. 2463 assert!( 2464 serde_json::from_str::<RegistrationRelaxed>( 2465 serde_json::json!({ 2466 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2467 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2468 "response": { 2469 "clientDataJSON": b64_cdata_json, 2470 "authenticatorData": b64_adata, 2471 "transports": [], 2472 "publicKey": b64_key, 2473 "publicKeyAlgorithm": -8i8, 2474 "attestationObject": b64_aobj, 2475 }, 2476 "clientExtensionResults": { 2477 "prf": { 2478 "enabled": true, 2479 "results": { 2480 "first": null 2481 }, 2482 } 2483 }, 2484 "type": "public-key" 2485 }) 2486 .to_string() 2487 .as_str() 2488 ) 2489 .is_ok_and(|reg| reg.0.client_extension_results.cred_props.is_none() 2490 && reg 2491 .0 2492 .client_extension_results 2493 .prf 2494 .is_some_and(|prf| prf.enabled)) 2495 ); 2496 // `null` `second`. 2497 assert!( 2498 serde_json::from_str::<RegistrationRelaxed>( 2499 serde_json::json!({ 2500 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2501 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2502 "response": { 2503 "clientDataJSON": b64_cdata_json, 2504 "authenticatorData": b64_adata, 2505 "transports": [], 2506 "publicKey": b64_key, 2507 "publicKeyAlgorithm": -8i8, 2508 "attestationObject": b64_aobj, 2509 }, 2510 "clientExtensionResults": { 2511 "prf": { 2512 "enabled": true, 2513 "results": { 2514 "first": null, 2515 "second": null 2516 }, 2517 } 2518 }, 2519 "type": "public-key" 2520 }) 2521 .to_string() 2522 .as_str() 2523 ) 2524 .is_ok_and(|reg| reg.0.client_extension_results.cred_props.is_none() 2525 && reg 2526 .0 2527 .client_extension_results 2528 .prf 2529 .is_some_and(|prf| prf.enabled)) 2530 ); 2531 // Non-`null` `first`. 2532 err = Error::invalid_type(Unexpected::Option, &"null") 2533 .to_string() 2534 .into_bytes(); 2535 assert_eq!( 2536 serde_json::from_str::<RegistrationRelaxed>( 2537 serde_json::json!({ 2538 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2539 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2540 "response": { 2541 "clientDataJSON": b64_cdata_json, 2542 "authenticatorData": b64_adata, 2543 "transports": [], 2544 "publicKey": b64_key, 2545 "publicKeyAlgorithm": -8i8, 2546 "attestationObject": b64_aobj, 2547 }, 2548 "clientExtensionResults": { 2549 "prf": { 2550 "enabled": true, 2551 "results": { 2552 "first": "" 2553 }, 2554 } 2555 }, 2556 "type": "public-key" 2557 }) 2558 .to_string() 2559 .as_str() 2560 ) 2561 .unwrap_err() 2562 .to_string() 2563 .into_bytes() 2564 .get(..err.len()), 2565 Some(err.as_slice()) 2566 ); 2567 // Non-`null` `second`. 2568 err = Error::invalid_type(Unexpected::Option, &"null") 2569 .to_string() 2570 .into_bytes(); 2571 assert_eq!( 2572 serde_json::from_str::<RegistrationRelaxed>( 2573 serde_json::json!({ 2574 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2575 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2576 "response": { 2577 "clientDataJSON": b64_cdata_json, 2578 "authenticatorData": b64_adata, 2579 "transports": [], 2580 "publicKey": b64_key, 2581 "publicKeyAlgorithm": -8i8, 2582 "attestationObject": b64_aobj, 2583 }, 2584 "clientExtensionResults": { 2585 "prf": { 2586 "enabled": true, 2587 "results": { 2588 "first": null, 2589 "second": "" 2590 }, 2591 } 2592 }, 2593 "type": "public-key" 2594 }) 2595 .to_string() 2596 .as_str() 2597 ) 2598 .unwrap_err() 2599 .to_string() 2600 .into_bytes() 2601 .get(..err.len()), 2602 Some(err.as_slice()) 2603 ); 2604 // Unknown `prf` field. 2605 drop( 2606 serde_json::from_str::<RegistrationRelaxed>( 2607 serde_json::json!({ 2608 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2609 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2610 "response": { 2611 "clientDataJSON": b64_cdata_json, 2612 "authenticatorData": b64_adata, 2613 "transports": [], 2614 "publicKey": b64_key, 2615 "publicKeyAlgorithm": -8i8, 2616 "attestationObject": b64_aobj, 2617 }, 2618 "clientExtensionResults": { 2619 "prf": { 2620 "enabled": true, 2621 "Results": null 2622 } 2623 }, 2624 "type": "public-key" 2625 }) 2626 .to_string() 2627 .as_str(), 2628 ) 2629 .unwrap(), 2630 ); 2631 // Unknown `results` field. 2632 drop( 2633 serde_json::from_str::<RegistrationRelaxed>( 2634 serde_json::json!({ 2635 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2636 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2637 "response": { 2638 "clientDataJSON": b64_cdata_json, 2639 "authenticatorData": b64_adata, 2640 "transports": [], 2641 "publicKey": b64_key, 2642 "publicKeyAlgorithm": -8i8, 2643 "attestationObject": b64_aobj, 2644 }, 2645 "clientExtensionResults": { 2646 "prf": { 2647 "enabled": true, 2648 "results": { 2649 "first": null, 2650 "Second": null 2651 } 2652 } 2653 }, 2654 "type": "public-key" 2655 }) 2656 .to_string() 2657 .as_str(), 2658 ) 2659 .unwrap(), 2660 ); 2661 // Duplicate field in `results`. 2662 err = Error::duplicate_field("first").to_string().into_bytes(); 2663 assert_eq!( 2664 serde_json::from_str::<RegistrationRelaxed>( 2665 format!( 2666 "{{ 2667 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 2668 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 2669 \"response\": {{ 2670 \"clientDataJSON\": \"{b64_cdata_json}\", 2671 \"authenticatorData\": \"{b64_adata}\", 2672 \"transports\": [], 2673 \"publicKey\": \"{b64_key}\", 2674 \"publicKeyAlgorithm\": -8, 2675 \"attestationObject\": \"{b64_aobj}\" 2676 }}, 2677 \"clientExtensionResults\": {{ 2678 \"prf\": {{ 2679 \"enabled\": true, 2680 \"results\": {{ 2681 \"first\": null, 2682 \"first\": null 2683 }} 2684 }} 2685 }}, 2686 \"type\": \"public-key\" 2687 }}" 2688 ) 2689 .as_str() 2690 ) 2691 .unwrap_err() 2692 .to_string() 2693 .into_bytes() 2694 .get(..err.len()), 2695 Some(err.as_slice()) 2696 ); 2697 } 2698 #[expect( 2699 clippy::assertions_on_result_states, 2700 clippy::unwrap_used, 2701 reason = "OK in tests" 2702 )] 2703 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 2704 #[expect(clippy::too_many_lines, reason = "a lot to test")] 2705 #[test] 2706 fn mldsa87_registration_deserialize_data_mismatch() { 2707 let c_data_json = serde_json::json!({}).to_string(); 2708 let att_obj: [u8; 2704] = [ 2709 cbor::MAP_3, 2710 cbor::TEXT_3, 2711 b'f', 2712 b'm', 2713 b't', 2714 cbor::TEXT_4, 2715 b'n', 2716 b'o', 2717 b'n', 2718 b'e', 2719 cbor::TEXT_7, 2720 b'a', 2721 b't', 2722 b't', 2723 b'S', 2724 b't', 2725 b'm', 2726 b't', 2727 cbor::MAP_0, 2728 cbor::TEXT_8, 2729 b'a', 2730 b'u', 2731 b't', 2732 b'h', 2733 b'D', 2734 b'a', 2735 b't', 2736 b'a', 2737 cbor::BYTES_INFO_25, 2738 10, 2739 113, 2740 // `rpIdHash`. 2741 0, 2742 0, 2743 0, 2744 0, 2745 0, 2746 0, 2747 0, 2748 0, 2749 0, 2750 0, 2751 0, 2752 0, 2753 0, 2754 0, 2755 0, 2756 0, 2757 0, 2758 0, 2759 0, 2760 0, 2761 0, 2762 0, 2763 0, 2764 0, 2765 0, 2766 0, 2767 0, 2768 0, 2769 0, 2770 0, 2771 0, 2772 0, 2773 // `flags`. 2774 0b0100_0101, 2775 // `signCount`. 2776 0, 2777 0, 2778 0, 2779 0, 2780 // `aaguid`. 2781 0, 2782 0, 2783 0, 2784 0, 2785 0, 2786 0, 2787 0, 2788 0, 2789 0, 2790 0, 2791 0, 2792 0, 2793 0, 2794 0, 2795 0, 2796 0, 2797 // `credentialIdLength`. 2798 0, 2799 16, 2800 // `credentialId`. 2801 0, 2802 0, 2803 0, 2804 0, 2805 0, 2806 0, 2807 0, 2808 0, 2809 0, 2810 0, 2811 0, 2812 0, 2813 0, 2814 0, 2815 0, 2816 0, 2817 // ML-DSA-87 COSE key. 2818 cbor::MAP_3, 2819 KTY, 2820 AKP, 2821 ALG, 2822 cbor::NEG_INFO_24, 2823 MLDSA87, 2824 // `pub`. 2825 cbor::NEG_ONE, 2826 cbor::BYTES_INFO_25, 2827 10, 2828 32, 2829 // Encoded key. 2830 1, 2831 1, 2832 1, 2833 1, 2834 1, 2835 1, 2836 1, 2837 1, 2838 1, 2839 1, 2840 1, 2841 1, 2842 1, 2843 1, 2844 1, 2845 1, 2846 1, 2847 1, 2848 1, 2849 1, 2850 1, 2851 1, 2852 1, 2853 1, 2854 1, 2855 1, 2856 1, 2857 1, 2858 1, 2859 1, 2860 1, 2861 1, 2862 1, 2863 1, 2864 1, 2865 1, 2866 1, 2867 1, 2868 1, 2869 1, 2870 1, 2871 1, 2872 1, 2873 1, 2874 1, 2875 1, 2876 1, 2877 1, 2878 1, 2879 1, 2880 1, 2881 1, 2882 1, 2883 1, 2884 1, 2885 1, 2886 1, 2887 1, 2888 1, 2889 1, 2890 1, 2891 1, 2892 1, 2893 1, 2894 1, 2895 1, 2896 1, 2897 1, 2898 1, 2899 1, 2900 1, 2901 1, 2902 1, 2903 1, 2904 1, 2905 1, 2906 1, 2907 1, 2908 1, 2909 1, 2910 1, 2911 1, 2912 1, 2913 1, 2914 1, 2915 1, 2916 1, 2917 1, 2918 1, 2919 1, 2920 1, 2921 1, 2922 1, 2923 1, 2924 1, 2925 1, 2926 1, 2927 1, 2928 1, 2929 1, 2930 1, 2931 1, 2932 1, 2933 1, 2934 1, 2935 1, 2936 1, 2937 1, 2938 1, 2939 1, 2940 1, 2941 1, 2942 1, 2943 1, 2944 1, 2945 1, 2946 1, 2947 1, 2948 1, 2949 1, 2950 1, 2951 1, 2952 1, 2953 1, 2954 1, 2955 1, 2956 1, 2957 1, 2958 1, 2959 1, 2960 1, 2961 1, 2962 1, 2963 1, 2964 1, 2965 1, 2966 1, 2967 1, 2968 1, 2969 1, 2970 1, 2971 1, 2972 1, 2973 1, 2974 1, 2975 1, 2976 1, 2977 1, 2978 1, 2979 1, 2980 1, 2981 1, 2982 1, 2983 1, 2984 1, 2985 1, 2986 1, 2987 1, 2988 1, 2989 1, 2990 1, 2991 1, 2992 1, 2993 1, 2994 1, 2995 1, 2996 1, 2997 1, 2998 1, 2999 1, 3000 1, 3001 1, 3002 1, 3003 1, 3004 1, 3005 1, 3006 1, 3007 1, 3008 1, 3009 1, 3010 1, 3011 1, 3012 1, 3013 1, 3014 1, 3015 1, 3016 1, 3017 1, 3018 1, 3019 1, 3020 1, 3021 1, 3022 1, 3023 1, 3024 1, 3025 1, 3026 1, 3027 1, 3028 1, 3029 1, 3030 1, 3031 1, 3032 1, 3033 1, 3034 1, 3035 1, 3036 1, 3037 1, 3038 1, 3039 1, 3040 1, 3041 1, 3042 1, 3043 1, 3044 1, 3045 1, 3046 1, 3047 1, 3048 1, 3049 1, 3050 1, 3051 1, 3052 1, 3053 1, 3054 1, 3055 1, 3056 1, 3057 1, 3058 1, 3059 1, 3060 1, 3061 1, 3062 1, 3063 1, 3064 1, 3065 1, 3066 1, 3067 1, 3068 1, 3069 1, 3070 1, 3071 1, 3072 1, 3073 1, 3074 1, 3075 1, 3076 1, 3077 1, 3078 1, 3079 1, 3080 1, 3081 1, 3082 1, 3083 1, 3084 1, 3085 1, 3086 1, 3087 1, 3088 1, 3089 1, 3090 1, 3091 1, 3092 1, 3093 1, 3094 1, 3095 1, 3096 1, 3097 1, 3098 1, 3099 1, 3100 1, 3101 1, 3102 1, 3103 1, 3104 1, 3105 1, 3106 1, 3107 1, 3108 1, 3109 1, 3110 1, 3111 1, 3112 1, 3113 1, 3114 1, 3115 1, 3116 1, 3117 1, 3118 1, 3119 1, 3120 1, 3121 1, 3122 1, 3123 1, 3124 1, 3125 1, 3126 1, 3127 1, 3128 1, 3129 1, 3130 1, 3131 1, 3132 1, 3133 1, 3134 1, 3135 1, 3136 1, 3137 1, 3138 1, 3139 1, 3140 1, 3141 1, 3142 1, 3143 1, 3144 1, 3145 1, 3146 1, 3147 1, 3148 1, 3149 1, 3150 1, 3151 1, 3152 1, 3153 1, 3154 1, 3155 1, 3156 1, 3157 1, 3158 1, 3159 1, 3160 1, 3161 1, 3162 1, 3163 1, 3164 1, 3165 1, 3166 1, 3167 1, 3168 1, 3169 1, 3170 1, 3171 1, 3172 1, 3173 1, 3174 1, 3175 1, 3176 1, 3177 1, 3178 1, 3179 1, 3180 1, 3181 1, 3182 1, 3183 1, 3184 1, 3185 1, 3186 1, 3187 1, 3188 1, 3189 1, 3190 1, 3191 1, 3192 1, 3193 1, 3194 1, 3195 1, 3196 1, 3197 1, 3198 1, 3199 1, 3200 1, 3201 1, 3202 1, 3203 1, 3204 1, 3205 1, 3206 1, 3207 1, 3208 1, 3209 1, 3210 1, 3211 1, 3212 1, 3213 1, 3214 1, 3215 1, 3216 1, 3217 1, 3218 1, 3219 1, 3220 1, 3221 1, 3222 1, 3223 1, 3224 1, 3225 1, 3226 1, 3227 1, 3228 1, 3229 1, 3230 1, 3231 1, 3232 1, 3233 1, 3234 1, 3235 1, 3236 1, 3237 1, 3238 1, 3239 1, 3240 1, 3241 1, 3242 1, 3243 1, 3244 1, 3245 1, 3246 1, 3247 1, 3248 1, 3249 1, 3250 1, 3251 1, 3252 1, 3253 1, 3254 1, 3255 1, 3256 1, 3257 1, 3258 1, 3259 1, 3260 1, 3261 1, 3262 1, 3263 1, 3264 1, 3265 1, 3266 1, 3267 1, 3268 1, 3269 1, 3270 1, 3271 1, 3272 1, 3273 1, 3274 1, 3275 1, 3276 1, 3277 1, 3278 1, 3279 1, 3280 1, 3281 1, 3282 1, 3283 1, 3284 1, 3285 1, 3286 1, 3287 1, 3288 1, 3289 1, 3290 1, 3291 1, 3292 1, 3293 1, 3294 1, 3295 1, 3296 1, 3297 1, 3298 1, 3299 1, 3300 1, 3301 1, 3302 1, 3303 1, 3304 1, 3305 1, 3306 1, 3307 1, 3308 1, 3309 1, 3310 1, 3311 1, 3312 1, 3313 1, 3314 1, 3315 1, 3316 1, 3317 1, 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 1, 3351 1, 3352 1, 3353 1, 3354 1, 3355 1, 3356 1, 3357 1, 3358 1, 3359 1, 3360 1, 3361 1, 3362 1, 3363 1, 3364 1, 3365 1, 3366 1, 3367 1, 3368 1, 3369 1, 3370 1, 3371 1, 3372 1, 3373 1, 3374 1, 3375 1, 3376 1, 3377 1, 3378 1, 3379 1, 3380 1, 3381 1, 3382 1, 3383 1, 3384 1, 3385 1, 3386 1, 3387 1, 3388 1, 3389 1, 3390 1, 3391 1, 3392 1, 3393 1, 3394 1, 3395 1, 3396 1, 3397 1, 3398 1, 3399 1, 3400 1, 3401 1, 3402 1, 3403 1, 3404 1, 3405 1, 3406 1, 3407 1, 3408 1, 3409 1, 3410 1, 3411 1, 3412 1, 3413 1, 3414 1, 3415 1, 3416 1, 3417 1, 3418 1, 3419 1, 3420 1, 3421 1, 3422 1, 3423 1, 3424 1, 3425 1, 3426 1, 3427 1, 3428 1, 3429 1, 3430 1, 3431 1, 3432 1, 3433 1, 3434 1, 3435 1, 3436 1, 3437 1, 3438 1, 3439 1, 3440 1, 3441 1, 3442 1, 3443 1, 3444 1, 3445 1, 3446 1, 3447 1, 3448 1, 3449 1, 3450 1, 3451 1, 3452 1, 3453 1, 3454 1, 3455 1, 3456 1, 3457 1, 3458 1, 3459 1, 3460 1, 3461 1, 3462 1, 3463 1, 3464 1, 3465 1, 3466 1, 3467 1, 3468 1, 3469 1, 3470 1, 3471 1, 3472 1, 3473 1, 3474 1, 3475 1, 3476 1, 3477 1, 3478 1, 3479 1, 3480 1, 3481 1, 3482 1, 3483 1, 3484 1, 3485 1, 3486 1, 3487 1, 3488 1, 3489 1, 3490 1, 3491 1, 3492 1, 3493 1, 3494 1, 3495 1, 3496 1, 3497 1, 3498 1, 3499 1, 3500 1, 3501 1, 3502 1, 3503 1, 3504 1, 3505 1, 3506 1, 3507 1, 3508 1, 3509 1, 3510 1, 3511 1, 3512 1, 3513 1, 3514 1, 3515 1, 3516 1, 3517 1, 3518 1, 3519 1, 3520 1, 3521 1, 3522 1, 3523 1, 3524 1, 3525 1, 3526 1, 3527 1, 3528 1, 3529 1, 3530 1, 3531 1, 3532 1, 3533 1, 3534 1, 3535 1, 3536 1, 3537 1, 3538 1, 3539 1, 3540 1, 3541 1, 3542 1, 3543 1, 3544 1, 3545 1, 3546 1, 3547 1, 3548 1, 3549 1, 3550 1, 3551 1, 3552 1, 3553 1, 3554 1, 3555 1, 3556 1, 3557 1, 3558 1, 3559 1, 3560 1, 3561 1, 3562 1, 3563 1, 3564 1, 3565 1, 3566 1, 3567 1, 3568 1, 3569 1, 3570 1, 3571 1, 3572 1, 3573 1, 3574 1, 3575 1, 3576 1, 3577 1, 3578 1, 3579 1, 3580 1, 3581 1, 3582 1, 3583 1, 3584 1, 3585 1, 3586 1, 3587 1, 3588 1, 3589 1, 3590 1, 3591 1, 3592 1, 3593 1, 3594 1, 3595 1, 3596 1, 3597 1, 3598 1, 3599 1, 3600 1, 3601 1, 3602 1, 3603 1, 3604 1, 3605 1, 3606 1, 3607 1, 3608 1, 3609 1, 3610 1, 3611 1, 3612 1, 3613 1, 3614 1, 3615 1, 3616 1, 3617 1, 3618 1, 3619 1, 3620 1, 3621 1, 3622 1, 3623 1, 3624 1, 3625 1, 3626 1, 3627 1, 3628 1, 3629 1, 3630 1, 3631 1, 3632 1, 3633 1, 3634 1, 3635 1, 3636 1, 3637 1, 3638 1, 3639 1, 3640 1, 3641 1, 3642 1, 3643 1, 3644 1, 3645 1, 3646 1, 3647 1, 3648 1, 3649 1, 3650 1, 3651 1, 3652 1, 3653 1, 3654 1, 3655 1, 3656 1, 3657 1, 3658 1, 3659 1, 3660 1, 3661 1, 3662 1, 3663 1, 3664 1, 3665 1, 3666 1, 3667 1, 3668 1, 3669 1, 3670 1, 3671 1, 3672 1, 3673 1, 3674 1, 3675 1, 3676 1, 3677 1, 3678 1, 3679 1, 3680 1, 3681 1, 3682 1, 3683 1, 3684 1, 3685 1, 3686 1, 3687 1, 3688 1, 3689 1, 3690 1, 3691 1, 3692 1, 3693 1, 3694 1, 3695 1, 3696 1, 3697 1, 3698 1, 3699 1, 3700 1, 3701 1, 3702 1, 3703 1, 3704 1, 3705 1, 3706 1, 3707 1, 3708 1, 3709 1, 3710 1, 3711 1, 3712 1, 3713 1, 3714 1, 3715 1, 3716 1, 3717 1, 3718 1, 3719 1, 3720 1, 3721 1, 3722 1, 3723 1, 3724 1, 3725 1, 3726 1, 3727 1, 3728 1, 3729 1, 3730 1, 3731 1, 3732 1, 3733 1, 3734 1, 3735 1, 3736 1, 3737 1, 3738 1, 3739 1, 3740 1, 3741 1, 3742 1, 3743 1, 3744 1, 3745 1, 3746 1, 3747 1, 3748 1, 3749 1, 3750 1, 3751 1, 3752 1, 3753 1, 3754 1, 3755 1, 3756 1, 3757 1, 3758 1, 3759 1, 3760 1, 3761 1, 3762 1, 3763 1, 3764 1, 3765 1, 3766 1, 3767 1, 3768 1, 3769 1, 3770 1, 3771 1, 3772 1, 3773 1, 3774 1, 3775 1, 3776 1, 3777 1, 3778 1, 3779 1, 3780 1, 3781 1, 3782 1, 3783 1, 3784 1, 3785 1, 3786 1, 3787 1, 3788 1, 3789 1, 3790 1, 3791 1, 3792 1, 3793 1, 3794 1, 3795 1, 3796 1, 3797 1, 3798 1, 3799 1, 3800 1, 3801 1, 3802 1, 3803 1, 3804 1, 3805 1, 3806 1, 3807 1, 3808 1, 3809 1, 3810 1, 3811 1, 3812 1, 3813 1, 3814 1, 3815 1, 3816 1, 3817 1, 3818 1, 3819 1, 3820 1, 3821 1, 3822 1, 3823 1, 3824 1, 3825 1, 3826 1, 3827 1, 3828 1, 3829 1, 3830 1, 3831 1, 3832 1, 3833 1, 3834 1, 3835 1, 3836 1, 3837 1, 3838 1, 3839 1, 3840 1, 3841 1, 3842 1, 3843 1, 3844 1, 3845 1, 3846 1, 3847 1, 3848 1, 3849 1, 3850 1, 3851 1, 3852 1, 3853 1, 3854 1, 3855 1, 3856 1, 3857 1, 3858 1, 3859 1, 3860 1, 3861 1, 3862 1, 3863 1, 3864 1, 3865 1, 3866 1, 3867 1, 3868 1, 3869 1, 3870 1, 3871 1, 3872 1, 3873 1, 3874 1, 3875 1, 3876 1, 3877 1, 3878 1, 3879 1, 3880 1, 3881 1, 3882 1, 3883 1, 3884 1, 3885 1, 3886 1, 3887 1, 3888 1, 3889 1, 3890 1, 3891 1, 3892 1, 3893 1, 3894 1, 3895 1, 3896 1, 3897 1, 3898 1, 3899 1, 3900 1, 3901 1, 3902 1, 3903 1, 3904 1, 3905 1, 3906 1, 3907 1, 3908 1, 3909 1, 3910 1, 3911 1, 3912 1, 3913 1, 3914 1, 3915 1, 3916 1, 3917 1, 3918 1, 3919 1, 3920 1, 3921 1, 3922 1, 3923 1, 3924 1, 3925 1, 3926 1, 3927 1, 3928 1, 3929 1, 3930 1, 3931 1, 3932 1, 3933 1, 3934 1, 3935 1, 3936 1, 3937 1, 3938 1, 3939 1, 3940 1, 3941 1, 3942 1, 3943 1, 3944 1, 3945 1, 3946 1, 3947 1, 3948 1, 3949 1, 3950 1, 3951 1, 3952 1, 3953 1, 3954 1, 3955 1, 3956 1, 3957 1, 3958 1, 3959 1, 3960 1, 3961 1, 3962 1, 3963 1, 3964 1, 3965 1, 3966 1, 3967 1, 3968 1, 3969 1, 3970 1, 3971 1, 3972 1, 3973 1, 3974 1, 3975 1, 3976 1, 3977 1, 3978 1, 3979 1, 3980 1, 3981 1, 3982 1, 3983 1, 3984 1, 3985 1, 3986 1, 3987 1, 3988 1, 3989 1, 3990 1, 3991 1, 3992 1, 3993 1, 3994 1, 3995 1, 3996 1, 3997 1, 3998 1, 3999 1, 4000 1, 4001 1, 4002 1, 4003 1, 4004 1, 4005 1, 4006 1, 4007 1, 4008 1, 4009 1, 4010 1, 4011 1, 4012 1, 4013 1, 4014 1, 4015 1, 4016 1, 4017 1, 4018 1, 4019 1, 4020 1, 4021 1, 4022 1, 4023 1, 4024 1, 4025 1, 4026 1, 4027 1, 4028 1, 4029 1, 4030 1, 4031 1, 4032 1, 4033 1, 4034 1, 4035 1, 4036 1, 4037 1, 4038 1, 4039 1, 4040 1, 4041 1, 4042 1, 4043 1, 4044 1, 4045 1, 4046 1, 4047 1, 4048 1, 4049 1, 4050 1, 4051 1, 4052 1, 4053 1, 4054 1, 4055 1, 4056 1, 4057 1, 4058 1, 4059 1, 4060 1, 4061 1, 4062 1, 4063 1, 4064 1, 4065 1, 4066 1, 4067 1, 4068 1, 4069 1, 4070 1, 4071 1, 4072 1, 4073 1, 4074 1, 4075 1, 4076 1, 4077 1, 4078 1, 4079 1, 4080 1, 4081 1, 4082 1, 4083 1, 4084 1, 4085 1, 4086 1, 4087 1, 4088 1, 4089 1, 4090 1, 4091 1, 4092 1, 4093 1, 4094 1, 4095 1, 4096 1, 4097 1, 4098 1, 4099 1, 4100 1, 4101 1, 4102 1, 4103 1, 4104 1, 4105 1, 4106 1, 4107 1, 4108 1, 4109 1, 4110 1, 4111 1, 4112 1, 4113 1, 4114 1, 4115 1, 4116 1, 4117 1, 4118 1, 4119 1, 4120 1, 4121 1, 4122 1, 4123 1, 4124 1, 4125 1, 4126 1, 4127 1, 4128 1, 4129 1, 4130 1, 4131 1, 4132 1, 4133 1, 4134 1, 4135 1, 4136 1, 4137 1, 4138 1, 4139 1, 4140 1, 4141 1, 4142 1, 4143 1, 4144 1, 4145 1, 4146 1, 4147 1, 4148 1, 4149 1, 4150 1, 4151 1, 4152 1, 4153 1, 4154 1, 4155 1, 4156 1, 4157 1, 4158 1, 4159 1, 4160 1, 4161 1, 4162 1, 4163 1, 4164 1, 4165 1, 4166 1, 4167 1, 4168 1, 4169 1, 4170 1, 4171 1, 4172 1, 4173 1, 4174 1, 4175 1, 4176 1, 4177 1, 4178 1, 4179 1, 4180 1, 4181 1, 4182 1, 4183 1, 4184 1, 4185 1, 4186 1, 4187 1, 4188 1, 4189 1, 4190 1, 4191 1, 4192 1, 4193 1, 4194 1, 4195 1, 4196 1, 4197 1, 4198 1, 4199 1, 4200 1, 4201 1, 4202 1, 4203 1, 4204 1, 4205 1, 4206 1, 4207 1, 4208 1, 4209 1, 4210 1, 4211 1, 4212 1, 4213 1, 4214 1, 4215 1, 4216 1, 4217 1, 4218 1, 4219 1, 4220 1, 4221 1, 4222 1, 4223 1, 4224 1, 4225 1, 4226 1, 4227 1, 4228 1, 4229 1, 4230 1, 4231 1, 4232 1, 4233 1, 4234 1, 4235 1, 4236 1, 4237 1, 4238 1, 4239 1, 4240 1, 4241 1, 4242 1, 4243 1, 4244 1, 4245 1, 4246 1, 4247 1, 4248 1, 4249 1, 4250 1, 4251 1, 4252 1, 4253 1, 4254 1, 4255 1, 4256 1, 4257 1, 4258 1, 4259 1, 4260 1, 4261 1, 4262 1, 4263 1, 4264 1, 4265 1, 4266 1, 4267 1, 4268 1, 4269 1, 4270 1, 4271 1, 4272 1, 4273 1, 4274 1, 4275 1, 4276 1, 4277 1, 4278 1, 4279 1, 4280 1, 4281 1, 4282 1, 4283 1, 4284 1, 4285 1, 4286 1, 4287 1, 4288 1, 4289 1, 4290 1, 4291 1, 4292 1, 4293 1, 4294 1, 4295 1, 4296 1, 4297 1, 4298 1, 4299 1, 4300 1, 4301 1, 4302 1, 4303 1, 4304 1, 4305 1, 4306 1, 4307 1, 4308 1, 4309 1, 4310 1, 4311 1, 4312 1, 4313 1, 4314 1, 4315 1, 4316 1, 4317 1, 4318 1, 4319 1, 4320 1, 4321 1, 4322 1, 4323 1, 4324 1, 4325 1, 4326 1, 4327 1, 4328 1, 4329 1, 4330 1, 4331 1, 4332 1, 4333 1, 4334 1, 4335 1, 4336 1, 4337 1, 4338 1, 4339 1, 4340 1, 4341 1, 4342 1, 4343 1, 4344 1, 4345 1, 4346 1, 4347 1, 4348 1, 4349 1, 4350 1, 4351 1, 4352 1, 4353 1, 4354 1, 4355 1, 4356 1, 4357 1, 4358 1, 4359 1, 4360 1, 4361 1, 4362 1, 4363 1, 4364 1, 4365 1, 4366 1, 4367 1, 4368 1, 4369 1, 4370 1, 4371 1, 4372 1, 4373 1, 4374 1, 4375 1, 4376 1, 4377 1, 4378 1, 4379 1, 4380 1, 4381 1, 4382 1, 4383 1, 4384 1, 4385 1, 4386 1, 4387 1, 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 ]; 5423 let pub_key = MlDsaVerKey::<MlDsa87>::decode(&[1u8; 2592].into()) 5424 .to_public_key_der() 5425 .unwrap(); 5426 let att_obj_len = att_obj.len(); 5427 let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes()); 5428 let b64_adata = base64url_nopad::encode(&att_obj[att_obj_len - 2673..]); 5429 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 5430 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 5431 // Base case is valid. 5432 assert!( 5433 serde_json::from_str::<RegistrationRelaxed>( 5434 serde_json::json!({ 5435 "id": "AAAAAAAAAAAAAAAAAAAAAA", 5436 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 5437 "response": { 5438 "clientDataJSON": b64_cdata_json, 5439 "authenticatorData": b64_adata, 5440 "transports": [], 5441 "publicKey": b64_key, 5442 "publicKeyAlgorithm": -50i8, 5443 "attestationObject": b64_aobj, 5444 }, 5445 "clientExtensionResults": {}, 5446 "type": "public-key" 5447 }) 5448 .to_string() 5449 .as_str() 5450 ) 5451 .is_ok_and( 5452 |reg| reg.0.response.client_data_json == c_data_json.as_bytes() 5453 && reg.0.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 5454 && reg.0.response.attestation_object_and_c_data_hash[att_obj_len..] 5455 == *Sha256::digest(c_data_json.as_bytes()) 5456 && reg.0.response.transports.is_empty() 5457 && matches!( 5458 reg.0.authenticator_attachment, 5459 AuthenticatorAttachment::None 5460 ) 5461 && reg.0.client_extension_results.cred_props.is_none() 5462 && reg.0.client_extension_results.prf.is_none() 5463 ) 5464 ); 5465 // `publicKeyAlgorithm` mismatch. 5466 let mut err = Error::invalid_value( 5467 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()), 5468 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa87).as_str() 5469 ) 5470 .to_string().into_bytes(); 5471 assert_eq!( 5472 serde_json::from_str::<RegistrationRelaxed>( 5473 serde_json::json!({ 5474 "id": "AAAAAAAAAAAAAAAAAAAAAA", 5475 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 5476 "response": { 5477 "clientDataJSON": b64_cdata_json, 5478 "authenticatorData": b64_adata, 5479 "transports": [], 5480 "publicKey": b64_key, 5481 "publicKeyAlgorithm": -8i8, 5482 "attestationObject": b64_aobj, 5483 }, 5484 "clientExtensionResults": {}, 5485 "type": "public-key" 5486 }) 5487 .to_string() 5488 .as_str() 5489 ) 5490 .unwrap_err() 5491 .to_string() 5492 .into_bytes() 5493 .get(..err.len()), 5494 Some(err.as_slice()) 5495 ); 5496 // Missing `publicKeyAlgorithm`. 5497 drop( 5498 serde_json::from_str::<RegistrationRelaxed>( 5499 serde_json::json!({ 5500 "id": "AAAAAAAAAAAAAAAAAAAAAA", 5501 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 5502 "response": { 5503 "clientDataJSON": b64_cdata_json, 5504 "authenticatorData": b64_adata, 5505 "transports": [], 5506 "publicKey": b64_key, 5507 "attestationObject": b64_aobj, 5508 }, 5509 "clientExtensionResults": {}, 5510 "type": "public-key" 5511 }) 5512 .to_string() 5513 .as_str(), 5514 ) 5515 .unwrap(), 5516 ); 5517 // `null` `publicKeyAlgorithm`. 5518 drop( 5519 serde_json::from_str::<RegistrationRelaxed>( 5520 serde_json::json!({ 5521 "id": "AAAAAAAAAAAAAAAAAAAAAA", 5522 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 5523 "response": { 5524 "clientDataJSON": b64_cdata_json, 5525 "authenticatorData": b64_adata, 5526 "transports": [], 5527 "publicKey": b64_key, 5528 "publicKeyAlgorithm": null, 5529 "attestationObject": b64_aobj, 5530 }, 5531 "clientExtensionResults": {}, 5532 "type": "public-key" 5533 }) 5534 .to_string() 5535 .as_str(), 5536 ) 5537 .unwrap(), 5538 ); 5539 // `publicKey` mismatch. 5540 let bad_pub_key = MlDsaVerKey::<MlDsa87>::decode(&[2; 2592].into()); 5541 err = Error::invalid_value( 5542 Unexpected::Bytes([0; 32].as_slice()), 5543 &format!( 5544 "DER-encoded public key to match the public key within the attestation object: MlDsa87(MlDsa87PubKey({:?}))", 5545 &[1u8; 2592] 5546 ) 5547 .as_str(), 5548 ) 5549 .to_string().into_bytes(); 5550 assert_eq!(serde_json::from_str::<RegistrationRelaxed>( 5551 serde_json::json!({ 5552 "id": "AAAAAAAAAAAAAAAAAAAAAA", 5553 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 5554 "response": { 5555 "clientDataJSON": b64_cdata_json, 5556 "authenticatorData": b64_adata, 5557 "transports": [], 5558 "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()), 5559 "publicKeyAlgorithm": -50i8, 5560 "attestationObject": b64_aobj, 5561 }, 5562 "clientExtensionResults": {}, 5563 "type": "public-key" 5564 }) 5565 .to_string() 5566 .as_str() 5567 ) 5568 .unwrap_err().to_string().into_bytes().get(..err.len()), 5569 Some(err.as_slice()) 5570 ); 5571 // Missing `publicKey` is allowed when not using EdDSA, ES256, or RS256. 5572 assert!( 5573 serde_json::from_str::<RegistrationRelaxed>( 5574 serde_json::json!({ 5575 "id": "AAAAAAAAAAAAAAAAAAAAAA", 5576 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 5577 "response": { 5578 "clientDataJSON": b64_cdata_json, 5579 "authenticatorData": b64_adata, 5580 "transports": [], 5581 "publicKeyAlgorithm": -50i8, 5582 "attestationObject": b64_aobj, 5583 }, 5584 "clientExtensionResults": {}, 5585 "type": "public-key" 5586 }) 5587 .to_string() 5588 .as_str() 5589 ) 5590 .is_ok() 5591 ); 5592 // `publicKeyAlgorithm` mismatch when `publicKey` does not exist. 5593 err = Error::invalid_value( 5594 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 5595 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa87).as_str() 5596 ) 5597 .to_string().into_bytes(); 5598 assert_eq!( 5599 serde_json::from_str::<RegistrationRelaxed>( 5600 serde_json::json!({ 5601 "id": "AAAAAAAAAAAAAAAAAAAAAA", 5602 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 5603 "response": { 5604 "clientDataJSON": b64_cdata_json, 5605 "authenticatorData": b64_adata, 5606 "transports": [], 5607 "publicKeyAlgorithm": -7i8, 5608 "attestationObject": b64_aobj, 5609 }, 5610 "clientExtensionResults": {}, 5611 "type": "public-key" 5612 }) 5613 .to_string() 5614 .as_str() 5615 ) 5616 .unwrap_err() 5617 .to_string() 5618 .into_bytes() 5619 .get(..err.len()), 5620 Some(err.as_slice()) 5621 ); 5622 // `null` `publicKey` is allowed when not using EdDSA, ES256, or RS256. 5623 assert!( 5624 serde_json::from_str::<RegistrationRelaxed>( 5625 serde_json::json!({ 5626 "id": "AAAAAAAAAAAAAAAAAAAAAA", 5627 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 5628 "response": { 5629 "clientDataJSON": b64_cdata_json, 5630 "authenticatorData": b64_adata, 5631 "transports": [], 5632 "publicKey": null, 5633 "publicKeyAlgorithm": -50i8, 5634 "attestationObject": b64_aobj, 5635 }, 5636 "clientExtensionResults": {}, 5637 "type": "public-key" 5638 }) 5639 .to_string() 5640 .as_str() 5641 ) 5642 .is_ok() 5643 ); 5644 // `publicKeyAlgorithm` mismatch when `publicKey` is null. 5645 err = Error::invalid_value( 5646 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 5647 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa87).as_str() 5648 ) 5649 .to_string().into_bytes(); 5650 assert_eq!( 5651 serde_json::from_str::<RegistrationRelaxed>( 5652 serde_json::json!({ 5653 "id": "AAAAAAAAAAAAAAAAAAAAAA", 5654 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 5655 "response": { 5656 "clientDataJSON": b64_cdata_json, 5657 "authenticatorData": b64_adata, 5658 "transports": [], 5659 "publicKey": null, 5660 "publicKeyAlgorithm": -7i8, 5661 "attestationObject": b64_aobj, 5662 }, 5663 "clientExtensionResults": {}, 5664 "type": "public-key" 5665 }) 5666 .to_string() 5667 .as_str() 5668 ) 5669 .unwrap_err() 5670 .to_string() 5671 .into_bytes() 5672 .get(..err.len()), 5673 Some(err.as_slice()) 5674 ); 5675 } 5676 #[expect( 5677 clippy::assertions_on_result_states, 5678 clippy::unwrap_used, 5679 reason = "OK in tests" 5680 )] 5681 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 5682 #[expect(clippy::too_many_lines, reason = "a lot to test")] 5683 #[test] 5684 fn mldsa65_registration_deserialize_data_mismatch() { 5685 let c_data_json = serde_json::json!({}).to_string(); 5686 let att_obj: [u8; 2064] = [ 5687 cbor::MAP_3, 5688 cbor::TEXT_3, 5689 b'f', 5690 b'm', 5691 b't', 5692 cbor::TEXT_4, 5693 b'n', 5694 b'o', 5695 b'n', 5696 b'e', 5697 cbor::TEXT_7, 5698 b'a', 5699 b't', 5700 b't', 5701 b'S', 5702 b't', 5703 b'm', 5704 b't', 5705 cbor::MAP_0, 5706 cbor::TEXT_8, 5707 b'a', 5708 b'u', 5709 b't', 5710 b'h', 5711 b'D', 5712 b'a', 5713 b't', 5714 b'a', 5715 cbor::BYTES_INFO_25, 5716 7, 5717 241, 5718 // `rpIdHash`. 5719 0, 5720 0, 5721 0, 5722 0, 5723 0, 5724 0, 5725 0, 5726 0, 5727 0, 5728 0, 5729 0, 5730 0, 5731 0, 5732 0, 5733 0, 5734 0, 5735 0, 5736 0, 5737 0, 5738 0, 5739 0, 5740 0, 5741 0, 5742 0, 5743 0, 5744 0, 5745 0, 5746 0, 5747 0, 5748 0, 5749 0, 5750 0, 5751 // `flags`. 5752 0b0100_0101, 5753 // `signCount`. 5754 0, 5755 0, 5756 0, 5757 0, 5758 // `aaguid`. 5759 0, 5760 0, 5761 0, 5762 0, 5763 0, 5764 0, 5765 0, 5766 0, 5767 0, 5768 0, 5769 0, 5770 0, 5771 0, 5772 0, 5773 0, 5774 0, 5775 // `credentialIdLength`. 5776 0, 5777 16, 5778 // `credentialId`. 5779 0, 5780 0, 5781 0, 5782 0, 5783 0, 5784 0, 5785 0, 5786 0, 5787 0, 5788 0, 5789 0, 5790 0, 5791 0, 5792 0, 5793 0, 5794 0, 5795 // ML-DSA-65 COSE key. 5796 cbor::MAP_3, 5797 KTY, 5798 AKP, 5799 ALG, 5800 cbor::NEG_INFO_24, 5801 MLDSA65, 5802 // `pub`. 5803 cbor::NEG_ONE, 5804 cbor::BYTES_INFO_25, 5805 7, 5806 160, 5807 // Encoded key. 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 1, 6981 1, 6982 1, 6983 1, 6984 1, 6985 1, 6986 1, 6987 1, 6988 1, 6989 1, 6990 1, 6991 1, 6992 1, 6993 1, 6994 1, 6995 1, 6996 1, 6997 1, 6998 1, 6999 1, 7000 1, 7001 1, 7002 1, 7003 1, 7004 1, 7005 1, 7006 1, 7007 1, 7008 1, 7009 1, 7010 1, 7011 1, 7012 1, 7013 1, 7014 1, 7015 1, 7016 1, 7017 1, 7018 1, 7019 1, 7020 1, 7021 1, 7022 1, 7023 1, 7024 1, 7025 1, 7026 1, 7027 1, 7028 1, 7029 1, 7030 1, 7031 1, 7032 1, 7033 1, 7034 1, 7035 1, 7036 1, 7037 1, 7038 1, 7039 1, 7040 1, 7041 1, 7042 1, 7043 1, 7044 1, 7045 1, 7046 1, 7047 1, 7048 1, 7049 1, 7050 1, 7051 1, 7052 1, 7053 1, 7054 1, 7055 1, 7056 1, 7057 1, 7058 1, 7059 1, 7060 1, 7061 1, 7062 1, 7063 1, 7064 1, 7065 1, 7066 1, 7067 1, 7068 1, 7069 1, 7070 1, 7071 1, 7072 1, 7073 1, 7074 1, 7075 1, 7076 1, 7077 1, 7078 1, 7079 1, 7080 1, 7081 1, 7082 1, 7083 1, 7084 1, 7085 1, 7086 1, 7087 1, 7088 1, 7089 1, 7090 1, 7091 1, 7092 1, 7093 1, 7094 1, 7095 1, 7096 1, 7097 1, 7098 1, 7099 1, 7100 1, 7101 1, 7102 1, 7103 1, 7104 1, 7105 1, 7106 1, 7107 1, 7108 1, 7109 1, 7110 1, 7111 1, 7112 1, 7113 1, 7114 1, 7115 1, 7116 1, 7117 1, 7118 1, 7119 1, 7120 1, 7121 1, 7122 1, 7123 1, 7124 1, 7125 1, 7126 1, 7127 1, 7128 1, 7129 1, 7130 1, 7131 1, 7132 1, 7133 1, 7134 1, 7135 1, 7136 1, 7137 1, 7138 1, 7139 1, 7140 1, 7141 1, 7142 1, 7143 1, 7144 1, 7145 1, 7146 1, 7147 1, 7148 1, 7149 1, 7150 1, 7151 1, 7152 1, 7153 1, 7154 1, 7155 1, 7156 1, 7157 1, 7158 1, 7159 1, 7160 1, 7161 1, 7162 1, 7163 1, 7164 1, 7165 1, 7166 1, 7167 1, 7168 1, 7169 1, 7170 1, 7171 1, 7172 1, 7173 1, 7174 1, 7175 1, 7176 1, 7177 1, 7178 1, 7179 1, 7180 1, 7181 1, 7182 1, 7183 1, 7184 1, 7185 1, 7186 1, 7187 1, 7188 1, 7189 1, 7190 1, 7191 1, 7192 1, 7193 1, 7194 1, 7195 1, 7196 1, 7197 1, 7198 1, 7199 1, 7200 1, 7201 1, 7202 1, 7203 1, 7204 1, 7205 1, 7206 1, 7207 1, 7208 1, 7209 1, 7210 1, 7211 1, 7212 1, 7213 1, 7214 1, 7215 1, 7216 1, 7217 1, 7218 1, 7219 1, 7220 1, 7221 1, 7222 1, 7223 1, 7224 1, 7225 1, 7226 1, 7227 1, 7228 1, 7229 1, 7230 1, 7231 1, 7232 1, 7233 1, 7234 1, 7235 1, 7236 1, 7237 1, 7238 1, 7239 1, 7240 1, 7241 1, 7242 1, 7243 1, 7244 1, 7245 1, 7246 1, 7247 1, 7248 1, 7249 1, 7250 1, 7251 1, 7252 1, 7253 1, 7254 1, 7255 1, 7256 1, 7257 1, 7258 1, 7259 1, 7260 1, 7261 1, 7262 1, 7263 1, 7264 1, 7265 1, 7266 1, 7267 1, 7268 1, 7269 1, 7270 1, 7271 1, 7272 1, 7273 1, 7274 1, 7275 1, 7276 1, 7277 1, 7278 1, 7279 1, 7280 1, 7281 1, 7282 1, 7283 1, 7284 1, 7285 1, 7286 1, 7287 1, 7288 1, 7289 1, 7290 1, 7291 1, 7292 1, 7293 1, 7294 1, 7295 1, 7296 1, 7297 1, 7298 1, 7299 1, 7300 1, 7301 1, 7302 1, 7303 1, 7304 1, 7305 1, 7306 1, 7307 1, 7308 1, 7309 1, 7310 1, 7311 1, 7312 1, 7313 1, 7314 1, 7315 1, 7316 1, 7317 1, 7318 1, 7319 1, 7320 1, 7321 1, 7322 1, 7323 1, 7324 1, 7325 1, 7326 1, 7327 1, 7328 1, 7329 1, 7330 1, 7331 1, 7332 1, 7333 1, 7334 1, 7335 1, 7336 1, 7337 1, 7338 1, 7339 1, 7340 1, 7341 1, 7342 1, 7343 1, 7344 1, 7345 1, 7346 1, 7347 1, 7348 1, 7349 1, 7350 1, 7351 1, 7352 1, 7353 1, 7354 1, 7355 1, 7356 1, 7357 1, 7358 1, 7359 1, 7360 1, 7361 1, 7362 1, 7363 1, 7364 1, 7365 1, 7366 1, 7367 1, 7368 1, 7369 1, 7370 1, 7371 1, 7372 1, 7373 1, 7374 1, 7375 1, 7376 1, 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 ]; 7761 let pub_key = MlDsaVerKey::<MlDsa65>::decode(&[1u8; 1952].into()) 7762 .to_public_key_der() 7763 .unwrap(); 7764 let att_obj_len = att_obj.len(); 7765 let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes()); 7766 let b64_adata = base64url_nopad::encode(&att_obj[att_obj_len - 2033..]); 7767 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 7768 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 7769 // Base case is valid. 7770 assert!( 7771 serde_json::from_str::<RegistrationRelaxed>( 7772 serde_json::json!({ 7773 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7774 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7775 "response": { 7776 "clientDataJSON": b64_cdata_json, 7777 "authenticatorData": b64_adata, 7778 "transports": [], 7779 "publicKey": b64_key, 7780 "publicKeyAlgorithm": -49i8, 7781 "attestationObject": b64_aobj, 7782 }, 7783 "clientExtensionResults": {}, 7784 "type": "public-key" 7785 }) 7786 .to_string() 7787 .as_str() 7788 ) 7789 .is_ok_and( 7790 |reg| reg.0.response.client_data_json == c_data_json.as_bytes() 7791 && reg.0.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 7792 && reg.0.response.attestation_object_and_c_data_hash[att_obj_len..] 7793 == *Sha256::digest(c_data_json.as_bytes()) 7794 && reg.0.response.transports.is_empty() 7795 && matches!( 7796 reg.0.authenticator_attachment, 7797 AuthenticatorAttachment::None 7798 ) 7799 && reg.0.client_extension_results.cred_props.is_none() 7800 && reg.0.client_extension_results.prf.is_none() 7801 ) 7802 ); 7803 // `publicKeyAlgorithm` mismatch. 7804 let mut err = Error::invalid_value( 7805 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()), 7806 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa65).as_str() 7807 ) 7808 .to_string().into_bytes(); 7809 assert_eq!( 7810 serde_json::from_str::<RegistrationRelaxed>( 7811 serde_json::json!({ 7812 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7813 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7814 "response": { 7815 "clientDataJSON": b64_cdata_json, 7816 "authenticatorData": b64_adata, 7817 "transports": [], 7818 "publicKey": b64_key, 7819 "publicKeyAlgorithm": -8i8, 7820 "attestationObject": b64_aobj, 7821 }, 7822 "clientExtensionResults": {}, 7823 "type": "public-key" 7824 }) 7825 .to_string() 7826 .as_str() 7827 ) 7828 .unwrap_err() 7829 .to_string() 7830 .into_bytes() 7831 .get(..err.len()), 7832 Some(err.as_slice()) 7833 ); 7834 // Missing `publicKeyAlgorithm`. 7835 drop( 7836 serde_json::from_str::<RegistrationRelaxed>( 7837 serde_json::json!({ 7838 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7839 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7840 "response": { 7841 "clientDataJSON": b64_cdata_json, 7842 "authenticatorData": b64_adata, 7843 "transports": [], 7844 "publicKey": b64_key, 7845 "attestationObject": b64_aobj, 7846 }, 7847 "clientExtensionResults": {}, 7848 "type": "public-key" 7849 }) 7850 .to_string() 7851 .as_str(), 7852 ) 7853 .unwrap(), 7854 ); 7855 // `null` `publicKeyAlgorithm`. 7856 drop( 7857 serde_json::from_str::<RegistrationRelaxed>( 7858 serde_json::json!({ 7859 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7860 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7861 "response": { 7862 "clientDataJSON": b64_cdata_json, 7863 "authenticatorData": b64_adata, 7864 "transports": [], 7865 "publicKey": b64_key, 7866 "publicKeyAlgorithm": null, 7867 "attestationObject": b64_aobj, 7868 }, 7869 "clientExtensionResults": {}, 7870 "type": "public-key" 7871 }) 7872 .to_string() 7873 .as_str(), 7874 ) 7875 .unwrap(), 7876 ); 7877 // `publicKey` mismatch. 7878 let bad_pub_key = MlDsaVerKey::<MlDsa65>::decode(&[2; 1952].into()); 7879 err = Error::invalid_value( 7880 Unexpected::Bytes([0; 32].as_slice()), 7881 &format!( 7882 "DER-encoded public key to match the public key within the attestation object: MlDsa65(MlDsa65PubKey({:?}))", 7883 &[1u8; 1952] 7884 ) 7885 .as_str(), 7886 ) 7887 .to_string().into_bytes(); 7888 assert_eq!(serde_json::from_str::<RegistrationRelaxed>( 7889 serde_json::json!({ 7890 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7891 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7892 "response": { 7893 "clientDataJSON": b64_cdata_json, 7894 "authenticatorData": b64_adata, 7895 "transports": [], 7896 "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()), 7897 "publicKeyAlgorithm": -49i8, 7898 "attestationObject": b64_aobj, 7899 }, 7900 "clientExtensionResults": {}, 7901 "type": "public-key" 7902 }) 7903 .to_string() 7904 .as_str() 7905 ) 7906 .unwrap_err().to_string().into_bytes().get(..err.len()), 7907 Some(err.as_slice()) 7908 ); 7909 // Missing `publicKey` is allowed when not using EdDSA, ES256, or RS256. 7910 assert!( 7911 serde_json::from_str::<RegistrationRelaxed>( 7912 serde_json::json!({ 7913 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7914 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7915 "response": { 7916 "clientDataJSON": b64_cdata_json, 7917 "authenticatorData": b64_adata, 7918 "transports": [], 7919 "publicKeyAlgorithm": -49i8, 7920 "attestationObject": b64_aobj, 7921 }, 7922 "clientExtensionResults": {}, 7923 "type": "public-key" 7924 }) 7925 .to_string() 7926 .as_str() 7927 ) 7928 .is_ok() 7929 ); 7930 // `publicKeyAlgorithm` mismatch when `publicKey` does not exist. 7931 err = Error::invalid_value( 7932 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 7933 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa65).as_str() 7934 ) 7935 .to_string().into_bytes(); 7936 assert_eq!( 7937 serde_json::from_str::<RegistrationRelaxed>( 7938 serde_json::json!({ 7939 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7940 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7941 "response": { 7942 "clientDataJSON": b64_cdata_json, 7943 "authenticatorData": b64_adata, 7944 "transports": [], 7945 "publicKeyAlgorithm": -7i8, 7946 "attestationObject": b64_aobj, 7947 }, 7948 "clientExtensionResults": {}, 7949 "type": "public-key" 7950 }) 7951 .to_string() 7952 .as_str() 7953 ) 7954 .unwrap_err() 7955 .to_string() 7956 .into_bytes() 7957 .get(..err.len()), 7958 Some(err.as_slice()) 7959 ); 7960 // `null` `publicKey` is allowed when not using EdDSA, ES256, or RS256. 7961 assert!( 7962 serde_json::from_str::<RegistrationRelaxed>( 7963 serde_json::json!({ 7964 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7965 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7966 "response": { 7967 "clientDataJSON": b64_cdata_json, 7968 "authenticatorData": b64_adata, 7969 "transports": [], 7970 "publicKey": null, 7971 "publicKeyAlgorithm": -49i8, 7972 "attestationObject": b64_aobj, 7973 }, 7974 "clientExtensionResults": {}, 7975 "type": "public-key" 7976 }) 7977 .to_string() 7978 .as_str() 7979 ) 7980 .is_ok() 7981 ); 7982 // `publicKeyAlgorithm` mismatch when `publicKey` is null. 7983 err = Error::invalid_value( 7984 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 7985 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa65).as_str() 7986 ) 7987 .to_string().into_bytes(); 7988 assert_eq!( 7989 serde_json::from_str::<RegistrationRelaxed>( 7990 serde_json::json!({ 7991 "id": "AAAAAAAAAAAAAAAAAAAAAA", 7992 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 7993 "response": { 7994 "clientDataJSON": b64_cdata_json, 7995 "authenticatorData": b64_adata, 7996 "transports": [], 7997 "publicKey": null, 7998 "publicKeyAlgorithm": -7i8, 7999 "attestationObject": b64_aobj, 8000 }, 8001 "clientExtensionResults": {}, 8002 "type": "public-key" 8003 }) 8004 .to_string() 8005 .as_str() 8006 ) 8007 .unwrap_err() 8008 .to_string() 8009 .into_bytes() 8010 .get(..err.len()), 8011 Some(err.as_slice()) 8012 ); 8013 } 8014 #[expect( 8015 clippy::assertions_on_result_states, 8016 clippy::unwrap_used, 8017 reason = "OK in tests" 8018 )] 8019 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 8020 #[expect(clippy::too_many_lines, reason = "a lot to test")] 8021 #[test] 8022 fn mldsa44_registration_deserialize_data_mismatch() { 8023 let c_data_json = serde_json::json!({}).to_string(); 8024 let att_obj: [u8; 1424] = [ 8025 cbor::MAP_3, 8026 cbor::TEXT_3, 8027 b'f', 8028 b'm', 8029 b't', 8030 cbor::TEXT_4, 8031 b'n', 8032 b'o', 8033 b'n', 8034 b'e', 8035 cbor::TEXT_7, 8036 b'a', 8037 b't', 8038 b't', 8039 b'S', 8040 b't', 8041 b'm', 8042 b't', 8043 cbor::MAP_0, 8044 cbor::TEXT_8, 8045 b'a', 8046 b'u', 8047 b't', 8048 b'h', 8049 b'D', 8050 b'a', 8051 b't', 8052 b'a', 8053 cbor::BYTES_INFO_25, 8054 5, 8055 113, 8056 // `rpIdHash`. 8057 0, 8058 0, 8059 0, 8060 0, 8061 0, 8062 0, 8063 0, 8064 0, 8065 0, 8066 0, 8067 0, 8068 0, 8069 0, 8070 0, 8071 0, 8072 0, 8073 0, 8074 0, 8075 0, 8076 0, 8077 0, 8078 0, 8079 0, 8080 0, 8081 0, 8082 0, 8083 0, 8084 0, 8085 0, 8086 0, 8087 0, 8088 0, 8089 // `flags`. 8090 0b0100_0101, 8091 // `signCount`. 8092 0, 8093 0, 8094 0, 8095 0, 8096 // `aaguid`. 8097 0, 8098 0, 8099 0, 8100 0, 8101 0, 8102 0, 8103 0, 8104 0, 8105 0, 8106 0, 8107 0, 8108 0, 8109 0, 8110 0, 8111 0, 8112 0, 8113 // `credentialIdLength`. 8114 0, 8115 16, 8116 // `credentialId`. 8117 0, 8118 0, 8119 0, 8120 0, 8121 0, 8122 0, 8123 0, 8124 0, 8125 0, 8126 0, 8127 0, 8128 0, 8129 0, 8130 0, 8131 0, 8132 0, 8133 // ML-DSA-44 COSE key. 8134 cbor::MAP_3, 8135 KTY, 8136 AKP, 8137 ALG, 8138 cbor::NEG_INFO_24, 8139 MLDSA44, 8140 // `pub`. 8141 cbor::NEG_ONE, 8142 cbor::BYTES_INFO_25, 8143 5, 8144 32, 8145 // Encoded key. 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 1, 9330 1, 9331 1, 9332 1, 9333 1, 9334 1, 9335 1, 9336 1, 9337 1, 9338 1, 9339 1, 9340 1, 9341 1, 9342 1, 9343 1, 9344 1, 9345 1, 9346 1, 9347 1, 9348 1, 9349 1, 9350 1, 9351 1, 9352 1, 9353 1, 9354 1, 9355 1, 9356 1, 9357 1, 9358 1, 9359 1, 9360 1, 9361 1, 9362 1, 9363 1, 9364 1, 9365 1, 9366 1, 9367 1, 9368 1, 9369 1, 9370 1, 9371 1, 9372 1, 9373 1, 9374 1, 9375 1, 9376 1, 9377 1, 9378 1, 9379 1, 9380 1, 9381 1, 9382 1, 9383 1, 9384 1, 9385 1, 9386 1, 9387 1, 9388 1, 9389 1, 9390 1, 9391 1, 9392 1, 9393 1, 9394 1, 9395 1, 9396 1, 9397 1, 9398 1, 9399 1, 9400 1, 9401 1, 9402 1, 9403 1, 9404 1, 9405 1, 9406 1, 9407 1, 9408 1, 9409 1, 9410 1, 9411 1, 9412 1, 9413 1, 9414 1, 9415 1, 9416 1, 9417 1, 9418 1, 9419 1, 9420 1, 9421 1, 9422 1, 9423 1, 9424 1, 9425 1, 9426 1, 9427 1, 9428 1, 9429 1, 9430 1, 9431 1, 9432 1, 9433 1, 9434 1, 9435 1, 9436 1, 9437 1, 9438 1, 9439 1, 9440 1, 9441 1, 9442 1, 9443 1, 9444 1, 9445 1, 9446 1, 9447 1, 9448 1, 9449 1, 9450 1, 9451 1, 9452 1, 9453 1, 9454 1, 9455 1, 9456 1, 9457 1, 9458 ]; 9459 let pub_key = MlDsaVerKey::<MlDsa44>::decode(&[1u8; 1312].into()) 9460 .to_public_key_der() 9461 .unwrap(); 9462 let att_obj_len = att_obj.len(); 9463 let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes()); 9464 let b64_adata = base64url_nopad::encode(&att_obj[att_obj_len - 1393..]); 9465 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 9466 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 9467 // Base case is valid. 9468 assert!( 9469 serde_json::from_str::<RegistrationRelaxed>( 9470 serde_json::json!({ 9471 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9472 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9473 "response": { 9474 "clientDataJSON": b64_cdata_json, 9475 "authenticatorData": b64_adata, 9476 "transports": [], 9477 "publicKey": b64_key, 9478 "publicKeyAlgorithm": -48i8, 9479 "attestationObject": b64_aobj, 9480 }, 9481 "clientExtensionResults": {}, 9482 "type": "public-key" 9483 }) 9484 .to_string() 9485 .as_str() 9486 ) 9487 .is_ok_and( 9488 |reg| reg.0.response.client_data_json == c_data_json.as_bytes() 9489 && reg.0.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 9490 && reg.0.response.attestation_object_and_c_data_hash[att_obj_len..] 9491 == *Sha256::digest(c_data_json.as_bytes()) 9492 && reg.0.response.transports.is_empty() 9493 && matches!( 9494 reg.0.authenticator_attachment, 9495 AuthenticatorAttachment::None 9496 ) 9497 && reg.0.client_extension_results.cred_props.is_none() 9498 && reg.0.client_extension_results.prf.is_none() 9499 ) 9500 ); 9501 // `publicKeyAlgorithm` mismatch. 9502 let mut err = Error::invalid_value( 9503 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()), 9504 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa44).as_str() 9505 ) 9506 .to_string().into_bytes(); 9507 assert_eq!( 9508 serde_json::from_str::<RegistrationRelaxed>( 9509 serde_json::json!({ 9510 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9511 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9512 "response": { 9513 "clientDataJSON": b64_cdata_json, 9514 "authenticatorData": b64_adata, 9515 "transports": [], 9516 "publicKey": b64_key, 9517 "publicKeyAlgorithm": -8i8, 9518 "attestationObject": b64_aobj, 9519 }, 9520 "clientExtensionResults": {}, 9521 "type": "public-key" 9522 }) 9523 .to_string() 9524 .as_str() 9525 ) 9526 .unwrap_err() 9527 .to_string() 9528 .into_bytes() 9529 .get(..err.len()), 9530 Some(err.as_slice()) 9531 ); 9532 // Missing `publicKeyAlgorithm`. 9533 drop( 9534 serde_json::from_str::<RegistrationRelaxed>( 9535 serde_json::json!({ 9536 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9537 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9538 "response": { 9539 "clientDataJSON": b64_cdata_json, 9540 "authenticatorData": b64_adata, 9541 "transports": [], 9542 "publicKey": b64_key, 9543 "attestationObject": b64_aobj, 9544 }, 9545 "clientExtensionResults": {}, 9546 "type": "public-key" 9547 }) 9548 .to_string() 9549 .as_str(), 9550 ) 9551 .unwrap(), 9552 ); 9553 // `null` `publicKeyAlgorithm`. 9554 drop( 9555 serde_json::from_str::<RegistrationRelaxed>( 9556 serde_json::json!({ 9557 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9558 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9559 "response": { 9560 "clientDataJSON": b64_cdata_json, 9561 "authenticatorData": b64_adata, 9562 "transports": [], 9563 "publicKey": b64_key, 9564 "publicKeyAlgorithm": null, 9565 "attestationObject": b64_aobj, 9566 }, 9567 "clientExtensionResults": {}, 9568 "type": "public-key" 9569 }) 9570 .to_string() 9571 .as_str(), 9572 ) 9573 .unwrap(), 9574 ); 9575 // `publicKey` mismatch. 9576 let bad_pub_key = MlDsaVerKey::<MlDsa44>::decode(&[2; 1312].into()); 9577 err = Error::invalid_value( 9578 Unexpected::Bytes([0; 32].as_slice()), 9579 &format!( 9580 "DER-encoded public key to match the public key within the attestation object: MlDsa44(MlDsa44PubKey({:?}))", 9581 &[1u8; 1312] 9582 ) 9583 .as_str(), 9584 ) 9585 .to_string().into_bytes(); 9586 assert_eq!(serde_json::from_str::<RegistrationRelaxed>( 9587 serde_json::json!({ 9588 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9589 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9590 "response": { 9591 "clientDataJSON": b64_cdata_json, 9592 "authenticatorData": b64_adata, 9593 "transports": [], 9594 "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()), 9595 "publicKeyAlgorithm": -48i8, 9596 "attestationObject": b64_aobj, 9597 }, 9598 "clientExtensionResults": {}, 9599 "type": "public-key" 9600 }) 9601 .to_string() 9602 .as_str() 9603 ) 9604 .unwrap_err().to_string().into_bytes().get(..err.len()), 9605 Some(err.as_slice()) 9606 ); 9607 // Missing `publicKey` is allowed when not using EdDSA, ES256, or RS256. 9608 assert!( 9609 serde_json::from_str::<RegistrationRelaxed>( 9610 serde_json::json!({ 9611 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9612 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9613 "response": { 9614 "clientDataJSON": b64_cdata_json, 9615 "authenticatorData": b64_adata, 9616 "transports": [], 9617 "publicKeyAlgorithm": -48i8, 9618 "attestationObject": b64_aobj, 9619 }, 9620 "clientExtensionResults": {}, 9621 "type": "public-key" 9622 }) 9623 .to_string() 9624 .as_str() 9625 ) 9626 .is_ok() 9627 ); 9628 // `publicKeyAlgorithm` mismatch when `publicKey` does not exist. 9629 err = Error::invalid_value( 9630 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 9631 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa44).as_str() 9632 ) 9633 .to_string().into_bytes(); 9634 assert_eq!( 9635 serde_json::from_str::<RegistrationRelaxed>( 9636 serde_json::json!({ 9637 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9638 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9639 "response": { 9640 "clientDataJSON": b64_cdata_json, 9641 "authenticatorData": b64_adata, 9642 "transports": [], 9643 "publicKeyAlgorithm": -7i8, 9644 "attestationObject": b64_aobj, 9645 }, 9646 "clientExtensionResults": {}, 9647 "type": "public-key" 9648 }) 9649 .to_string() 9650 .as_str() 9651 ) 9652 .unwrap_err() 9653 .to_string() 9654 .into_bytes() 9655 .get(..err.len()), 9656 Some(err.as_slice()) 9657 ); 9658 // `null` `publicKey` is allowed when not using EdDSA, ES256, or RS256. 9659 assert!( 9660 serde_json::from_str::<RegistrationRelaxed>( 9661 serde_json::json!({ 9662 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9663 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9664 "response": { 9665 "clientDataJSON": b64_cdata_json, 9666 "authenticatorData": b64_adata, 9667 "transports": [], 9668 "publicKey": null, 9669 "publicKeyAlgorithm": -48i8, 9670 "attestationObject": b64_aobj, 9671 }, 9672 "clientExtensionResults": {}, 9673 "type": "public-key" 9674 }) 9675 .to_string() 9676 .as_str() 9677 ) 9678 .is_ok() 9679 ); 9680 // `publicKeyAlgorithm` mismatch when `publicKey` is null. 9681 err = Error::invalid_value( 9682 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 9683 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Mldsa44).as_str() 9684 ) 9685 .to_string().into_bytes(); 9686 assert_eq!( 9687 serde_json::from_str::<RegistrationRelaxed>( 9688 serde_json::json!({ 9689 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9690 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9691 "response": { 9692 "clientDataJSON": b64_cdata_json, 9693 "authenticatorData": b64_adata, 9694 "transports": [], 9695 "publicKey": null, 9696 "publicKeyAlgorithm": -7i8, 9697 "attestationObject": b64_aobj, 9698 }, 9699 "clientExtensionResults": {}, 9700 "type": "public-key" 9701 }) 9702 .to_string() 9703 .as_str() 9704 ) 9705 .unwrap_err() 9706 .to_string() 9707 .into_bytes() 9708 .get(..err.len()), 9709 Some(err.as_slice()) 9710 ); 9711 } 9712 #[expect(clippy::unwrap_used, reason = "OK in tests")] 9713 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 9714 #[expect(clippy::too_many_lines, reason = "a lot to test")] 9715 #[test] 9716 fn es256_registration_deserialize_data_mismatch() { 9717 let c_data_json = serde_json::json!({}).to_string(); 9718 let mut att_obj: [u8; 178] = [ 9719 cbor::MAP_3, 9720 cbor::TEXT_3, 9721 b'f', 9722 b'm', 9723 b't', 9724 cbor::TEXT_4, 9725 b'n', 9726 b'o', 9727 b'n', 9728 b'e', 9729 cbor::TEXT_7, 9730 b'a', 9731 b't', 9732 b't', 9733 b'S', 9734 b't', 9735 b'm', 9736 b't', 9737 cbor::MAP_0, 9738 cbor::TEXT_8, 9739 b'a', 9740 b'u', 9741 b't', 9742 b'h', 9743 b'D', 9744 b'a', 9745 b't', 9746 b'a', 9747 cbor::BYTES_INFO_24, 9748 148, 9749 // `rpIdHash`. 9750 0, 9751 0, 9752 0, 9753 0, 9754 0, 9755 0, 9756 0, 9757 0, 9758 0, 9759 0, 9760 0, 9761 0, 9762 0, 9763 0, 9764 0, 9765 0, 9766 0, 9767 0, 9768 0, 9769 0, 9770 0, 9771 0, 9772 0, 9773 0, 9774 0, 9775 0, 9776 0, 9777 0, 9778 0, 9779 0, 9780 0, 9781 0, 9782 // `flags`. 9783 0b0100_0101, 9784 // `signCount`. 9785 0, 9786 0, 9787 0, 9788 0, 9789 // `aaguid`. 9790 0, 9791 0, 9792 0, 9793 0, 9794 0, 9795 0, 9796 0, 9797 0, 9798 0, 9799 0, 9800 0, 9801 0, 9802 0, 9803 0, 9804 0, 9805 0, 9806 // `credentialIdLength`. 9807 0, 9808 16, 9809 // `credentialId`. 9810 0, 9811 0, 9812 0, 9813 0, 9814 0, 9815 0, 9816 0, 9817 0, 9818 0, 9819 0, 9820 0, 9821 0, 9822 0, 9823 0, 9824 0, 9825 0, 9826 // P-256 COSE key. 9827 cbor::MAP_5, 9828 KTY, 9829 EC2, 9830 ALG, 9831 ES256, 9832 // `crv`. 9833 cbor::NEG_ONE, 9834 // `P-256`. 9835 cbor::ONE, 9836 // `x`. 9837 cbor::NEG_TWO, 9838 cbor::BYTES_INFO_24, 9839 32, 9840 // x-coordinate. This will be overwritten later. 9841 0, 9842 0, 9843 0, 9844 0, 9845 0, 9846 0, 9847 0, 9848 0, 9849 0, 9850 0, 9851 0, 9852 0, 9853 0, 9854 0, 9855 0, 9856 0, 9857 0, 9858 0, 9859 0, 9860 0, 9861 0, 9862 0, 9863 0, 9864 0, 9865 0, 9866 0, 9867 0, 9868 0, 9869 0, 9870 0, 9871 0, 9872 0, 9873 // `y`. 9874 cbor::NEG_THREE, 9875 cbor::BYTES_INFO_24, 9876 32, 9877 // y-coordinate. This will be overwritten later. 9878 0, 9879 0, 9880 0, 9881 0, 9882 0, 9883 0, 9884 0, 9885 0, 9886 0, 9887 0, 9888 0, 9889 0, 9890 0, 9891 0, 9892 0, 9893 0, 9894 0, 9895 0, 9896 0, 9897 0, 9898 0, 9899 0, 9900 0, 9901 0, 9902 0, 9903 0, 9904 0, 9905 0, 9906 0, 9907 0, 9908 0, 9909 0, 9910 ]; 9911 let key = P256Key::from_bytes( 9912 &[ 9913 137, 133, 36, 206, 163, 47, 255, 5, 76, 144, 163, 141, 40, 109, 108, 240, 246, 115, 9914 178, 237, 169, 68, 6, 129, 92, 21, 238, 127, 55, 158, 207, 95, 9915 ] 9916 .into(), 9917 ) 9918 .unwrap() 9919 .public_key(); 9920 let enc_key = key.to_sec1_point(false); 9921 let pub_key = key.to_public_key_der().unwrap(); 9922 let att_obj_len = att_obj.len(); 9923 let x_start = att_obj_len - 67; 9924 let y_meta_start = x_start + 32; 9925 let y_start = y_meta_start + 3; 9926 att_obj[x_start..y_meta_start].copy_from_slice(enc_key.x().unwrap()); 9927 att_obj[y_start..].copy_from_slice(enc_key.y().unwrap()); 9928 let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes()); 9929 let b64_adata = base64url_nopad::encode(&att_obj[att_obj_len - 148..]); 9930 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 9931 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 9932 // Base case is valid. 9933 assert!( 9934 serde_json::from_str::<RegistrationRelaxed>( 9935 serde_json::json!({ 9936 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9937 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9938 "response": { 9939 "clientDataJSON": b64_cdata_json, 9940 "authenticatorData": b64_adata, 9941 "transports": [], 9942 "publicKey": b64_key, 9943 "publicKeyAlgorithm": -7i8, 9944 "attestationObject": b64_aobj, 9945 }, 9946 "clientExtensionResults": {}, 9947 "type": "public-key" 9948 }) 9949 .to_string() 9950 .as_str() 9951 ) 9952 .is_ok_and( 9953 |reg| reg.0.response.client_data_json == c_data_json.as_bytes() 9954 && reg.0.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 9955 && reg.0.response.attestation_object_and_c_data_hash[att_obj_len..] 9956 == *Sha256::digest(c_data_json.as_bytes()) 9957 && reg.0.response.transports.is_empty() 9958 && matches!( 9959 reg.0.authenticator_attachment, 9960 AuthenticatorAttachment::None 9961 ) 9962 && reg.0.client_extension_results.cred_props.is_none() 9963 && reg.0.client_extension_results.prf.is_none() 9964 ) 9965 ); 9966 // `publicKeyAlgorithm` mismatch. 9967 let mut err = Error::invalid_value( 9968 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()), 9969 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es256).as_str() 9970 ) 9971 .to_string().into_bytes(); 9972 assert_eq!( 9973 serde_json::from_str::<RegistrationRelaxed>( 9974 serde_json::json!({ 9975 "id": "AAAAAAAAAAAAAAAAAAAAAA", 9976 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 9977 "response": { 9978 "clientDataJSON": b64_cdata_json, 9979 "authenticatorData": b64_adata, 9980 "transports": [], 9981 "publicKey": b64_key, 9982 "publicKeyAlgorithm": -8i8, 9983 "attestationObject": b64_aobj, 9984 }, 9985 "clientExtensionResults": {}, 9986 "type": "public-key" 9987 }) 9988 .to_string() 9989 .as_str() 9990 ) 9991 .unwrap_err() 9992 .to_string() 9993 .into_bytes() 9994 .get(..err.len()), 9995 Some(err.as_slice()) 9996 ); 9997 // Missing `publicKeyAlgorithm`. 9998 drop( 9999 serde_json::from_str::<RegistrationRelaxed>( 10000 serde_json::json!({ 10001 "id": "AAAAAAAAAAAAAAAAAAAAAA", 10002 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 10003 "response": { 10004 "clientDataJSON": b64_cdata_json, 10005 "authenticatorData": b64_adata, 10006 "transports": [], 10007 "publicKey": b64_key, 10008 "attestationObject": b64_aobj, 10009 }, 10010 "clientExtensionResults": {}, 10011 "type": "public-key" 10012 }) 10013 .to_string() 10014 .as_str(), 10015 ) 10016 .unwrap(), 10017 ); 10018 // `null` `publicKeyAlgorithm`. 10019 drop( 10020 serde_json::from_str::<RegistrationRelaxed>( 10021 serde_json::json!({ 10022 "id": "AAAAAAAAAAAAAAAAAAAAAA", 10023 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 10024 "response": { 10025 "clientDataJSON": b64_cdata_json, 10026 "authenticatorData": b64_adata, 10027 "transports": [], 10028 "publicKey": b64_key, 10029 "publicKeyAlgorithm": null, 10030 "attestationObject": b64_aobj, 10031 }, 10032 "clientExtensionResults": {}, 10033 "type": "public-key" 10034 }) 10035 .to_string() 10036 .as_str(), 10037 ) 10038 .unwrap(), 10039 ); 10040 // `publicKey` mismatch. 10041 let bad_pub_key = P256PubKey::from_sec1_point(&P256Pt::from_affine_coordinates( 10042 &[ 10043 66, 71, 188, 41, 125, 2, 226, 44, 148, 62, 63, 190, 172, 64, 33, 214, 6, 37, 148, 23, 10044 240, 235, 203, 84, 112, 219, 232, 197, 54, 182, 17, 235, 10045 ] 10046 .into(), 10047 &[ 10048 22, 172, 123, 13, 170, 242, 217, 248, 193, 209, 206, 163, 92, 4, 162, 168, 113, 63, 2, 10049 117, 16, 223, 239, 196, 109, 179, 10, 130, 43, 213, 205, 92, 10050 ] 10051 .into(), 10052 false, 10053 )) 10054 .unwrap(); 10055 err = Error::invalid_value( 10056 Unexpected::Bytes([0; 32].as_slice()), 10057 &format!( 10058 "DER-encoded public key to match the public key within the attestation object: P256(UncompressedP256PubKey({:?}, {:?}))", 10059 &att_obj[x_start..y_meta_start], 10060 &att_obj[y_start..], 10061 ) 10062 .as_str(), 10063 ) 10064 .to_string().into_bytes(); 10065 assert_eq!(serde_json::from_str::<RegistrationRelaxed>( 10066 serde_json::json!({ 10067 "id": "AAAAAAAAAAAAAAAAAAAAAA", 10068 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 10069 "response": { 10070 "clientDataJSON": b64_cdata_json, 10071 "authenticatorData": b64_adata, 10072 "transports": [], 10073 "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()), 10074 "publicKeyAlgorithm": -7i8, 10075 "attestationObject": b64_aobj, 10076 }, 10077 "clientExtensionResults": {}, 10078 "type": "public-key" 10079 }) 10080 .to_string() 10081 .as_str() 10082 ) 10083 .unwrap_err().to_string().into_bytes().get(..err.len()), 10084 Some(err.as_slice()) 10085 ); 10086 // Missing `publicKey`. 10087 drop( 10088 serde_json::from_str::<RegistrationRelaxed>( 10089 serde_json::json!({ 10090 "id": "AAAAAAAAAAAAAAAAAAAAAA", 10091 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 10092 "response": { 10093 "clientDataJSON": b64_cdata_json, 10094 "authenticatorData": b64_adata, 10095 "transports": [], 10096 "publicKeyAlgorithm": -7i8, 10097 "attestationObject": b64_aobj, 10098 }, 10099 "clientExtensionResults": {}, 10100 "type": "public-key" 10101 }) 10102 .to_string() 10103 .as_str(), 10104 ) 10105 .unwrap(), 10106 ); 10107 // `null` `publicKey`. 10108 drop( 10109 serde_json::from_str::<RegistrationRelaxed>( 10110 serde_json::json!({ 10111 "id": "AAAAAAAAAAAAAAAAAAAAAA", 10112 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 10113 "response": { 10114 "clientDataJSON": b64_cdata_json, 10115 "authenticatorData": b64_adata, 10116 "transports": [], 10117 "publicKey": null, 10118 "publicKeyAlgorithm": -7i8, 10119 "attestationObject": b64_aobj, 10120 }, 10121 "clientExtensionResults": {}, 10122 "type": "public-key" 10123 }) 10124 .to_string() 10125 .as_str(), 10126 ) 10127 .unwrap(), 10128 ); 10129 // Base case is valid. 10130 assert!( 10131 serde_json::from_str::<CustomRegistration>( 10132 serde_json::json!({ 10133 "clientDataJSON": b64_cdata_json, 10134 "transports": [], 10135 "attestationObject": b64_aobj, 10136 "clientExtensionResults": {}, 10137 "type": "public-key" 10138 }) 10139 .to_string() 10140 .as_str() 10141 ) 10142 .is_ok_and( 10143 |reg| reg.0.response.client_data_json == c_data_json.as_bytes() 10144 && reg.0.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 10145 && reg.0.response.attestation_object_and_c_data_hash[att_obj_len..] 10146 == *Sha256::digest(c_data_json.as_bytes()) 10147 && reg.0.response.transports.is_empty() 10148 && matches!( 10149 reg.0.authenticator_attachment, 10150 AuthenticatorAttachment::None 10151 ) 10152 && reg.0.client_extension_results.cred_props.is_none() 10153 && reg.0.client_extension_results.prf.is_none() 10154 ) 10155 ); 10156 } 10157 #[expect(clippy::unwrap_used, reason = "OK in tests")] 10158 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 10159 #[expect(clippy::too_many_lines, reason = "a lot to test")] 10160 #[test] 10161 fn es384_registration_deserialize_data_mismatch() { 10162 let c_data_json = serde_json::json!({}).to_string(); 10163 let mut att_obj: [u8; 211] = [ 10164 cbor::MAP_3, 10165 cbor::TEXT_3, 10166 b'f', 10167 b'm', 10168 b't', 10169 cbor::TEXT_4, 10170 b'n', 10171 b'o', 10172 b'n', 10173 b'e', 10174 cbor::TEXT_7, 10175 b'a', 10176 b't', 10177 b't', 10178 b'S', 10179 b't', 10180 b'm', 10181 b't', 10182 cbor::MAP_0, 10183 cbor::TEXT_8, 10184 b'a', 10185 b'u', 10186 b't', 10187 b'h', 10188 b'D', 10189 b'a', 10190 b't', 10191 b'a', 10192 cbor::BYTES_INFO_24, 10193 181, 10194 // `rpIdHash`. 10195 0, 10196 0, 10197 0, 10198 0, 10199 0, 10200 0, 10201 0, 10202 0, 10203 0, 10204 0, 10205 0, 10206 0, 10207 0, 10208 0, 10209 0, 10210 0, 10211 0, 10212 0, 10213 0, 10214 0, 10215 0, 10216 0, 10217 0, 10218 0, 10219 0, 10220 0, 10221 0, 10222 0, 10223 0, 10224 0, 10225 0, 10226 0, 10227 // `flags`. 10228 0b0100_0101, 10229 // `signCount`. 10230 0, 10231 0, 10232 0, 10233 0, 10234 // `aaguid`. 10235 0, 10236 0, 10237 0, 10238 0, 10239 0, 10240 0, 10241 0, 10242 0, 10243 0, 10244 0, 10245 0, 10246 0, 10247 0, 10248 0, 10249 0, 10250 0, 10251 // `credentialIdLength`. 10252 0, 10253 16, 10254 // `credentialId`. 10255 0, 10256 0, 10257 0, 10258 0, 10259 0, 10260 0, 10261 0, 10262 0, 10263 0, 10264 0, 10265 0, 10266 0, 10267 0, 10268 0, 10269 0, 10270 0, 10271 // P-384 COSE key. 10272 cbor::MAP_5, 10273 KTY, 10274 EC2, 10275 ALG, 10276 cbor::NEG_INFO_24, 10277 ES384, 10278 // `crv`. 10279 cbor::NEG_ONE, 10280 // `P-384`. 10281 cbor::TWO, 10282 // `x`. 10283 cbor::NEG_TWO, 10284 cbor::BYTES_INFO_24, 10285 48, 10286 // x-coordinate. This will be overwritten later. 10287 0, 10288 0, 10289 0, 10290 0, 10291 0, 10292 0, 10293 0, 10294 0, 10295 0, 10296 0, 10297 0, 10298 0, 10299 0, 10300 0, 10301 0, 10302 0, 10303 0, 10304 0, 10305 0, 10306 0, 10307 0, 10308 0, 10309 0, 10310 0, 10311 0, 10312 0, 10313 0, 10314 0, 10315 0, 10316 0, 10317 0, 10318 0, 10319 0, 10320 0, 10321 0, 10322 0, 10323 0, 10324 0, 10325 0, 10326 0, 10327 0, 10328 0, 10329 0, 10330 0, 10331 0, 10332 0, 10333 0, 10334 0, 10335 // `y`. 10336 cbor::NEG_THREE, 10337 cbor::BYTES_INFO_24, 10338 48, 10339 // y-coordinate. This will be overwritten later. 10340 0, 10341 0, 10342 0, 10343 0, 10344 0, 10345 0, 10346 0, 10347 0, 10348 0, 10349 0, 10350 0, 10351 0, 10352 0, 10353 0, 10354 0, 10355 0, 10356 0, 10357 0, 10358 0, 10359 0, 10360 0, 10361 0, 10362 0, 10363 0, 10364 0, 10365 0, 10366 0, 10367 0, 10368 0, 10369 0, 10370 0, 10371 0, 10372 0, 10373 0, 10374 0, 10375 0, 10376 0, 10377 0, 10378 0, 10379 0, 10380 0, 10381 0, 10382 0, 10383 0, 10384 0, 10385 0, 10386 0, 10387 0, 10388 ]; 10389 let key = P384Key::from_bytes( 10390 &[ 10391 158, 99, 156, 49, 190, 211, 85, 167, 28, 2, 80, 57, 31, 22, 17, 38, 85, 78, 232, 42, 10392 45, 199, 154, 243, 136, 251, 84, 34, 5, 120, 208, 91, 61, 248, 64, 144, 87, 1, 32, 86, 10393 220, 68, 182, 11, 105, 223, 75, 70, 10394 ] 10395 .into(), 10396 ) 10397 .unwrap() 10398 .public_key(); 10399 let enc_key = key.to_sec1_point(false); 10400 let pub_key = key.to_public_key_der().unwrap(); 10401 let att_obj_len = att_obj.len(); 10402 let x_start = att_obj_len - 99; 10403 let y_meta_start = x_start + 48; 10404 let y_start = y_meta_start + 3; 10405 att_obj[x_start..y_meta_start].copy_from_slice(enc_key.x().unwrap()); 10406 att_obj[y_start..].copy_from_slice(enc_key.y().unwrap()); 10407 let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes()); 10408 let b64_adata = base64url_nopad::encode(&att_obj[att_obj_len - 181..]); 10409 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 10410 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 10411 // Base case is valid. 10412 assert!( 10413 serde_json::from_str::<RegistrationRelaxed>( 10414 serde_json::json!({ 10415 "id": "AAAAAAAAAAAAAAAAAAAAAA", 10416 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 10417 "response": { 10418 "clientDataJSON": b64_cdata_json, 10419 "authenticatorData": b64_adata, 10420 "transports": [], 10421 "publicKey": b64_key, 10422 "publicKeyAlgorithm": -35i8, 10423 "attestationObject": b64_aobj, 10424 }, 10425 "clientExtensionResults": {}, 10426 "type": "public-key" 10427 }) 10428 .to_string() 10429 .as_str() 10430 ) 10431 .is_ok_and( 10432 |reg| reg.0.response.client_data_json == c_data_json.as_bytes() 10433 && reg.0.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 10434 && reg.0.response.attestation_object_and_c_data_hash[att_obj_len..] 10435 == *Sha256::digest(c_data_json.as_bytes()) 10436 && reg.0.response.transports.is_empty() 10437 && matches!( 10438 reg.0.authenticator_attachment, 10439 AuthenticatorAttachment::None 10440 ) 10441 && reg.0.client_extension_results.cred_props.is_none() 10442 && reg.0.client_extension_results.prf.is_none() 10443 ) 10444 ); 10445 // `publicKeyAlgorithm` mismatch. 10446 let mut err = Error::invalid_value( 10447 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Es256).as_str()), 10448 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es384).as_str() 10449 ) 10450 .to_string().into_bytes(); 10451 assert_eq!( 10452 serde_json::from_str::<RegistrationRelaxed>( 10453 serde_json::json!({ 10454 "id": "AAAAAAAAAAAAAAAAAAAAAA", 10455 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 10456 "response": { 10457 "clientDataJSON": b64_cdata_json, 10458 "authenticatorData": b64_adata, 10459 "transports": [], 10460 "publicKey": b64_key, 10461 "publicKeyAlgorithm": -7i8, 10462 "attestationObject": b64_aobj, 10463 }, 10464 "clientExtensionResults": {}, 10465 "type": "public-key" 10466 }) 10467 .to_string() 10468 .as_str() 10469 ) 10470 .unwrap_err() 10471 .to_string() 10472 .into_bytes() 10473 .get(..err.len()), 10474 Some(err.as_slice()) 10475 ); 10476 // Missing `publicKeyAlgorithm`. 10477 drop( 10478 serde_json::from_str::<RegistrationRelaxed>( 10479 serde_json::json!({ 10480 "id": "AAAAAAAAAAAAAAAAAAAAAA", 10481 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 10482 "response": { 10483 "clientDataJSON": b64_cdata_json, 10484 "authenticatorData": b64_adata, 10485 "transports": [], 10486 "publicKey": b64_key, 10487 "attestationObject": b64_aobj, 10488 }, 10489 "clientExtensionResults": {}, 10490 "type": "public-key" 10491 }) 10492 .to_string() 10493 .as_str(), 10494 ) 10495 .unwrap(), 10496 ); 10497 // `null` `publicKeyAlgorithm`. 10498 drop( 10499 serde_json::from_str::<RegistrationRelaxed>( 10500 serde_json::json!({ 10501 "id": "AAAAAAAAAAAAAAAAAAAAAA", 10502 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 10503 "response": { 10504 "clientDataJSON": b64_cdata_json, 10505 "authenticatorData": b64_adata, 10506 "transports": [], 10507 "publicKey": b64_key, 10508 "publicKeyAlgorithm": null, 10509 "attestationObject": b64_aobj, 10510 }, 10511 "clientExtensionResults": {}, 10512 "type": "public-key" 10513 }) 10514 .to_string() 10515 .as_str(), 10516 ) 10517 .unwrap(), 10518 ); 10519 // `publicKey` mismatch. 10520 let bad_pub_key = P384PubKey::from_sec1_point(&P384Pt::from_affine_coordinates( 10521 &[ 10522 192, 10, 27, 46, 66, 67, 80, 98, 33, 230, 156, 95, 1, 135, 150, 110, 64, 243, 22, 118, 10523 5, 255, 107, 44, 234, 111, 217, 105, 125, 114, 39, 7, 126, 2, 191, 111, 48, 93, 234, 10524 175, 18, 172, 59, 28, 97, 106, 178, 152, 10525 ] 10526 .into(), 10527 &[ 10528 57, 36, 196, 12, 109, 129, 253, 115, 88, 154, 6, 43, 195, 85, 169, 5, 230, 51, 28, 205, 10529 142, 28, 150, 35, 24, 222, 170, 253, 14, 248, 84, 151, 109, 191, 152, 111, 222, 70, 10530 134, 247, 109, 171, 211, 33, 214, 217, 200, 111, 10531 ] 10532 .into(), 10533 false, 10534 )) 10535 .unwrap(); 10536 err = Error::invalid_value( 10537 Unexpected::Bytes([0; 32].as_slice()), 10538 &format!( 10539 "DER-encoded public key to match the public key within the attestation object: P384(UncompressedP384PubKey({:?}, {:?}))", 10540 &att_obj[x_start..y_meta_start], 10541 &att_obj[y_start..], 10542 ) 10543 .as_str(), 10544 ) 10545 .to_string().into_bytes(); 10546 assert_eq!(serde_json::from_str::<RegistrationRelaxed>( 10547 serde_json::json!({ 10548 "id": "AAAAAAAAAAAAAAAAAAAAAA", 10549 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 10550 "response": { 10551 "clientDataJSON": b64_cdata_json, 10552 "authenticatorData": b64_adata, 10553 "transports": [], 10554 "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()), 10555 "publicKeyAlgorithm": -35i8, 10556 "attestationObject": b64_aobj, 10557 }, 10558 "clientExtensionResults": {}, 10559 "type": "public-key" 10560 }).to_string().as_str() 10561 ).unwrap_err().to_string().into_bytes().get(..err.len()), Some(err.as_slice())); 10562 // Missing `publicKey`. 10563 drop( 10564 serde_json::from_str::<RegistrationRelaxed>( 10565 serde_json::json!({ 10566 "id": "AAAAAAAAAAAAAAAAAAAAAA", 10567 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 10568 "response": { 10569 "clientDataJSON": b64_cdata_json, 10570 "authenticatorData": b64_adata, 10571 "transports": [], 10572 "publicKeyAlgorithm": -35i8, 10573 "attestationObject": b64_aobj, 10574 }, 10575 "clientExtensionResults": {}, 10576 "type": "public-key" 10577 }) 10578 .to_string() 10579 .as_str(), 10580 ) 10581 .unwrap(), 10582 ); 10583 // `publicKeyAlgorithm` mismatch when `publicKey` does not exist. 10584 err = Error::invalid_value( 10585 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()), 10586 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es384).as_str() 10587 ) 10588 .to_string().into_bytes(); 10589 assert_eq!( 10590 serde_json::from_str::<RegistrationRelaxed>( 10591 serde_json::json!({ 10592 "id": "AAAAAAAAAAAAAAAAAAAAAA", 10593 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 10594 "response": { 10595 "clientDataJSON": b64_cdata_json, 10596 "authenticatorData": b64_adata, 10597 "transports": [], 10598 "publicKeyAlgorithm": -8i8, 10599 "attestationObject": b64_aobj, 10600 }, 10601 "clientExtensionResults": {}, 10602 "type": "public-key" 10603 }) 10604 .to_string() 10605 .as_str() 10606 ) 10607 .unwrap_err() 10608 .to_string() 10609 .into_bytes() 10610 .get(..err.len()), 10611 Some(err.as_slice()) 10612 ); 10613 // `null` `publicKey`. 10614 drop( 10615 serde_json::from_str::<RegistrationRelaxed>( 10616 serde_json::json!({ 10617 "id": "AAAAAAAAAAAAAAAAAAAAAA", 10618 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 10619 "response": { 10620 "clientDataJSON": b64_cdata_json, 10621 "authenticatorData": b64_adata, 10622 "transports": [], 10623 "publicKey": null, 10624 "publicKeyAlgorithm": -35i8, 10625 "attestationObject": b64_aobj, 10626 }, 10627 "clientExtensionResults": {}, 10628 "type": "public-key" 10629 }) 10630 .to_string() 10631 .as_str(), 10632 ) 10633 .unwrap(), 10634 ); 10635 // `publicKeyAlgorithm` mismatch when `publicKey` is null. 10636 err = Error::invalid_value( 10637 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()), 10638 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Es384).as_str() 10639 ) 10640 .to_string().into_bytes(); 10641 assert_eq!( 10642 serde_json::from_str::<RegistrationRelaxed>( 10643 serde_json::json!({ 10644 "id": "AAAAAAAAAAAAAAAAAAAAAA", 10645 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 10646 "response": { 10647 "clientDataJSON": b64_cdata_json, 10648 "authenticatorData": b64_adata, 10649 "transports": [], 10650 "publicKey": null, 10651 "publicKeyAlgorithm": -8i8, 10652 "attestationObject": b64_aobj, 10653 }, 10654 "clientExtensionResults": {}, 10655 "type": "public-key" 10656 }) 10657 .to_string() 10658 .as_str() 10659 ) 10660 .unwrap_err() 10661 .to_string() 10662 .into_bytes() 10663 .get(..err.len()), 10664 Some(err.as_slice()) 10665 ); 10666 // Base case is valid. 10667 assert!( 10668 serde_json::from_str::<CustomRegistration>( 10669 serde_json::json!({ 10670 "clientDataJSON": b64_cdata_json, 10671 "transports": [], 10672 "attestationObject": b64_aobj, 10673 "clientExtensionResults": {}, 10674 "type": "public-key" 10675 }) 10676 .to_string() 10677 .as_str() 10678 ) 10679 .is_ok_and( 10680 |reg| reg.0.response.client_data_json == c_data_json.as_bytes() 10681 && reg.0.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 10682 && reg.0.response.attestation_object_and_c_data_hash[att_obj_len..] 10683 == *Sha256::digest(c_data_json.as_bytes()) 10684 && reg.0.response.transports.is_empty() 10685 && matches!( 10686 reg.0.authenticator_attachment, 10687 AuthenticatorAttachment::None 10688 ) 10689 && reg.0.client_extension_results.cred_props.is_none() 10690 && reg.0.client_extension_results.prf.is_none() 10691 ) 10692 ); 10693 } 10694 #[expect(clippy::unwrap_used, reason = "OK in tests")] 10695 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 10696 #[expect(clippy::too_many_lines, reason = "a lot to test")] 10697 #[test] 10698 fn rs256_registration_deserialize_data_mismatch() { 10699 let c_data_json = serde_json::json!({}).to_string(); 10700 let mut att_obj: [u8; 374] = [ 10701 cbor::MAP_3, 10702 cbor::TEXT_3, 10703 b'f', 10704 b'm', 10705 b't', 10706 cbor::TEXT_4, 10707 b'n', 10708 b'o', 10709 b'n', 10710 b'e', 10711 cbor::TEXT_7, 10712 b'a', 10713 b't', 10714 b't', 10715 b'S', 10716 b't', 10717 b'm', 10718 b't', 10719 cbor::MAP_0, 10720 cbor::TEXT_8, 10721 b'a', 10722 b'u', 10723 b't', 10724 b'h', 10725 b'D', 10726 b'a', 10727 b't', 10728 b'a', 10729 cbor::BYTES_INFO_25, 10730 1, 10731 87, 10732 // `rpIdHash`. 10733 0, 10734 0, 10735 0, 10736 0, 10737 0, 10738 0, 10739 0, 10740 0, 10741 0, 10742 0, 10743 0, 10744 0, 10745 0, 10746 0, 10747 0, 10748 0, 10749 0, 10750 0, 10751 0, 10752 0, 10753 0, 10754 0, 10755 0, 10756 0, 10757 0, 10758 0, 10759 0, 10760 0, 10761 0, 10762 0, 10763 0, 10764 0, 10765 // `flags`. 10766 0b0100_0101, 10767 // `signCount`. 10768 0, 10769 0, 10770 0, 10771 0, 10772 // `aaguid`. 10773 0, 10774 0, 10775 0, 10776 0, 10777 0, 10778 0, 10779 0, 10780 0, 10781 0, 10782 0, 10783 0, 10784 0, 10785 0, 10786 0, 10787 0, 10788 0, 10789 // `credentialIdLength`. 10790 0, 10791 16, 10792 // `credentialId`. 10793 0, 10794 0, 10795 0, 10796 0, 10797 0, 10798 0, 10799 0, 10800 0, 10801 0, 10802 0, 10803 0, 10804 0, 10805 0, 10806 0, 10807 0, 10808 0, 10809 // RSA COSE key. 10810 cbor::MAP_4, 10811 KTY, 10812 RSA, 10813 ALG, 10814 cbor::NEG_INFO_25, 10815 // RS256. 10816 1, 10817 0, 10818 // `n`. 10819 cbor::NEG_ONE, 10820 cbor::BYTES_INFO_25, 10821 1, 10822 0, 10823 // n. This will be overwritten later. 10824 0, 10825 0, 10826 0, 10827 0, 10828 0, 10829 0, 10830 0, 10831 0, 10832 0, 10833 0, 10834 0, 10835 0, 10836 0, 10837 0, 10838 0, 10839 0, 10840 0, 10841 0, 10842 0, 10843 0, 10844 0, 10845 0, 10846 0, 10847 0, 10848 0, 10849 0, 10850 0, 10851 0, 10852 0, 10853 0, 10854 0, 10855 0, 10856 0, 10857 0, 10858 0, 10859 0, 10860 0, 10861 0, 10862 0, 10863 0, 10864 0, 10865 0, 10866 0, 10867 0, 10868 0, 10869 0, 10870 0, 10871 0, 10872 0, 10873 0, 10874 0, 10875 0, 10876 0, 10877 0, 10878 0, 10879 0, 10880 0, 10881 0, 10882 0, 10883 0, 10884 0, 10885 0, 10886 0, 10887 0, 10888 0, 10889 0, 10890 0, 10891 0, 10892 0, 10893 0, 10894 0, 10895 0, 10896 0, 10897 0, 10898 0, 10899 0, 10900 0, 10901 0, 10902 0, 10903 0, 10904 0, 10905 0, 10906 0, 10907 0, 10908 0, 10909 0, 10910 0, 10911 0, 10912 0, 10913 0, 10914 0, 10915 0, 10916 0, 10917 0, 10918 0, 10919 0, 10920 0, 10921 0, 10922 0, 10923 0, 10924 0, 10925 0, 10926 0, 10927 0, 10928 0, 10929 0, 10930 0, 10931 0, 10932 0, 10933 0, 10934 0, 10935 0, 10936 0, 10937 0, 10938 0, 10939 0, 10940 0, 10941 0, 10942 0, 10943 0, 10944 0, 10945 0, 10946 0, 10947 0, 10948 0, 10949 0, 10950 0, 10951 0, 10952 0, 10953 0, 10954 0, 10955 0, 10956 0, 10957 0, 10958 0, 10959 0, 10960 0, 10961 0, 10962 0, 10963 0, 10964 0, 10965 0, 10966 0, 10967 0, 10968 0, 10969 0, 10970 0, 10971 0, 10972 0, 10973 0, 10974 0, 10975 0, 10976 0, 10977 0, 10978 0, 10979 0, 10980 0, 10981 0, 10982 0, 10983 0, 10984 0, 10985 0, 10986 0, 10987 0, 10988 0, 10989 0, 10990 0, 10991 0, 10992 0, 10993 0, 10994 0, 10995 0, 10996 0, 10997 0, 10998 0, 10999 0, 11000 0, 11001 0, 11002 0, 11003 0, 11004 0, 11005 0, 11006 0, 11007 0, 11008 0, 11009 0, 11010 0, 11011 0, 11012 0, 11013 0, 11014 0, 11015 0, 11016 0, 11017 0, 11018 0, 11019 0, 11020 0, 11021 0, 11022 0, 11023 0, 11024 0, 11025 0, 11026 0, 11027 0, 11028 0, 11029 0, 11030 0, 11031 0, 11032 0, 11033 0, 11034 0, 11035 0, 11036 0, 11037 0, 11038 0, 11039 0, 11040 0, 11041 0, 11042 0, 11043 0, 11044 0, 11045 0, 11046 0, 11047 0, 11048 0, 11049 0, 11050 0, 11051 0, 11052 0, 11053 0, 11054 0, 11055 0, 11056 0, 11057 0, 11058 0, 11059 0, 11060 0, 11061 0, 11062 0, 11063 0, 11064 0, 11065 0, 11066 0, 11067 0, 11068 0, 11069 0, 11070 0, 11071 0, 11072 0, 11073 0, 11074 0, 11075 0, 11076 0, 11077 0, 11078 0, 11079 0, 11080 // `e`. 11081 cbor::NEG_TWO, 11082 cbor::BYTES | 3, 11083 // e. 11084 1, 11085 0, 11086 1, 11087 ]; 11088 let n = [ 11089 111, 183, 124, 133, 38, 167, 70, 148, 44, 50, 30, 60, 121, 14, 38, 37, 96, 114, 107, 195, 11090 248, 64, 79, 36, 237, 140, 43, 27, 94, 74, 102, 152, 135, 102, 184, 150, 186, 206, 185, 19, 11091 165, 209, 48, 98, 98, 9, 3, 205, 208, 82, 250, 105, 132, 201, 73, 62, 60, 165, 100, 128, 11092 153, 9, 41, 118, 66, 95, 236, 214, 73, 135, 197, 68, 184, 10, 27, 116, 204, 145, 50, 174, 11093 58, 42, 183, 181, 119, 232, 126, 252, 217, 96, 162, 190, 103, 122, 64, 87, 145, 45, 32, 11094 207, 17, 239, 223, 3, 35, 14, 112, 119, 124, 141, 123, 208, 239, 105, 81, 217, 151, 162, 11095 190, 17, 88, 182, 176, 158, 81, 200, 42, 166, 133, 48, 23, 236, 55, 117, 248, 233, 151, 11096 203, 122, 155, 231, 46, 177, 20, 20, 151, 64, 222, 239, 226, 7, 21, 254, 81, 202, 64, 232, 11097 161, 235, 22, 51, 246, 207, 213, 0, 229, 138, 46, 222, 205, 157, 108, 139, 253, 230, 80, 11098 50, 2, 122, 212, 163, 100, 180, 114, 12, 113, 52, 56, 99, 188, 42, 198, 212, 23, 182, 222, 11099 56, 221, 200, 79, 96, 239, 221, 135, 10, 17, 106, 183, 56, 104, 68, 94, 198, 196, 35, 200, 11100 83, 204, 26, 185, 204, 212, 31, 183, 19, 111, 233, 13, 72, 93, 53, 65, 111, 59, 242, 122, 11101 160, 244, 162, 126, 38, 235, 156, 47, 88, 39, 132, 153, 79, 0, 133, 78, 7, 218, 165, 241, 11102 ]; 11103 let e = 0x0001_0001u32; 11104 let d = [ 11105 145, 79, 21, 97, 233, 3, 192, 194, 177, 68, 181, 80, 120, 197, 23, 44, 185, 74, 144, 0, 11106 132, 149, 139, 11, 16, 224, 4, 112, 236, 94, 238, 97, 121, 124, 213, 145, 24, 253, 168, 35, 11107 190, 205, 132, 115, 33, 201, 38, 253, 246, 180, 66, 155, 165, 46, 3, 254, 68, 108, 154, 11108 247, 246, 45, 187, 0, 204, 96, 185, 157, 249, 174, 158, 38, 62, 244, 183, 76, 102, 6, 219, 11109 92, 212, 138, 59, 147, 163, 219, 111, 39, 105, 21, 236, 196, 38, 255, 114, 247, 82, 104, 11110 113, 204, 29, 152, 209, 219, 48, 239, 74, 129, 19, 247, 33, 239, 119, 166, 216, 152, 94, 11111 138, 238, 164, 242, 129, 50, 150, 57, 20, 53, 224, 56, 241, 138, 97, 111, 215, 107, 212, 11112 195, 146, 108, 143, 0, 229, 181, 171, 73, 152, 105, 146, 25, 243, 242, 140, 252, 248, 162, 11113 247, 63, 168, 180, 20, 153, 120, 10, 248, 211, 1, 71, 127, 212, 249, 237, 203, 202, 48, 26, 11114 216, 226, 228, 186, 13, 204, 70, 255, 240, 89, 255, 59, 83, 31, 253, 55, 43, 158, 90, 248, 11115 83, 32, 159, 105, 57, 134, 34, 96, 18, 255, 245, 153, 162, 60, 91, 99, 220, 51, 44, 85, 11116 114, 67, 125, 202, 65, 217, 245, 40, 8, 81, 165, 142, 24, 245, 127, 122, 247, 152, 212, 75, 11117 45, 59, 90, 184, 234, 31, 147, 36, 8, 212, 45, 50, 23, 3, 25, 253, 87, 227, 79, 119, 161, 11118 ]; 11119 let p = BoxedUint::from_le_slice_vartime( 11120 [ 11121 215, 166, 5, 21, 11, 179, 41, 77, 198, 92, 165, 48, 77, 162, 42, 41, 206, 141, 60, 69, 11122 47, 164, 19, 92, 46, 72, 100, 238, 100, 53, 214, 197, 163, 185, 6, 140, 229, 250, 195, 11123 77, 8, 12, 5, 236, 178, 173, 86, 201, 43, 213, 165, 51, 108, 101, 161, 99, 76, 240, 14, 11124 234, 76, 197, 137, 53, 198, 168, 135, 205, 212, 198, 120, 29, 16, 82, 98, 233, 236, 11125 177, 12, 171, 141, 100, 107, 146, 33, 176, 125, 202, 172, 79, 147, 179, 30, 62, 247, 11126 206, 169, 19, 168, 114, 26, 73, 108, 178, 105, 84, 89, 191, 168, 253, 228, 214, 54, 16, 11127 212, 199, 111, 72, 3, 41, 247, 227, 165, 244, 32, 188, 24, 247, 11128 ] 11129 .as_slice(), 11130 ); 11131 let p_2 = BoxedUint::from_le_slice_vartime( 11132 [ 11133 41, 25, 198, 240, 134, 206, 121, 57, 11, 5, 134, 192, 212, 77, 229, 197, 14, 78, 85, 11134 212, 190, 114, 179, 188, 21, 171, 174, 12, 104, 74, 15, 164, 136, 173, 62, 177, 141, 11135 213, 93, 102, 147, 83, 59, 124, 146, 59, 175, 213, 55, 27, 25, 248, 154, 29, 39, 85, 11136 50, 235, 134, 60, 203, 106, 186, 195, 190, 185, 71, 169, 142, 236, 92, 11, 250, 187, 11137 198, 8, 201, 184, 120, 178, 227, 87, 63, 243, 89, 227, 234, 184, 28, 252, 112, 211, 11138 193, 69, 23, 92, 5, 72, 93, 53, 69, 159, 73, 160, 105, 244, 249, 94, 214, 173, 9, 236, 11139 4, 255, 129, 11, 224, 140, 252, 168, 57, 143, 176, 241, 60, 219, 90, 250, 11140 ] 11141 .as_slice(), 11142 ); 11143 let key = RsaPrivateKey::from_components( 11144 BoxedUint::from_le_slice_vartime(n.as_slice()), 11145 e.into(), 11146 BoxedUint::from_le_slice_vartime(d.as_slice()), 11147 vec![p, p_2], 11148 ) 11149 .unwrap() 11150 .to_public_key(); 11151 let pub_key = key.to_public_key_der().unwrap(); 11152 let att_obj_len = att_obj.len(); 11153 let n_start = att_obj_len - 261; 11154 let e_start = n_start + 256; 11155 att_obj[n_start..e_start].copy_from_slice(key.n().to_be_bytes().as_ref()); 11156 let b64_cdata_json = base64url_nopad::encode(c_data_json.as_bytes()); 11157 let b64_adata = base64url_nopad::encode(&att_obj[att_obj_len - 343..]); 11158 let b64_key = base64url_nopad::encode(pub_key.as_bytes()); 11159 let b64_aobj = base64url_nopad::encode(att_obj.as_slice()); 11160 // Base case is valid. 11161 assert!( 11162 serde_json::from_str::<RegistrationRelaxed>( 11163 serde_json::json!({ 11164 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11165 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11166 "response": { 11167 "clientDataJSON": b64_cdata_json, 11168 "authenticatorData": b64_adata, 11169 "transports": [], 11170 "publicKey": b64_key, 11171 "publicKeyAlgorithm": -257i16, 11172 "attestationObject": b64_aobj, 11173 }, 11174 "clientExtensionResults": {}, 11175 "type": "public-key" 11176 }) 11177 .to_string() 11178 .as_str() 11179 ) 11180 .is_ok_and( 11181 |reg| reg.0.response.client_data_json == c_data_json.as_bytes() 11182 && reg.0.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 11183 && reg.0.response.attestation_object_and_c_data_hash[att_obj_len..] 11184 == *Sha256::digest(c_data_json.as_bytes()) 11185 && reg.0.response.transports.is_empty() 11186 && matches!( 11187 reg.0.authenticator_attachment, 11188 AuthenticatorAttachment::None 11189 ) 11190 && reg.0.client_extension_results.cred_props.is_none() 11191 && reg.0.client_extension_results.prf.is_none() 11192 ) 11193 ); 11194 // `publicKeyAlgorithm` mismatch. 11195 let mut err = Error::invalid_value( 11196 Unexpected::Other(format!("{:?}", CoseAlgorithmIdentifier::Eddsa).as_str()), 11197 &format!("public key algorithm to match the algorithm associated with the public key within the attestation object: {:?}", CoseAlgorithmIdentifier::Rs256).as_str() 11198 ) 11199 .to_string().into_bytes(); 11200 assert_eq!( 11201 serde_json::from_str::<RegistrationRelaxed>( 11202 serde_json::json!({ 11203 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11204 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11205 "response": { 11206 "clientDataJSON": b64_cdata_json, 11207 "authenticatorData": b64_adata, 11208 "transports": [], 11209 "publicKey": b64_key, 11210 "publicKeyAlgorithm": -8i8, 11211 "attestationObject": b64_aobj, 11212 }, 11213 "clientExtensionResults": {}, 11214 "type": "public-key" 11215 }) 11216 .to_string() 11217 .as_str() 11218 ) 11219 .unwrap_err() 11220 .to_string() 11221 .into_bytes() 11222 .get(..err.len()), 11223 Some(err.as_slice()) 11224 ); 11225 // Missing `publicKeyAlgorithm`. 11226 drop( 11227 serde_json::from_str::<RegistrationRelaxed>( 11228 serde_json::json!({ 11229 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11230 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11231 "response": { 11232 "clientDataJSON": b64_cdata_json, 11233 "authenticatorData": b64_adata, 11234 "transports": [], 11235 "publicKey": b64_key, 11236 "attestationObject": b64_aobj, 11237 }, 11238 "clientExtensionResults": {}, 11239 "type": "public-key" 11240 }) 11241 .to_string() 11242 .as_str(), 11243 ) 11244 .unwrap(), 11245 ); 11246 // `null` `publicKeyAlgorithm`. 11247 drop( 11248 serde_json::from_str::<RegistrationRelaxed>( 11249 serde_json::json!({ 11250 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11251 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11252 "response": { 11253 "clientDataJSON": b64_cdata_json, 11254 "authenticatorData": b64_adata, 11255 "transports": [], 11256 "publicKey": b64_key, 11257 "publicKeyAlgorithm": null, 11258 "attestationObject": b64_aobj, 11259 }, 11260 "clientExtensionResults": {}, 11261 "type": "public-key" 11262 }) 11263 .to_string() 11264 .as_str(), 11265 ) 11266 .unwrap(), 11267 ); 11268 // `publicKey` mismatch. 11269 let bad_pub_key = RsaPrivateKey::from_components( 11270 BoxedUint::from_le_slice_vartime( 11271 [ 11272 175, 161, 161, 75, 52, 244, 72, 168, 29, 119, 33, 120, 3, 222, 231, 152, 222, 119, 11273 112, 83, 221, 237, 74, 174, 79, 216, 147, 251, 245, 94, 234, 114, 254, 21, 17, 254, 11274 8, 115, 75, 127, 150, 87, 59, 109, 230, 116, 85, 90, 11, 160, 63, 217, 9, 38, 187, 11275 250, 226, 183, 38, 164, 182, 218, 22, 19, 58, 189, 83, 219, 11, 144, 15, 99, 151, 11276 166, 46, 57, 17, 111, 189, 131, 142, 113, 85, 122, 188, 238, 52, 21, 116, 125, 102, 11277 195, 182, 165, 29, 156, 213, 182, 125, 156, 88, 56, 221, 2, 98, 43, 210, 115, 32, 11278 4, 105, 88, 181, 158, 207, 236, 162, 250, 253, 240, 72, 8, 253, 50, 220, 247, 76, 11279 170, 143, 68, 225, 231, 113, 64, 244, 17, 138, 162, 233, 33, 2, 67, 11, 223, 188, 11280 232, 152, 193, 20, 32, 243, 52, 64, 43, 2, 243, 8, 77, 150, 232, 109, 148, 95, 127, 11281 55, 71, 162, 34, 54, 83, 135, 52, 172, 191, 32, 42, 106, 43, 211, 206, 100, 104, 11282 110, 232, 5, 43, 120, 180, 166, 40, 144, 233, 239, 103, 134, 103, 255, 224, 138, 11283 184, 208, 137, 127, 36, 189, 143, 248, 201, 2, 218, 51, 232, 96, 30, 83, 124, 109, 11284 241, 23, 179, 247, 151, 238, 212, 204, 44, 43, 223, 148, 241, 172, 10, 235, 155, 11285 94, 68, 116, 24, 116, 191, 86, 53, 127, 35, 133, 198, 204, 59, 76, 110, 16, 1, 15, 11286 148, 135, 157, 11287 ] 11288 .as_slice(), 11289 ), 11290 0x0001_0001u32.into(), 11291 BoxedUint::from_le_slice_vartime( 11292 [ 11293 129, 93, 123, 251, 104, 29, 84, 203, 116, 100, 75, 237, 111, 160, 12, 100, 172, 76, 11294 57, 178, 144, 235, 81, 61, 115, 243, 28, 40, 183, 22, 56, 150, 68, 38, 220, 62, 11295 233, 110, 48, 174, 35, 197, 244, 109, 148, 109, 36, 69, 69, 82, 225, 113, 175, 6, 11296 239, 27, 193, 101, 50, 239, 122, 102, 7, 46, 98, 79, 195, 116, 155, 158, 138, 147, 11297 51, 93, 24, 237, 246, 82, 14, 109, 144, 250, 239, 93, 63, 214, 96, 130, 226, 134, 11298 198, 145, 161, 11, 231, 97, 214, 180, 255, 95, 158, 88, 108, 254, 243, 177, 133, 11299 184, 92, 95, 148, 88, 55, 124, 245, 244, 84, 86, 4, 121, 44, 231, 97, 176, 190, 29, 11300 155, 40, 57, 69, 165, 80, 168, 9, 56, 43, 233, 6, 14, 157, 112, 223, 64, 88, 141, 11301 7, 65, 23, 64, 208, 6, 83, 61, 8, 182, 248, 126, 84, 179, 163, 80, 238, 90, 133, 4, 11302 14, 71, 177, 175, 27, 29, 151, 211, 108, 162, 195, 7, 157, 167, 86, 169, 3, 87, 11303 235, 89, 158, 237, 216, 31, 243, 197, 62, 5, 84, 131, 230, 186, 248, 49, 12, 93, 11304 244, 61, 135, 180, 17, 162, 241, 13, 115, 241, 138, 219, 98, 155, 166, 191, 63, 12, 11305 37, 1, 165, 178, 84, 200, 72, 80, 41, 77, 136, 217, 141, 246, 209, 31, 243, 159, 11306 71, 43, 246, 159, 182, 171, 116, 12, 3, 142, 235, 218, 164, 70, 90, 147, 238, 42, 11307 75, 11308 ] 11309 .as_slice(), 11310 ), 11311 vec![ 11312 BoxedUint::from_le_slice_vartime( 11313 [ 11314 215, 199, 110, 28, 64, 16, 16, 109, 106, 152, 150, 124, 52, 166, 121, 92, 242, 11315 13, 0, 69, 7, 152, 72, 172, 118, 63, 156, 180, 140, 39, 53, 29, 197, 224, 177, 11316 48, 41, 221, 102, 65, 17, 185, 55, 62, 219, 152, 227, 7, 78, 219, 14, 139, 71, 11317 204, 144, 152, 14, 39, 247, 244, 165, 224, 234, 60, 213, 74, 237, 30, 102, 177, 11318 242, 138, 168, 31, 122, 47, 206, 155, 225, 113, 103, 175, 152, 244, 27, 233, 11319 112, 223, 248, 38, 215, 178, 20, 244, 8, 121, 26, 11, 70, 122, 16, 85, 167, 87, 11320 64, 216, 228, 227, 173, 57, 250, 8, 221, 38, 12, 203, 212, 1, 112, 43, 72, 91, 11321 225, 97, 228, 57, 154, 193, 11322 ] 11323 .as_slice(), 11324 ), 11325 BoxedUint::from_le_slice_vartime( 11326 [ 11327 233, 89, 204, 152, 31, 242, 8, 110, 38, 190, 111, 159, 105, 105, 45, 85, 15, 11328 244, 30, 250, 174, 226, 219, 111, 107, 191, 196, 135, 17, 123, 186, 167, 85, 11329 13, 120, 197, 159, 129, 78, 237, 152, 31, 230, 26, 229, 253, 197, 211, 105, 11330 204, 126, 142, 250, 55, 26, 172, 65, 160, 45, 6, 99, 86, 66, 238, 107, 6, 98, 11331 171, 93, 224, 201, 160, 31, 204, 82, 120, 228, 158, 238, 6, 190, 12, 150, 153, 11332 239, 95, 57, 71, 100, 239, 235, 155, 73, 200, 5, 225, 127, 185, 46, 48, 243, 11333 84, 33, 142, 17, 19, 20, 23, 215, 16, 114, 58, 211, 14, 73, 148, 168, 252, 159, 11334 252, 125, 57, 101, 211, 188, 12, 77, 208, 11335 ] 11336 .as_slice(), 11337 ), 11338 ], 11339 ) 11340 .unwrap() 11341 .to_public_key(); 11342 err = Error::invalid_value( 11343 Unexpected::Bytes([0; 32].as_slice()), 11344 &format!( 11345 "DER-encoded public key to match the public key within the attestation object: Rsa(RsaPubKey({:?}, 65537))", 11346 &att_obj[n_start..e_start], 11347 ) 11348 .as_str(), 11349 ) 11350 .to_string().into_bytes(); 11351 assert_eq!(serde_json::from_str::<RegistrationRelaxed>( 11352 serde_json::json!({ 11353 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11354 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11355 "response": { 11356 "clientDataJSON": b64_cdata_json, 11357 "authenticatorData": b64_adata, 11358 "transports": [], 11359 "publicKey": base64url_nopad::encode(bad_pub_key.to_public_key_der().unwrap().as_bytes()), 11360 "publicKeyAlgorithm": -257i16, 11361 "attestationObject": b64_aobj, 11362 }, 11363 "clientExtensionResults": {}, 11364 "type": "public-key" 11365 }) 11366 .to_string() 11367 .as_str() 11368 ) 11369 .unwrap_err().to_string().into_bytes().get(..err.len()), 11370 Some(err.as_slice()) 11371 ); 11372 // Missing `publicKey`. 11373 drop( 11374 serde_json::from_str::<RegistrationRelaxed>( 11375 serde_json::json!({ 11376 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11377 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11378 "response": { 11379 "clientDataJSON": b64_cdata_json, 11380 "authenticatorData": b64_adata, 11381 "transports": [], 11382 "publicKeyAlgorithm": -257i16, 11383 "attestationObject": b64_aobj, 11384 }, 11385 "clientExtensionResults": {}, 11386 "type": "public-key" 11387 }) 11388 .to_string() 11389 .as_str(), 11390 ) 11391 .unwrap(), 11392 ); 11393 // `null` `publicKey`. 11394 drop( 11395 serde_json::from_str::<RegistrationRelaxed>( 11396 serde_json::json!({ 11397 "id": "AAAAAAAAAAAAAAAAAAAAAA", 11398 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 11399 "response": { 11400 "clientDataJSON": b64_cdata_json, 11401 "authenticatorData": b64_adata, 11402 "transports": [], 11403 "publicKey": null, 11404 "publicKeyAlgorithm": -257i16, 11405 "attestationObject": b64_aobj, 11406 }, 11407 "clientExtensionResults": {}, 11408 "type": "public-key" 11409 }) 11410 .to_string() 11411 .as_str(), 11412 ) 11413 .unwrap(), 11414 ); 11415 // Base case is valid. 11416 assert!( 11417 serde_json::from_str::<CustomRegistration>( 11418 serde_json::json!({ 11419 "clientDataJSON": b64_cdata_json, 11420 "transports": [], 11421 "attestationObject": b64_aobj, 11422 "clientExtensionResults": {}, 11423 "type": "public-key" 11424 }) 11425 .to_string() 11426 .as_str() 11427 ) 11428 .is_ok_and( 11429 |reg| reg.0.response.client_data_json == c_data_json.as_bytes() 11430 && reg.0.response.attestation_object_and_c_data_hash[..att_obj_len] == att_obj 11431 && reg.0.response.attestation_object_and_c_data_hash[att_obj_len..] 11432 == *Sha256::digest(c_data_json.as_bytes()) 11433 && reg.0.response.transports.is_empty() 11434 && matches!( 11435 reg.0.authenticator_attachment, 11436 AuthenticatorAttachment::None 11437 ) 11438 && reg.0.client_extension_results.cred_props.is_none() 11439 && reg.0.client_extension_results.prf.is_none() 11440 ) 11441 ); 11442 }