tests.rs (178176B)
1 #[cfg(feature = "custom")] 2 use super::{ 3 super::{ 4 AggErr, AuthenticatedCredential, 5 response::{ 6 AuthTransports, AuthenticatorAttachment, Backup, CredentialId, 7 auth::{ 8 DiscoverableAuthentication, DiscoverableAuthenticatorAssertion, 9 NonDiscoverableAuthentication, NonDiscoverableAuthenticatorAssertion, 10 }, 11 register::{ 12 AuthenticationExtensionsPrfOutputs, AuthenticatorAttestation, 13 AuthenticatorExtensionOutputStaticState, ClientExtensionsOutputs, 14 ClientExtensionsOutputsStaticState, CompressedP256PubKey, CompressedP384PubKey, 15 CompressedPubKeyOwned, CredentialProtectionPolicy, DynamicState, Ed25519PubKey, 16 MlDsa44PubKey, MlDsa65PubKey, MlDsa87PubKey, Registration, RsaPubKey, StaticState, 17 UncompressedPubKey, 18 }, 19 }, 20 }, 21 Challenge, Credentials as _, ExtensionInfo, ExtensionReq, PrfInput, 22 PublicKeyCredentialDescriptor, RpId, UserVerificationRequirement, 23 auth::{ 24 AllowedCredential, AllowedCredentials, AuthenticationVerificationOptions, 25 CredentialSpecificExtension, DiscoverableCredentialRequestOptions, Extension as AuthExt, 26 NonDiscoverableCredentialRequestOptions, PrfInputOwned, 27 }, 28 register::{ 29 CredProtect, CredentialCreationOptions, Extension as RegExt, FourToSixtyThree, 30 PublicKeyCredentialUserEntity, RegistrationVerificationOptions, UserHandle, 31 }, 32 }; 33 use super::{AsciiDomainStatic, Hints, PublicKeyCredentialHint}; 34 #[cfg(feature = "custom")] 35 use ed25519_dalek::{Signer as _, SigningKey}; 36 #[cfg(feature = "custom")] 37 use ml_dsa::{ 38 ExpandedSigningKey as MlDsaSigKey, MlDsa44, MlDsa65, MlDsa87, Signature as MlDsaSignature, 39 }; 40 #[cfg(feature = "custom")] 41 use p256::{ 42 ecdsa::{DerSignature as P256DerSig, SigningKey as P256Key}, 43 elliptic_curve::sec1::Tag, 44 }; 45 #[cfg(feature = "custom")] 46 use p384::ecdsa::{DerSignature as P384DerSig, SigningKey as P384Key}; 47 #[cfg(feature = "custom")] 48 use rsa::{ 49 BoxedUint, RsaPrivateKey, 50 pkcs1v15::SigningKey as RsaKey, 51 sha2::{Digest as _, Sha256}, 52 signature::{Keypair as _, SignatureEncoding as _}, 53 traits::PublicKeyParts as _, 54 }; 55 use serde_json as _; 56 #[cfg(feature = "custom")] 57 const CBOR_UINT: u8 = 0b000_00000; 58 #[cfg(feature = "custom")] 59 const CBOR_NEG: u8 = 0b001_00000; 60 #[cfg(feature = "custom")] 61 const CBOR_BYTES: u8 = 0b010_00000; 62 #[cfg(feature = "custom")] 63 const CBOR_TEXT: u8 = 0b011_00000; 64 #[cfg(feature = "custom")] 65 const CBOR_MAP: u8 = 0b101_00000; 66 #[cfg(feature = "custom")] 67 const CBOR_SIMPLE: u8 = 0b111_00000; 68 #[cfg(feature = "custom")] 69 const CBOR_TRUE: u8 = CBOR_SIMPLE | 21; 70 #[test] 71 fn ascii_domain_static() { 72 /// No trailing dot, max label length, max domain length. 73 const LONG: AsciiDomainStatic = AsciiDomainStatic::new( 74 "wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 75 ) 76 .unwrap(); 77 /// Trailing dot, min label length, max domain length. 78 const LONG_TRAILING: AsciiDomainStatic = AsciiDomainStatic::new("w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.").unwrap(); 79 /// Single character domain. 80 const SHORT: AsciiDomainStatic = AsciiDomainStatic::new("w").unwrap(); 81 let long_label = "wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"; 82 assert_eq!(long_label.len(), 63); 83 let mut long = format!("{long_label}.{long_label}.{long_label}.{long_label}"); 84 _ = long.pop(); 85 _ = long.pop(); 86 assert_eq!(LONG.0.len(), 253); 87 assert_eq!(LONG.0, long.as_str()); 88 let trailing = "w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w.w."; 89 assert_eq!(LONG_TRAILING.0.len(), 254); 90 assert_eq!(LONG_TRAILING.0, trailing); 91 assert_eq!(SHORT.0.len(), 1); 92 assert_eq!(SHORT.0, "w"); 93 assert!(AsciiDomainStatic::new("www.Example.com").is_none()); 94 assert!(AsciiDomainStatic::new("").is_none()); 95 assert!(AsciiDomainStatic::new(".").is_none()); 96 assert!(AsciiDomainStatic::new("www..c").is_none()); 97 let too_long_label = "wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"; 98 assert_eq!(too_long_label.len(), 64); 99 assert!(AsciiDomainStatic::new(too_long_label).is_none()); 100 let dom_254_no_trailing_dot = "wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"; 101 assert_eq!(dom_254_no_trailing_dot.len(), 254); 102 assert!(AsciiDomainStatic::new(dom_254_no_trailing_dot).is_none()); 103 assert!(AsciiDomainStatic::new("\u{3bb}.com").is_none()); 104 } 105 #[cfg(feature = "custom")] 106 const RP_ID: &RpId = &RpId::from_static_domain("example.com").unwrap(); 107 #[expect(clippy::panic_in_result_fn, reason = "OK in tests")] 108 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 109 #[expect(clippy::too_many_lines, reason = "a lot to test")] 110 #[test] 111 #[cfg(feature = "custom")] 112 fn eddsa_reg() -> Result<(), AggErr> { 113 let id = UserHandle::from([0]); 114 let mut opts = CredentialCreationOptions::passkey( 115 RP_ID, 116 PublicKeyCredentialUserEntity { 117 name: "foo", 118 id: &id, 119 display_name: "", 120 }, 121 Vec::new(), 122 ); 123 opts.public_key.challenge = Challenge(0); 124 opts.public_key.extensions = RegExt { 125 cred_props: None, 126 cred_protect: CredProtect::UserVerificationRequired( 127 false, 128 ExtensionInfo::RequireEnforceValue, 129 ), 130 min_pin_length: Some((FourToSixtyThree::Ten, ExtensionInfo::RequireEnforceValue)), 131 prf: Some(( 132 PrfInput { 133 first: [0].as_slice(), 134 second: Some(&[]), 135 }, 136 ExtensionInfo::RequireEnforceValue, 137 )), 138 }; 139 let client_data_json = br#"{"type":"webauthn.create","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec(); 140 // We over-allocate by 32 bytes. See [`AuthenticatorAttestation::new`] for more information. 141 let mut attestation_object = Vec::new(); 142 attestation_object.extend_from_slice( 143 [ 144 CBOR_MAP | 3, 145 CBOR_TEXT | 3, 146 b'f', 147 b'm', 148 b't', 149 CBOR_TEXT | 6, 150 b'p', 151 b'a', 152 b'c', 153 b'k', 154 b'e', 155 b'd', 156 CBOR_TEXT | 7, 157 b'a', 158 b't', 159 b't', 160 b'S', 161 b't', 162 b'm', 163 b't', 164 CBOR_MAP | 2, 165 CBOR_TEXT | 3, 166 b'a', 167 b'l', 168 b'g', 169 // COSE EdDSA. 170 CBOR_NEG | 7, 171 CBOR_TEXT | 3, 172 b's', 173 b'i', 174 b'g', 175 CBOR_BYTES | 24, 176 64, 177 0, 178 0, 179 0, 180 0, 181 0, 182 0, 183 0, 184 0, 185 0, 186 0, 187 0, 188 0, 189 0, 190 0, 191 0, 192 0, 193 0, 194 0, 195 0, 196 0, 197 0, 198 0, 199 0, 200 0, 201 0, 202 0, 203 0, 204 0, 205 0, 206 0, 207 0, 208 0, 209 0, 210 0, 211 0, 212 0, 213 0, 214 0, 215 0, 216 0, 217 0, 218 0, 219 0, 220 0, 221 0, 222 0, 223 0, 224 0, 225 0, 226 0, 227 0, 228 0, 229 0, 230 0, 231 0, 232 0, 233 0, 234 0, 235 0, 236 0, 237 0, 238 0, 239 0, 240 0, 241 CBOR_TEXT | 8, 242 b'a', 243 b'u', 244 b't', 245 b'h', 246 b'D', 247 b'a', 248 b't', 249 b'a', 250 CBOR_BYTES | 24, 251 // Length is 251. 252 251, 253 // RP ID HASH. 254 // This will be overwritten later. 255 0, 256 0, 257 0, 258 0, 259 0, 260 0, 261 0, 262 0, 263 0, 264 0, 265 0, 266 0, 267 0, 268 0, 269 0, 270 0, 271 0, 272 0, 273 0, 274 0, 275 0, 276 0, 277 0, 278 0, 279 0, 280 0, 281 0, 282 0, 283 0, 284 0, 285 0, 286 0, 287 // FLAGS. 288 // UP, UV, AT, and ED (right-to-left). 289 0b1100_0101, 290 // COUNTER. 291 // 0 as 32-bit big endian. 292 0, 293 0, 294 0, 295 0, 296 // AAGUID. 297 0, 298 0, 299 0, 300 0, 301 0, 302 0, 303 0, 304 0, 305 0, 306 0, 307 0, 308 0, 309 0, 310 0, 311 0, 312 0, 313 // L. 314 // CREDENTIAL ID length is 16 as 16-bit big endian. 315 0, 316 16, 317 // CREDENTIAL ID. 318 0, 319 0, 320 0, 321 0, 322 0, 323 0, 324 0, 325 0, 326 0, 327 0, 328 0, 329 0, 330 0, 331 0, 332 0, 333 0, 334 CBOR_MAP | 4, 335 // COSE kty. 336 CBOR_UINT | 1, 337 // COSE OKP. 338 CBOR_UINT | 1, 339 // COSE alg. 340 CBOR_UINT | 3, 341 // COSE EdDSA. 342 CBOR_NEG | 7, 343 // COSE OKP crv. 344 CBOR_NEG, 345 // COSE Ed25519. 346 CBOR_UINT | 6, 347 // COSE OKP x. 348 CBOR_NEG | 1, 349 CBOR_BYTES | 24, 350 // Length is 32. 351 32, 352 // Compressed-y coordinate. 353 // This will be overwritten later. 354 0, 355 0, 356 0, 357 0, 358 0, 359 0, 360 0, 361 0, 362 0, 363 0, 364 0, 365 0, 366 0, 367 0, 368 0, 369 0, 370 0, 371 0, 372 0, 373 0, 374 0, 375 0, 376 0, 377 0, 378 0, 379 0, 380 0, 381 0, 382 0, 383 0, 384 0, 385 0, 386 CBOR_MAP | 4, 387 CBOR_TEXT | 11, 388 b'c', 389 b'r', 390 b'e', 391 b'd', 392 b'P', 393 b'r', 394 b'o', 395 b't', 396 b'e', 397 b'c', 398 b't', 399 // userVerificationRequired. 400 CBOR_UINT | 3, 401 // CBOR text of length 11. 402 CBOR_TEXT | 11, 403 b'h', 404 b'm', 405 b'a', 406 b'c', 407 b'-', 408 b's', 409 b'e', 410 b'c', 411 b'r', 412 b'e', 413 b't', 414 CBOR_TRUE, 415 CBOR_TEXT | 12, 416 b'm', 417 b'i', 418 b'n', 419 b'P', 420 b'i', 421 b'n', 422 b'L', 423 b'e', 424 b'n', 425 b'g', 426 b't', 427 b'h', 428 CBOR_UINT | 16, 429 // CBOR text of length 14. 430 CBOR_TEXT | 14, 431 b'h', 432 b'm', 433 b'a', 434 b'c', 435 b'-', 436 b's', 437 b'e', 438 b'c', 439 b'r', 440 b'e', 441 b't', 442 b'-', 443 b'm', 444 b'c', 445 CBOR_BYTES | 24, 446 // Length is 80. 447 80, 448 1, 449 1, 450 1, 451 1, 452 1, 453 1, 454 1, 455 1, 456 1, 457 1, 458 1, 459 1, 460 1, 461 1, 462 1, 463 1, 464 1, 465 1, 466 1, 467 1, 468 1, 469 1, 470 1, 471 1, 472 1, 473 1, 474 1, 475 1, 476 1, 477 1, 478 1, 479 1, 480 1, 481 1, 482 1, 483 1, 484 1, 485 1, 486 1, 487 1, 488 1, 489 1, 490 1, 491 1, 492 1, 493 1, 494 1, 495 1, 496 1, 497 1, 498 1, 499 1, 500 1, 501 1, 502 1, 503 1, 504 1, 505 1, 506 1, 507 1, 508 1, 509 1, 510 1, 511 1, 512 1, 513 1, 514 1, 515 1, 516 1, 517 1, 518 1, 519 1, 520 1, 521 1, 522 1, 523 1, 524 1, 525 1, 526 1, 527 1, 528 ] 529 .as_slice(), 530 ); 531 attestation_object.extend_from_slice(&Sha256::digest(client_data_json.as_slice())); 532 let sig_key = SigningKey::from_bytes(&[0; 32]); 533 let ver_key = sig_key.verifying_key(); 534 let pub_key = ver_key.as_bytes(); 535 attestation_object[107..139].copy_from_slice(&Sha256::digest(RP_ID.as_ref().as_bytes())); 536 attestation_object[188..220].copy_from_slice(pub_key); 537 let sig = sig_key.sign(&attestation_object[107..]); 538 attestation_object[32..96].copy_from_slice(sig.to_bytes().as_slice()); 539 let att_obj_len = attestation_object.len() - 32; 540 attestation_object.truncate(att_obj_len); 541 assert!(matches!(opts.start_ceremony()?.0.verify( 542 RP_ID, 543 &Registration { 544 response: AuthenticatorAttestation::new( 545 client_data_json, 546 attestation_object, 547 AuthTransports::NONE, 548 ), 549 authenticator_attachment: AuthenticatorAttachment::None, 550 client_extension_results: ClientExtensionsOutputs { 551 cred_props: None, 552 prf: Some(AuthenticationExtensionsPrfOutputs { enabled: true, }), 553 }, 554 }, 555 &RegistrationVerificationOptions::<&str, &str>::default(), 556 )?.static_state.credential_public_key, UncompressedPubKey::Ed25519(k) if k.into_inner() == pub_key)); 557 Ok(()) 558 } 559 #[expect(clippy::panic_in_result_fn, reason = "OK in tests")] 560 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 561 #[expect(clippy::too_many_lines, reason = "a lot to test")] 562 #[test] 563 #[cfg(feature = "custom")] 564 fn eddsa_auth() -> Result<(), AggErr> { 565 let mut creds = AllowedCredentials::with_capacity(1); 566 _ = creds.push(AllowedCredential { 567 credential: PublicKeyCredentialDescriptor { 568 id: CredentialId::try_from(vec![0; 16].into_boxed_slice())?, 569 transports: AuthTransports::NONE, 570 }, 571 extension: CredentialSpecificExtension { 572 prf: Some(PrfInputOwned { 573 first: Vec::new(), 574 second: Some(Vec::new()), 575 ext_req: ExtensionReq::Require, 576 }), 577 }, 578 }); 579 let mut opts = NonDiscoverableCredentialRequestOptions::second_factor(RP_ID, creds); 580 opts.options.user_verification = UserVerificationRequirement::Required; 581 opts.options.challenge = Challenge(0); 582 opts.options.extensions = AuthExt { prf: None }; 583 let client_data_json = br#"{"type":"webauthn.get","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec(); 584 // We over-allocate by 32 bytes. See [`AuthenticatorAssertion::new`] for more information. 585 let mut authenticator_data = Vec::with_capacity(164); 586 authenticator_data.extend_from_slice( 587 [ 588 // rpIdHash. 589 // This will be overwritten later. 590 0, 591 0, 592 0, 593 0, 594 0, 595 0, 596 0, 597 0, 598 0, 599 0, 600 0, 601 0, 602 0, 603 0, 604 0, 605 0, 606 0, 607 0, 608 0, 609 0, 610 0, 611 0, 612 0, 613 0, 614 0, 615 0, 616 0, 617 0, 618 0, 619 0, 620 0, 621 0, 622 // flags. 623 // UP, UV, and ED (right-to-left). 624 0b1000_0101, 625 // signCount. 626 // 0 as 32-bit big endian. 627 0, 628 0, 629 0, 630 0, 631 CBOR_MAP | 1, 632 CBOR_TEXT | 11, 633 b'h', 634 b'm', 635 b'a', 636 b'c', 637 b'-', 638 b's', 639 b'e', 640 b'c', 641 b'r', 642 b'e', 643 b't', 644 CBOR_BYTES | 24, 645 // Length is 80. 646 80, 647 // Two HMAC outputs concatenated and encrypted. 648 0, 649 0, 650 0, 651 0, 652 0, 653 0, 654 0, 655 0, 656 0, 657 0, 658 0, 659 0, 660 0, 661 0, 662 0, 663 0, 664 0, 665 0, 666 0, 667 0, 668 0, 669 0, 670 0, 671 0, 672 0, 673 0, 674 0, 675 0, 676 0, 677 0, 678 0, 679 0, 680 0, 681 0, 682 0, 683 0, 684 0, 685 0, 686 0, 687 0, 688 0, 689 0, 690 0, 691 0, 692 0, 693 0, 694 0, 695 0, 696 0, 697 0, 698 0, 699 0, 700 0, 701 0, 702 0, 703 0, 704 0, 705 0, 706 0, 707 0, 708 0, 709 0, 710 0, 711 0, 712 0, 713 0, 714 0, 715 0, 716 0, 717 0, 718 0, 719 0, 720 0, 721 0, 722 0, 723 0, 724 0, 725 0, 726 0, 727 0, 728 ] 729 .as_slice(), 730 ); 731 authenticator_data[..32].copy_from_slice(&Sha256::digest(RP_ID.as_ref().as_bytes())); 732 authenticator_data.extend_from_slice(&Sha256::digest(client_data_json.as_slice())); 733 let ed_priv = SigningKey::from([0; 32]); 734 let sig = ed_priv.sign(authenticator_data.as_slice()).to_vec(); 735 authenticator_data.truncate(132); 736 assert!(!opts.start_ceremony()?.0.verify( 737 RP_ID, 738 &NonDiscoverableAuthentication { 739 raw_id: CredentialId::try_from(vec![0; 16].into_boxed_slice())?, 740 response: NonDiscoverableAuthenticatorAssertion::with_user( 741 client_data_json, 742 authenticator_data, 743 sig, 744 UserHandle::from([0]), 745 ), 746 authenticator_attachment: AuthenticatorAttachment::None, 747 }, 748 &mut AuthenticatedCredential::new( 749 CredentialId::try_from([0; 16].as_slice())?, 750 &UserHandle::from([0]), 751 StaticState { 752 credential_public_key: CompressedPubKeyOwned::Ed25519(Ed25519PubKey::from( 753 ed_priv.verifying_key().to_bytes() 754 ),), 755 extensions: AuthenticatorExtensionOutputStaticState { 756 cred_protect: CredentialProtectionPolicy::None, 757 hmac_secret: Some(true), 758 }, 759 client_extension_results: ClientExtensionsOutputsStaticState { 760 prf: Some(AuthenticationExtensionsPrfOutputs { enabled: true }), 761 } 762 }, 763 DynamicState { 764 user_verified: true, 765 backup: Backup::NotEligible, 766 sign_count: 0, 767 authenticator_attachment: AuthenticatorAttachment::None, 768 }, 769 )?, 770 &AuthenticationVerificationOptions::<&str, &str>::default(), 771 )?); 772 Ok(()) 773 } 774 #[expect(clippy::panic_in_result_fn, reason = "OK in tests")] 775 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 776 #[expect(clippy::too_many_lines, reason = "a lot to test")] 777 #[test] 778 #[cfg(feature = "custom")] 779 fn mldsa87_reg() -> Result<(), AggErr> { 780 let id = UserHandle::from([0]); 781 let mut opts = CredentialCreationOptions::passkey( 782 RP_ID, 783 PublicKeyCredentialUserEntity { 784 name: "foo", 785 id: &id, 786 display_name: "", 787 }, 788 Vec::new(), 789 ); 790 opts.public_key.challenge = Challenge(0); 791 let client_data_json = br#"{"type":"webauthn.create","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec(); 792 // We over-allocate by 32 bytes. See [`AuthenticatorAttestation::new`] for more information. 793 let mut attestation_object = Vec::with_capacity(2736); 794 attestation_object.extend_from_slice( 795 [ 796 CBOR_MAP | 3, 797 CBOR_TEXT | 3, 798 b'f', 799 b'm', 800 b't', 801 CBOR_TEXT | 4, 802 b'n', 803 b'o', 804 b'n', 805 b'e', 806 CBOR_TEXT | 7, 807 b'a', 808 b't', 809 b't', 810 b'S', 811 b't', 812 b'm', 813 b't', 814 CBOR_MAP, 815 CBOR_TEXT | 8, 816 b'a', 817 b'u', 818 b't', 819 b'h', 820 b'D', 821 b'a', 822 b't', 823 b'a', 824 CBOR_BYTES | 25, 825 10, 826 113, 827 // `rpIdHash`. 828 0, 829 0, 830 0, 831 0, 832 0, 833 0, 834 0, 835 0, 836 0, 837 0, 838 0, 839 0, 840 0, 841 0, 842 0, 843 0, 844 0, 845 0, 846 0, 847 0, 848 0, 849 0, 850 0, 851 0, 852 0, 853 0, 854 0, 855 0, 856 0, 857 0, 858 0, 859 0, 860 // `flags`. 861 0b0100_0101, 862 // `signCount`. 863 0, 864 0, 865 0, 866 0, 867 // `aaguid`. 868 0, 869 0, 870 0, 871 0, 872 0, 873 0, 874 0, 875 0, 876 0, 877 0, 878 0, 879 0, 880 0, 881 0, 882 0, 883 0, 884 // `credentialIdLength`. 885 0, 886 16, 887 // `credentialId`. 888 0, 889 0, 890 0, 891 0, 892 0, 893 0, 894 0, 895 0, 896 0, 897 0, 898 0, 899 0, 900 0, 901 0, 902 0, 903 0, 904 // ML-DSA-87 COSE key. 905 CBOR_MAP | 3, 906 // COSE kty. 907 CBOR_UINT | 1, 908 // COSE AKP 909 CBOR_UINT | 7, 910 // COSE alg. 911 CBOR_UINT | 3, 912 CBOR_NEG | 24, 913 // COSE ML-DSA-87. 914 49, 915 // `pub`. 916 CBOR_NEG, 917 CBOR_BYTES | 25, 918 // Length is 2592 as 16-bit big-endian. 919 10, 920 32, 921 // Encoded key. 922 1, 923 1, 924 1, 925 1, 926 1, 927 1, 928 1, 929 1, 930 1, 931 1, 932 1, 933 1, 934 1, 935 1, 936 1, 937 1, 938 1, 939 1, 940 1, 941 1, 942 1, 943 1, 944 1, 945 1, 946 1, 947 1, 948 1, 949 1, 950 1, 951 1, 952 1, 953 1, 954 1, 955 1, 956 1, 957 1, 958 1, 959 1, 960 1, 961 1, 962 1, 963 1, 964 1, 965 1, 966 1, 967 1, 968 1, 969 1, 970 1, 971 1, 972 1, 973 1, 974 1, 975 1, 976 1, 977 1, 978 1, 979 1, 980 1, 981 1, 982 1, 983 1, 984 1, 985 1, 986 1, 987 1, 988 1, 989 1, 990 1, 991 1, 992 1, 993 1, 994 1, 995 1, 996 1, 997 1, 998 1, 999 1, 1000 1, 1001 1, 1002 1, 1003 1, 1004 1, 1005 1, 1006 1, 1007 1, 1008 1, 1009 1, 1010 1, 1011 1, 1012 1, 1013 1, 1014 1, 1015 1, 1016 1, 1017 1, 1018 1, 1019 1, 1020 1, 1021 1, 1022 1, 1023 1, 1024 1, 1025 1, 1026 1, 1027 1, 1028 1, 1029 1, 1030 1, 1031 1, 1032 1, 1033 1, 1034 1, 1035 1, 1036 1, 1037 1, 1038 1, 1039 1, 1040 1, 1041 1, 1042 1, 1043 1, 1044 1, 1045 1, 1046 1, 1047 1, 1048 1, 1049 1, 1050 1, 1051 1, 1052 1, 1053 1, 1054 1, 1055 1, 1056 1, 1057 1, 1058 1, 1059 1, 1060 1, 1061 1, 1062 1, 1063 1, 1064 1, 1065 1, 1066 1, 1067 1, 1068 1, 1069 1, 1070 1, 1071 1, 1072 1, 1073 1, 1074 1, 1075 1, 1076 1, 1077 1, 1078 1, 1079 1, 1080 1, 1081 1, 1082 1, 1083 1, 1084 1, 1085 1, 1086 1, 1087 1, 1088 1, 1089 1, 1090 1, 1091 1, 1092 1, 1093 1, 1094 1, 1095 1, 1096 1, 1097 1, 1098 1, 1099 1, 1100 1, 1101 1, 1102 1, 1103 1, 1104 1, 1105 1, 1106 1, 1107 1, 1108 1, 1109 1, 1110 1, 1111 1, 1112 1, 1113 1, 1114 1, 1115 1, 1116 1, 1117 1, 1118 1, 1119 1, 1120 1, 1121 1, 1122 1, 1123 1, 1124 1, 1125 1, 1126 1, 1127 1, 1128 1, 1129 1, 1130 1, 1131 1, 1132 1, 1133 1, 1134 1, 1135 1, 1136 1, 1137 1, 1138 1, 1139 1, 1140 1, 1141 1, 1142 1, 1143 1, 1144 1, 1145 1, 1146 1, 1147 1, 1148 1, 1149 1, 1150 1, 1151 1, 1152 1, 1153 1, 1154 1, 1155 1, 1156 1, 1157 1, 1158 1, 1159 1, 1160 1, 1161 1, 1162 1, 1163 1, 1164 1, 1165 1, 1166 1, 1167 1, 1168 1, 1169 1, 1170 1, 1171 1, 1172 1, 1173 1, 1174 1, 1175 1, 1176 1, 1177 1, 1178 1, 1179 1, 1180 1, 1181 1, 1182 1, 1183 1, 1184 1, 1185 1, 1186 1, 1187 1, 1188 1, 1189 1, 1190 1, 1191 1, 1192 1, 1193 1, 1194 1, 1195 1, 1196 1, 1197 1, 1198 1, 1199 1, 1200 1, 1201 1, 1202 1, 1203 1, 1204 1, 1205 1, 1206 1, 1207 1, 1208 1, 1209 1, 1210 1, 1211 1, 1212 1, 1213 1, 1214 1, 1215 1, 1216 1, 1217 1, 1218 1, 1219 1, 1220 1, 1221 1, 1222 1, 1223 1, 1224 1, 1225 1, 1226 1, 1227 1, 1228 1, 1229 1, 1230 1, 1231 1, 1232 1, 1233 1, 1234 1, 1235 1, 1236 1, 1237 1, 1238 1, 1239 1, 1240 1, 1241 1, 1242 1, 1243 1, 1244 1, 1245 1, 1246 1, 1247 1, 1248 1, 1249 1, 1250 1, 1251 1, 1252 1, 1253 1, 1254 1, 1255 1, 1256 1, 1257 1, 1258 1, 1259 1, 1260 1, 1261 1, 1262 1, 1263 1, 1264 1, 1265 1, 1266 1, 1267 1, 1268 1, 1269 1, 1270 1, 1271 1, 1272 1, 1273 1, 1274 1, 1275 1, 1276 1, 1277 1, 1278 1, 1279 1, 1280 1, 1281 1, 1282 1, 1283 1, 1284 1, 1285 1, 1286 1, 1287 1, 1288 1, 1289 1, 1290 1, 1291 1, 1292 1, 1293 1, 1294 1, 1295 1, 1296 1, 1297 1, 1298 1, 1299 1, 1300 1, 1301 1, 1302 1, 1303 1, 1304 1, 1305 1, 1306 1, 1307 1, 1308 1, 1309 1, 1310 1, 1311 1, 1312 1, 1313 1, 1314 1, 1315 1, 1316 1, 1317 1, 1318 1, 1319 1, 1320 1, 1321 1, 1322 1, 1323 1, 1324 1, 1325 1, 1326 1, 1327 1, 1328 1, 1329 1, 1330 1, 1331 1, 1332 1, 1333 1, 1334 1, 1335 1, 1336 1, 1337 1, 1338 1, 1339 1, 1340 1, 1341 1, 1342 1, 1343 1, 1344 1, 1345 1, 1346 1, 1347 1, 1348 1, 1349 1, 1350 1, 1351 1, 1352 1, 1353 1, 1354 1, 1355 1, 1356 1, 1357 1, 1358 1, 1359 1, 1360 1, 1361 1, 1362 1, 1363 1, 1364 1, 1365 1, 1366 1, 1367 1, 1368 1, 1369 1, 1370 1, 1371 1, 1372 1, 1373 1, 1374 1, 1375 1, 1376 1, 1377 1, 1378 1, 1379 1, 1380 1, 1381 1, 1382 1, 1383 1, 1384 1, 1385 1, 1386 1, 1387 1, 1388 1, 1389 1, 1390 1, 1391 1, 1392 1, 1393 1, 1394 1, 1395 1, 1396 1, 1397 1, 1398 1, 1399 1, 1400 1, 1401 1, 1402 1, 1403 1, 1404 1, 1405 1, 1406 1, 1407 1, 1408 1, 1409 1, 1410 1, 1411 1, 1412 1, 1413 1, 1414 1, 1415 1, 1416 1, 1417 1, 1418 1, 1419 1, 1420 1, 1421 1, 1422 1, 1423 1, 1424 1, 1425 1, 1426 1, 1427 1, 1428 1, 1429 1, 1430 1, 1431 1, 1432 1, 1433 1, 1434 1, 1435 1, 1436 1, 1437 1, 1438 1, 1439 1, 1440 1, 1441 1, 1442 1, 1443 1, 1444 1, 1445 1, 1446 1, 1447 1, 1448 1, 1449 1, 1450 1, 1451 1, 1452 1, 1453 1, 1454 1, 1455 1, 1456 1, 1457 1, 1458 1, 1459 1, 1460 1, 1461 1, 1462 1, 1463 1, 1464 1, 1465 1, 1466 1, 1467 1, 1468 1, 1469 1, 1470 1, 1471 1, 1472 1, 1473 1, 1474 1, 1475 1, 1476 1, 1477 1, 1478 1, 1479 1, 1480 1, 1481 1, 1482 1, 1483 1, 1484 1, 1485 1, 1486 1, 1487 1, 1488 1, 1489 1, 1490 1, 1491 1, 1492 1, 1493 1, 1494 1, 1495 1, 1496 1, 1497 1, 1498 1, 1499 1, 1500 1, 1501 1, 1502 1, 1503 1, 1504 1, 1505 1, 1506 1, 1507 1, 1508 1, 1509 1, 1510 1, 1511 1, 1512 1, 1513 1, 1514 1, 1515 1, 1516 1, 1517 1, 1518 1, 1519 1, 1520 1, 1521 1, 1522 1, 1523 1, 1524 1, 1525 1, 1526 1, 1527 1, 1528 1, 1529 1, 1530 1, 1531 1, 1532 1, 1533 1, 1534 1, 1535 1, 1536 1, 1537 1, 1538 1, 1539 1, 1540 1, 1541 1, 1542 1, 1543 1, 1544 1, 1545 1, 1546 1, 1547 1, 1548 1, 1549 1, 1550 1, 1551 1, 1552 1, 1553 1, 1554 1, 1555 1, 1556 1, 1557 1, 1558 1, 1559 1, 1560 1, 1561 1, 1562 1, 1563 1, 1564 1, 1565 1, 1566 1, 1567 1, 1568 1, 1569 1, 1570 1, 1571 1, 1572 1, 1573 1, 1574 1, 1575 1, 1576 1, 1577 1, 1578 1, 1579 1, 1580 1, 1581 1, 1582 1, 1583 1, 1584 1, 1585 1, 1586 1, 1587 1, 1588 1, 1589 1, 1590 1, 1591 1, 1592 1, 1593 1, 1594 1, 1595 1, 1596 1, 1597 1, 1598 1, 1599 1, 1600 1, 1601 1, 1602 1, 1603 1, 1604 1, 1605 1, 1606 1, 1607 1, 1608 1, 1609 1, 1610 1, 1611 1, 1612 1, 1613 1, 1614 1, 1615 1, 1616 1, 1617 1, 1618 1, 1619 1, 1620 1, 1621 1, 1622 1, 1623 1, 1624 1, 1625 1, 1626 1, 1627 1, 1628 1, 1629 1, 1630 1, 1631 1, 1632 1, 1633 1, 1634 1, 1635 1, 1636 1, 1637 1, 1638 1, 1639 1, 1640 1, 1641 1, 1642 1, 1643 1, 1644 1, 1645 1, 1646 1, 1647 1, 1648 1, 1649 1, 1650 1, 1651 1, 1652 1, 1653 1, 1654 1, 1655 1, 1656 1, 1657 1, 1658 1, 1659 1, 1660 1, 1661 1, 1662 1, 1663 1, 1664 1, 1665 1, 1666 1, 1667 1, 1668 1, 1669 1, 1670 1, 1671 1, 1672 1, 1673 1, 1674 1, 1675 1, 1676 1, 1677 1, 1678 1, 1679 1, 1680 1, 1681 1, 1682 1, 1683 1, 1684 1, 1685 1, 1686 1, 1687 1, 1688 1, 1689 1, 1690 1, 1691 1, 1692 1, 1693 1, 1694 1, 1695 1, 1696 1, 1697 1, 1698 1, 1699 1, 1700 1, 1701 1, 1702 1, 1703 1, 1704 1, 1705 1, 1706 1, 1707 1, 1708 1, 1709 1, 1710 1, 1711 1, 1712 1, 1713 1, 1714 1, 1715 1, 1716 1, 1717 1, 1718 1, 1719 1, 1720 1, 1721 1, 1722 1, 1723 1, 1724 1, 1725 1, 1726 1, 1727 1, 1728 1, 1729 1, 1730 1, 1731 1, 1732 1, 1733 1, 1734 1, 1735 1, 1736 1, 1737 1, 1738 1, 1739 1, 1740 1, 1741 1, 1742 1, 1743 1, 1744 1, 1745 1, 1746 1, 1747 1, 1748 1, 1749 1, 1750 1, 1751 1, 1752 1, 1753 1, 1754 1, 1755 1, 1756 1, 1757 1, 1758 1, 1759 1, 1760 1, 1761 1, 1762 1, 1763 1, 1764 1, 1765 1, 1766 1, 1767 1, 1768 1, 1769 1, 1770 1, 1771 1, 1772 1, 1773 1, 1774 1, 1775 1, 1776 1, 1777 1, 1778 1, 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 1, 1812 1, 1813 1, 1814 1, 1815 1, 1816 1, 1817 1, 1818 1, 1819 1, 1820 1, 1821 1, 1822 1, 1823 1, 1824 1, 1825 1, 1826 1, 1827 1, 1828 1, 1829 1, 1830 1, 1831 1, 1832 1, 1833 1, 1834 1, 1835 1, 1836 1, 1837 1, 1838 1, 1839 1, 1840 1, 1841 1, 1842 1, 1843 1, 1844 1, 1845 1, 1846 1, 1847 1, 1848 1, 1849 1, 1850 1, 1851 1, 1852 1, 1853 1, 1854 1, 1855 1, 1856 1, 1857 1, 1858 1, 1859 1, 1860 1, 1861 1, 1862 1, 1863 1, 1864 1, 1865 1, 1866 1, 1867 1, 1868 1, 1869 1, 1870 1, 1871 1, 1872 1, 1873 1, 1874 1, 1875 1, 1876 1, 1877 1, 1878 1, 1879 1, 1880 1, 1881 1, 1882 1, 1883 1, 1884 1, 1885 1, 1886 1, 1887 1, 1888 1, 1889 1, 1890 1, 1891 1, 1892 1, 1893 1, 1894 1, 1895 1, 1896 1, 1897 1, 1898 1, 1899 1, 1900 1, 1901 1, 1902 1, 1903 1, 1904 1, 1905 1, 1906 1, 1907 1, 1908 1, 1909 1, 1910 1, 1911 1, 1912 1, 1913 1, 1914 1, 1915 1, 1916 1, 1917 1, 1918 1, 1919 1, 1920 1, 1921 1, 1922 1, 1923 1, 1924 1, 1925 1, 1926 1, 1927 1, 1928 1, 1929 1, 1930 1, 1931 1, 1932 1, 1933 1, 1934 1, 1935 1, 1936 1, 1937 1, 1938 1, 1939 1, 1940 1, 1941 1, 1942 1, 1943 1, 1944 1, 1945 1, 1946 1, 1947 1, 1948 1, 1949 1, 1950 1, 1951 1, 1952 1, 1953 1, 1954 1, 1955 1, 1956 1, 1957 1, 1958 1, 1959 1, 1960 1, 1961 1, 1962 1, 1963 1, 1964 1, 1965 1, 1966 1, 1967 1, 1968 1, 1969 1, 1970 1, 1971 1, 1972 1, 1973 1, 1974 1, 1975 1, 1976 1, 1977 1, 1978 1, 1979 1, 1980 1, 1981 1, 1982 1, 1983 1, 1984 1, 1985 1, 1986 1, 1987 1, 1988 1, 1989 1, 1990 1, 1991 1, 1992 1, 1993 1, 1994 1, 1995 1, 1996 1, 1997 1, 1998 1, 1999 1, 2000 1, 2001 1, 2002 1, 2003 1, 2004 1, 2005 1, 2006 1, 2007 1, 2008 1, 2009 1, 2010 1, 2011 1, 2012 1, 2013 1, 2014 1, 2015 1, 2016 1, 2017 1, 2018 1, 2019 1, 2020 1, 2021 1, 2022 1, 2023 1, 2024 1, 2025 1, 2026 1, 2027 1, 2028 1, 2029 1, 2030 1, 2031 1, 2032 1, 2033 1, 2034 1, 2035 1, 2036 1, 2037 1, 2038 1, 2039 1, 2040 1, 2041 1, 2042 1, 2043 1, 2044 1, 2045 1, 2046 1, 2047 1, 2048 1, 2049 1, 2050 1, 2051 1, 2052 1, 2053 1, 2054 1, 2055 1, 2056 1, 2057 1, 2058 1, 2059 1, 2060 1, 2061 1, 2062 1, 2063 1, 2064 1, 2065 1, 2066 1, 2067 1, 2068 1, 2069 1, 2070 1, 2071 1, 2072 1, 2073 1, 2074 1, 2075 1, 2076 1, 2077 1, 2078 1, 2079 1, 2080 1, 2081 1, 2082 1, 2083 1, 2084 1, 2085 1, 2086 1, 2087 1, 2088 1, 2089 1, 2090 1, 2091 1, 2092 1, 2093 1, 2094 1, 2095 1, 2096 1, 2097 1, 2098 1, 2099 1, 2100 1, 2101 1, 2102 1, 2103 1, 2104 1, 2105 1, 2106 1, 2107 1, 2108 1, 2109 1, 2110 1, 2111 1, 2112 1, 2113 1, 2114 1, 2115 1, 2116 1, 2117 1, 2118 1, 2119 1, 2120 1, 2121 1, 2122 1, 2123 1, 2124 1, 2125 1, 2126 1, 2127 1, 2128 1, 2129 1, 2130 1, 2131 1, 2132 1, 2133 1, 2134 1, 2135 1, 2136 1, 2137 1, 2138 1, 2139 1, 2140 1, 2141 1, 2142 1, 2143 1, 2144 1, 2145 1, 2146 1, 2147 1, 2148 1, 2149 1, 2150 1, 2151 1, 2152 1, 2153 1, 2154 1, 2155 1, 2156 1, 2157 1, 2158 1, 2159 1, 2160 1, 2161 1, 2162 1, 2163 1, 2164 1, 2165 1, 2166 1, 2167 1, 2168 1, 2169 1, 2170 1, 2171 1, 2172 1, 2173 1, 2174 1, 2175 1, 2176 1, 2177 1, 2178 1, 2179 1, 2180 1, 2181 1, 2182 1, 2183 1, 2184 1, 2185 1, 2186 1, 2187 1, 2188 1, 2189 1, 2190 1, 2191 1, 2192 1, 2193 1, 2194 1, 2195 1, 2196 1, 2197 1, 2198 1, 2199 1, 2200 1, 2201 1, 2202 1, 2203 1, 2204 1, 2205 1, 2206 1, 2207 1, 2208 1, 2209 1, 2210 1, 2211 1, 2212 1, 2213 1, 2214 1, 2215 1, 2216 1, 2217 1, 2218 1, 2219 1, 2220 1, 2221 1, 2222 1, 2223 1, 2224 1, 2225 1, 2226 1, 2227 1, 2228 1, 2229 1, 2230 1, 2231 1, 2232 1, 2233 1, 2234 1, 2235 1, 2236 1, 2237 1, 2238 1, 2239 1, 2240 1, 2241 1, 2242 1, 2243 1, 2244 1, 2245 1, 2246 1, 2247 1, 2248 1, 2249 1, 2250 1, 2251 1, 2252 1, 2253 1, 2254 1, 2255 1, 2256 1, 2257 1, 2258 1, 2259 1, 2260 1, 2261 1, 2262 1, 2263 1, 2264 1, 2265 1, 2266 1, 2267 1, 2268 1, 2269 1, 2270 1, 2271 1, 2272 1, 2273 1, 2274 1, 2275 1, 2276 1, 2277 1, 2278 1, 2279 1, 2280 1, 2281 1, 2282 1, 2283 1, 2284 1, 2285 1, 2286 1, 2287 1, 2288 1, 2289 1, 2290 1, 2291 1, 2292 1, 2293 1, 2294 1, 2295 1, 2296 1, 2297 1, 2298 1, 2299 1, 2300 1, 2301 1, 2302 1, 2303 1, 2304 1, 2305 1, 2306 1, 2307 1, 2308 1, 2309 1, 2310 1, 2311 1, 2312 1, 2313 1, 2314 1, 2315 1, 2316 1, 2317 1, 2318 1, 2319 1, 2320 1, 2321 1, 2322 1, 2323 1, 2324 1, 2325 1, 2326 1, 2327 1, 2328 1, 2329 1, 2330 1, 2331 1, 2332 1, 2333 1, 2334 1, 2335 1, 2336 1, 2337 1, 2338 1, 2339 1, 2340 1, 2341 1, 2342 1, 2343 1, 2344 1, 2345 1, 2346 1, 2347 1, 2348 1, 2349 1, 2350 1, 2351 1, 2352 1, 2353 1, 2354 1, 2355 1, 2356 1, 2357 1, 2358 1, 2359 1, 2360 1, 2361 1, 2362 1, 2363 1, 2364 1, 2365 1, 2366 1, 2367 1, 2368 1, 2369 1, 2370 1, 2371 1, 2372 1, 2373 1, 2374 1, 2375 1, 2376 1, 2377 1, 2378 1, 2379 1, 2380 1, 2381 1, 2382 1, 2383 1, 2384 1, 2385 1, 2386 1, 2387 1, 2388 1, 2389 1, 2390 1, 2391 1, 2392 1, 2393 1, 2394 1, 2395 1, 2396 1, 2397 1, 2398 1, 2399 1, 2400 1, 2401 1, 2402 1, 2403 1, 2404 1, 2405 1, 2406 1, 2407 1, 2408 1, 2409 1, 2410 1, 2411 1, 2412 1, 2413 1, 2414 1, 2415 1, 2416 1, 2417 1, 2418 1, 2419 1, 2420 1, 2421 1, 2422 1, 2423 1, 2424 1, 2425 1, 2426 1, 2427 1, 2428 1, 2429 1, 2430 1, 2431 1, 2432 1, 2433 1, 2434 1, 2435 1, 2436 1, 2437 1, 2438 1, 2439 1, 2440 1, 2441 1, 2442 1, 2443 1, 2444 1, 2445 1, 2446 1, 2447 1, 2448 1, 2449 1, 2450 1, 2451 1, 2452 1, 2453 1, 2454 1, 2455 1, 2456 1, 2457 1, 2458 1, 2459 1, 2460 1, 2461 1, 2462 1, 2463 1, 2464 1, 2465 1, 2466 1, 2467 1, 2468 1, 2469 1, 2470 1, 2471 1, 2472 1, 2473 1, 2474 1, 2475 1, 2476 1, 2477 1, 2478 1, 2479 1, 2480 1, 2481 1, 2482 1, 2483 1, 2484 1, 2485 1, 2486 1, 2487 1, 2488 1, 2489 1, 2490 1, 2491 1, 2492 1, 2493 1, 2494 1, 2495 1, 2496 1, 2497 1, 2498 1, 2499 1, 2500 1, 2501 1, 2502 1, 2503 1, 2504 1, 2505 1, 2506 1, 2507 1, 2508 1, 2509 1, 2510 1, 2511 1, 2512 1, 2513 1, 2514 1, 2515 1, 2516 1, 2517 1, 2518 1, 2519 1, 2520 1, 2521 1, 2522 1, 2523 1, 2524 1, 2525 1, 2526 1, 2527 1, 2528 1, 2529 1, 2530 1, 2531 1, 2532 1, 2533 1, 2534 1, 2535 1, 2536 1, 2537 1, 2538 1, 2539 1, 2540 1, 2541 1, 2542 1, 2543 1, 2544 1, 2545 1, 2546 1, 2547 1, 2548 1, 2549 1, 2550 1, 2551 1, 2552 1, 2553 1, 2554 1, 2555 1, 2556 1, 2557 1, 2558 1, 2559 1, 2560 1, 2561 1, 2562 1, 2563 1, 2564 1, 2565 1, 2566 1, 2567 1, 2568 1, 2569 1, 2570 1, 2571 1, 2572 1, 2573 1, 2574 1, 2575 1, 2576 1, 2577 1, 2578 1, 2579 1, 2580 1, 2581 1, 2582 1, 2583 1, 2584 1, 2585 1, 2586 1, 2587 1, 2588 1, 2589 1, 2590 1, 2591 1, 2592 1, 2593 1, 2594 1, 2595 1, 2596 1, 2597 1, 2598 1, 2599 1, 2600 1, 2601 1, 2602 1, 2603 1, 2604 1, 2605 1, 2606 1, 2607 1, 2608 1, 2609 1, 2610 1, 2611 1, 2612 1, 2613 1, 2614 1, 2615 1, 2616 1, 2617 1, 2618 1, 2619 1, 2620 1, 2621 1, 2622 1, 2623 1, 2624 1, 2625 1, 2626 1, 2627 1, 2628 1, 2629 1, 2630 1, 2631 1, 2632 1, 2633 1, 2634 1, 2635 1, 2636 1, 2637 1, 2638 1, 2639 1, 2640 1, 2641 1, 2642 1, 2643 1, 2644 1, 2645 1, 2646 1, 2647 1, 2648 1, 2649 1, 2650 1, 2651 1, 2652 1, 2653 1, 2654 1, 2655 1, 2656 1, 2657 1, 2658 1, 2659 1, 2660 1, 2661 1, 2662 1, 2663 1, 2664 1, 2665 1, 2666 1, 2667 1, 2668 1, 2669 1, 2670 1, 2671 1, 2672 1, 2673 1, 2674 1, 2675 1, 2676 1, 2677 1, 2678 1, 2679 1, 2680 1, 2681 1, 2682 1, 2683 1, 2684 1, 2685 1, 2686 1, 2687 1, 2688 1, 2689 1, 2690 1, 2691 1, 2692 1, 2693 1, 2694 1, 2695 1, 2696 1, 2697 1, 2698 1, 2699 1, 2700 1, 2701 1, 2702 1, 2703 1, 2704 1, 2705 1, 2706 1, 2707 1, 2708 1, 2709 1, 2710 1, 2711 1, 2712 1, 2713 1, 2714 1, 2715 1, 2716 1, 2717 1, 2718 1, 2719 1, 2720 1, 2721 1, 2722 1, 2723 1, 2724 1, 2725 1, 2726 1, 2727 1, 2728 1, 2729 1, 2730 1, 2731 1, 2732 1, 2733 1, 2734 1, 2735 1, 2736 1, 2737 1, 2738 1, 2739 1, 2740 1, 2741 1, 2742 1, 2743 1, 2744 1, 2745 1, 2746 1, 2747 1, 2748 1, 2749 1, 2750 1, 2751 1, 2752 1, 2753 1, 2754 1, 2755 1, 2756 1, 2757 1, 2758 1, 2759 1, 2760 1, 2761 1, 2762 1, 2763 1, 2764 1, 2765 1, 2766 1, 2767 1, 2768 1, 2769 1, 2770 1, 2771 1, 2772 1, 2773 1, 2774 1, 2775 1, 2776 1, 2777 1, 2778 1, 2779 1, 2780 1, 2781 1, 2782 1, 2783 1, 2784 1, 2785 1, 2786 1, 2787 1, 2788 1, 2789 1, 2790 1, 2791 1, 2792 1, 2793 1, 2794 1, 2795 1, 2796 1, 2797 1, 2798 1, 2799 1, 2800 1, 2801 1, 2802 1, 2803 1, 2804 1, 2805 1, 2806 1, 2807 1, 2808 1, 2809 1, 2810 1, 2811 1, 2812 1, 2813 1, 2814 1, 2815 1, 2816 1, 2817 1, 2818 1, 2819 1, 2820 1, 2821 1, 2822 1, 2823 1, 2824 1, 2825 1, 2826 1, 2827 1, 2828 1, 2829 1, 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 ] 3515 .as_slice(), 3516 ); 3517 attestation_object[31..63].copy_from_slice(&Sha256::digest(RP_ID.as_ref().as_bytes())); 3518 assert!(matches!(opts.start_ceremony()?.0.verify( 3519 RP_ID, 3520 &Registration { 3521 response: AuthenticatorAttestation::new( 3522 client_data_json, 3523 attestation_object, 3524 AuthTransports::NONE, 3525 ), 3526 authenticator_attachment: AuthenticatorAttachment::None, 3527 client_extension_results: ClientExtensionsOutputs { 3528 cred_props: None, 3529 prf: None, 3530 }, 3531 }, 3532 &RegistrationVerificationOptions::<&str, &str>::default(), 3533 )?.static_state.credential_public_key, UncompressedPubKey::MlDsa87(k) if **k.inner() == [1; 2592])); 3534 Ok(()) 3535 } 3536 #[expect( 3537 clippy::panic_in_result_fn, 3538 clippy::unwrap_in_result, 3539 clippy::unwrap_used, 3540 reason = "OK in tests" 3541 )] 3542 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 3543 #[test] 3544 #[cfg(feature = "custom")] 3545 fn mldsa87_auth() -> Result<(), AggErr> { 3546 let mut opts = DiscoverableCredentialRequestOptions::passkey(RP_ID); 3547 opts.public_key.challenge = Challenge(0); 3548 let client_data_json = br#"{"type":"webauthn.get","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec(); 3549 // We over-allocate by 32 bytes. See [`AuthenticatorAssertion::new`] for more information. 3550 let mut authenticator_data = Vec::with_capacity(69); 3551 authenticator_data.extend_from_slice( 3552 [ 3553 // rpIdHash. 3554 // This will be overwritten later. 3555 0, 3556 0, 3557 0, 3558 0, 3559 0, 3560 0, 3561 0, 3562 0, 3563 0, 3564 0, 3565 0, 3566 0, 3567 0, 3568 0, 3569 0, 3570 0, 3571 0, 3572 0, 3573 0, 3574 0, 3575 0, 3576 0, 3577 0, 3578 0, 3579 0, 3580 0, 3581 0, 3582 0, 3583 0, 3584 0, 3585 0, 3586 0, 3587 // flags. 3588 // UP and UV (right-to-left). 3589 0b0000_0101, 3590 // signCount. 3591 // 0 as 32-bit big endian. 3592 0, 3593 0, 3594 0, 3595 0, 3596 ] 3597 .as_slice(), 3598 ); 3599 authenticator_data[..32].copy_from_slice(&Sha256::digest(RP_ID.as_ref().as_bytes())); 3600 authenticator_data.extend_from_slice(&Sha256::digest(client_data_json.as_slice())); 3601 let mldsa87_key = MlDsaSigKey::<MlDsa87>::from_seed((&[0; 32]).into()); 3602 let sig: MlDsaSignature<MlDsa87> = mldsa87_key.sign(authenticator_data.as_slice()); 3603 let pub_key = mldsa87_key.verifying_key().encode(); 3604 authenticator_data.truncate(37); 3605 assert!(!opts.start_ceremony()?.0.verify( 3606 RP_ID, 3607 &DiscoverableAuthentication { 3608 raw_id: CredentialId::try_from(vec![0; 16].into_boxed_slice())?, 3609 response: DiscoverableAuthenticatorAssertion::new( 3610 client_data_json, 3611 authenticator_data, 3612 sig.encode().0.to_vec(), 3613 UserHandle::from([0]), 3614 ), 3615 authenticator_attachment: AuthenticatorAttachment::None, 3616 }, 3617 &mut AuthenticatedCredential::new( 3618 CredentialId::try_from([0; 16].as_slice())?, 3619 &UserHandle::from([0]), 3620 StaticState { 3621 credential_public_key: CompressedPubKeyOwned::MlDsa87( 3622 MlDsa87PubKey::try_from(Box::from(pub_key.as_slice())).unwrap() 3623 ), 3624 extensions: AuthenticatorExtensionOutputStaticState { 3625 cred_protect: CredentialProtectionPolicy::None, 3626 hmac_secret: None, 3627 }, 3628 client_extension_results: ClientExtensionsOutputsStaticState { prf: None } 3629 }, 3630 DynamicState { 3631 user_verified: true, 3632 backup: Backup::NotEligible, 3633 sign_count: 0, 3634 authenticator_attachment: AuthenticatorAttachment::None, 3635 }, 3636 )?, 3637 &AuthenticationVerificationOptions::<&str, &str>::default(), 3638 )?); 3639 Ok(()) 3640 } 3641 #[expect(clippy::panic_in_result_fn, reason = "OK in tests")] 3642 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 3643 #[expect(clippy::too_many_lines, reason = "a lot to test")] 3644 #[test] 3645 #[cfg(feature = "custom")] 3646 fn mldsa65_reg() -> Result<(), AggErr> { 3647 let id = UserHandle::from([0]); 3648 let mut opts = CredentialCreationOptions::passkey( 3649 RP_ID, 3650 PublicKeyCredentialUserEntity { 3651 name: "foo", 3652 id: &id, 3653 display_name: "", 3654 }, 3655 Vec::new(), 3656 ); 3657 opts.public_key.challenge = Challenge(0); 3658 let client_data_json = br#"{"type":"webauthn.create","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec(); 3659 // We over-allocate by 32 bytes. See [`AuthenticatorAttestation::new`] for more information. 3660 let mut attestation_object = Vec::with_capacity(2096); 3661 attestation_object.extend_from_slice( 3662 [ 3663 CBOR_MAP | 3, 3664 CBOR_TEXT | 3, 3665 b'f', 3666 b'm', 3667 b't', 3668 CBOR_TEXT | 4, 3669 b'n', 3670 b'o', 3671 b'n', 3672 b'e', 3673 CBOR_TEXT | 7, 3674 b'a', 3675 b't', 3676 b't', 3677 b'S', 3678 b't', 3679 b'm', 3680 b't', 3681 CBOR_MAP, 3682 CBOR_TEXT | 8, 3683 b'a', 3684 b'u', 3685 b't', 3686 b'h', 3687 b'D', 3688 b'a', 3689 b't', 3690 b'a', 3691 CBOR_BYTES | 25, 3692 7, 3693 241, 3694 // `rpIdHash`. 3695 0, 3696 0, 3697 0, 3698 0, 3699 0, 3700 0, 3701 0, 3702 0, 3703 0, 3704 0, 3705 0, 3706 0, 3707 0, 3708 0, 3709 0, 3710 0, 3711 0, 3712 0, 3713 0, 3714 0, 3715 0, 3716 0, 3717 0, 3718 0, 3719 0, 3720 0, 3721 0, 3722 0, 3723 0, 3724 0, 3725 0, 3726 0, 3727 // `flags`. 3728 0b0100_0101, 3729 // `signCount`. 3730 0, 3731 0, 3732 0, 3733 0, 3734 // `aaguid`. 3735 0, 3736 0, 3737 0, 3738 0, 3739 0, 3740 0, 3741 0, 3742 0, 3743 0, 3744 0, 3745 0, 3746 0, 3747 0, 3748 0, 3749 0, 3750 0, 3751 // `credentialIdLength`. 3752 0, 3753 16, 3754 // `credentialId`. 3755 0, 3756 0, 3757 0, 3758 0, 3759 0, 3760 0, 3761 0, 3762 0, 3763 0, 3764 0, 3765 0, 3766 0, 3767 0, 3768 0, 3769 0, 3770 0, 3771 // ML-DSA-65 COSE key. 3772 CBOR_MAP | 3, 3773 // COSE kty. 3774 CBOR_UINT | 1, 3775 // COSE AKP 3776 CBOR_UINT | 7, 3777 // COSE alg. 3778 CBOR_UINT | 3, 3779 CBOR_NEG | 24, 3780 // COSE ML-DSA-65. 3781 48, 3782 // `pub`. 3783 CBOR_NEG, 3784 CBOR_BYTES | 25, 3785 // Length is 1952 as 16-bit big-endian. 3786 7, 3787 160, 3788 // Encoded key. 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 1, 5423 1, 5424 1, 5425 1, 5426 1, 5427 1, 5428 1, 5429 1, 5430 1, 5431 1, 5432 1, 5433 1, 5434 1, 5435 1, 5436 1, 5437 1, 5438 1, 5439 1, 5440 1, 5441 1, 5442 1, 5443 1, 5444 1, 5445 1, 5446 1, 5447 1, 5448 1, 5449 1, 5450 1, 5451 1, 5452 1, 5453 1, 5454 1, 5455 1, 5456 1, 5457 1, 5458 1, 5459 1, 5460 1, 5461 1, 5462 1, 5463 1, 5464 1, 5465 1, 5466 1, 5467 1, 5468 1, 5469 1, 5470 1, 5471 1, 5472 1, 5473 1, 5474 1, 5475 1, 5476 1, 5477 1, 5478 1, 5479 1, 5480 1, 5481 1, 5482 1, 5483 1, 5484 1, 5485 1, 5486 1, 5487 1, 5488 1, 5489 1, 5490 1, 5491 1, 5492 1, 5493 1, 5494 1, 5495 1, 5496 1, 5497 1, 5498 1, 5499 1, 5500 1, 5501 1, 5502 1, 5503 1, 5504 1, 5505 1, 5506 1, 5507 1, 5508 1, 5509 1, 5510 1, 5511 1, 5512 1, 5513 1, 5514 1, 5515 1, 5516 1, 5517 1, 5518 1, 5519 1, 5520 1, 5521 1, 5522 1, 5523 1, 5524 1, 5525 1, 5526 1, 5527 1, 5528 1, 5529 1, 5530 1, 5531 1, 5532 1, 5533 1, 5534 1, 5535 1, 5536 1, 5537 1, 5538 1, 5539 1, 5540 1, 5541 1, 5542 1, 5543 1, 5544 1, 5545 1, 5546 1, 5547 1, 5548 1, 5549 1, 5550 1, 5551 1, 5552 1, 5553 1, 5554 1, 5555 1, 5556 1, 5557 1, 5558 1, 5559 1, 5560 1, 5561 1, 5562 1, 5563 1, 5564 1, 5565 1, 5566 1, 5567 1, 5568 1, 5569 1, 5570 1, 5571 1, 5572 1, 5573 1, 5574 1, 5575 1, 5576 1, 5577 1, 5578 1, 5579 1, 5580 1, 5581 1, 5582 1, 5583 1, 5584 1, 5585 1, 5586 1, 5587 1, 5588 1, 5589 1, 5590 1, 5591 1, 5592 1, 5593 1, 5594 1, 5595 1, 5596 1, 5597 1, 5598 1, 5599 1, 5600 1, 5601 1, 5602 1, 5603 1, 5604 1, 5605 1, 5606 1, 5607 1, 5608 1, 5609 1, 5610 1, 5611 1, 5612 1, 5613 1, 5614 1, 5615 1, 5616 1, 5617 1, 5618 1, 5619 1, 5620 1, 5621 1, 5622 1, 5623 1, 5624 1, 5625 1, 5626 1, 5627 1, 5628 1, 5629 1, 5630 1, 5631 1, 5632 1, 5633 1, 5634 1, 5635 1, 5636 1, 5637 1, 5638 1, 5639 1, 5640 1, 5641 1, 5642 1, 5643 1, 5644 1, 5645 1, 5646 1, 5647 1, 5648 1, 5649 1, 5650 1, 5651 1, 5652 1, 5653 1, 5654 1, 5655 1, 5656 1, 5657 1, 5658 1, 5659 1, 5660 1, 5661 1, 5662 1, 5663 1, 5664 1, 5665 1, 5666 1, 5667 1, 5668 1, 5669 1, 5670 1, 5671 1, 5672 1, 5673 1, 5674 1, 5675 1, 5676 1, 5677 1, 5678 1, 5679 1, 5680 1, 5681 1, 5682 1, 5683 1, 5684 1, 5685 1, 5686 1, 5687 1, 5688 1, 5689 1, 5690 1, 5691 1, 5692 1, 5693 1, 5694 1, 5695 1, 5696 1, 5697 1, 5698 1, 5699 1, 5700 1, 5701 1, 5702 1, 5703 1, 5704 1, 5705 1, 5706 1, 5707 1, 5708 1, 5709 1, 5710 1, 5711 1, 5712 1, 5713 1, 5714 1, 5715 1, 5716 1, 5717 1, 5718 1, 5719 1, 5720 1, 5721 1, 5722 1, 5723 1, 5724 1, 5725 1, 5726 1, 5727 1, 5728 1, 5729 1, 5730 1, 5731 1, 5732 1, 5733 1, 5734 1, 5735 1, 5736 1, 5737 1, 5738 1, 5739 1, 5740 1, 5741 ] 5742 .as_slice(), 5743 ); 5744 attestation_object[31..63].copy_from_slice(&Sha256::digest(RP_ID.as_ref().as_bytes())); 5745 assert!(matches!(opts.start_ceremony()?.0.verify( 5746 RP_ID, 5747 &Registration { 5748 response: AuthenticatorAttestation::new( 5749 client_data_json, 5750 attestation_object, 5751 AuthTransports::NONE, 5752 ), 5753 authenticator_attachment: AuthenticatorAttachment::None, 5754 client_extension_results: ClientExtensionsOutputs { 5755 cred_props: None, 5756 prf: None, 5757 }, 5758 }, 5759 &RegistrationVerificationOptions::<&str, &str>::default(), 5760 )?.static_state.credential_public_key, UncompressedPubKey::MlDsa65(k) if **k.inner() == [1; 1952])); 5761 Ok(()) 5762 } 5763 #[expect( 5764 clippy::panic_in_result_fn, 5765 clippy::unwrap_in_result, 5766 clippy::unwrap_used, 5767 reason = "OK in tests" 5768 )] 5769 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 5770 #[test] 5771 #[cfg(feature = "custom")] 5772 fn mldsa65_auth() -> Result<(), AggErr> { 5773 let mut opts = DiscoverableCredentialRequestOptions::passkey(RP_ID); 5774 opts.public_key.challenge = Challenge(0); 5775 let client_data_json = br#"{"type":"webauthn.get","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec(); 5776 // We over-allocate by 32 bytes. See [`AuthenticatorAssertion::new`] for more information. 5777 let mut authenticator_data = Vec::with_capacity(69); 5778 authenticator_data.extend_from_slice( 5779 [ 5780 // rpIdHash. 5781 // This will be overwritten later. 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 0, 5796 0, 5797 0, 5798 0, 5799 0, 5800 0, 5801 0, 5802 0, 5803 0, 5804 0, 5805 0, 5806 0, 5807 0, 5808 0, 5809 0, 5810 0, 5811 0, 5812 0, 5813 0, 5814 // flags. 5815 // UP and UV (right-to-left). 5816 0b0000_0101, 5817 // signCount. 5818 // 0 as 32-bit big endian. 5819 0, 5820 0, 5821 0, 5822 0, 5823 ] 5824 .as_slice(), 5825 ); 5826 authenticator_data[..32].copy_from_slice(&Sha256::digest(RP_ID.as_ref().as_bytes())); 5827 authenticator_data.extend_from_slice(&Sha256::digest(client_data_json.as_slice())); 5828 let mldsa65_key = MlDsaSigKey::<MlDsa65>::from_seed((&[0; 32]).into()); 5829 let sig: MlDsaSignature<MlDsa65> = mldsa65_key.sign(authenticator_data.as_slice()); 5830 let pub_key = mldsa65_key.verifying_key().encode(); 5831 authenticator_data.truncate(37); 5832 assert!(!opts.start_ceremony()?.0.verify( 5833 RP_ID, 5834 &DiscoverableAuthentication { 5835 raw_id: CredentialId::try_from(vec![0; 16].into_boxed_slice())?, 5836 response: DiscoverableAuthenticatorAssertion::new( 5837 client_data_json, 5838 authenticator_data, 5839 sig.encode().0.to_vec(), 5840 UserHandle::from([0]), 5841 ), 5842 authenticator_attachment: AuthenticatorAttachment::None, 5843 }, 5844 &mut AuthenticatedCredential::new( 5845 CredentialId::try_from([0; 16].as_slice())?, 5846 &UserHandle::from([0]), 5847 StaticState { 5848 credential_public_key: CompressedPubKeyOwned::MlDsa65( 5849 MlDsa65PubKey::try_from(Box::from(pub_key.as_slice())).unwrap() 5850 ), 5851 extensions: AuthenticatorExtensionOutputStaticState { 5852 cred_protect: CredentialProtectionPolicy::None, 5853 hmac_secret: None, 5854 }, 5855 client_extension_results: ClientExtensionsOutputsStaticState { prf: None } 5856 }, 5857 DynamicState { 5858 user_verified: true, 5859 backup: Backup::NotEligible, 5860 sign_count: 0, 5861 authenticator_attachment: AuthenticatorAttachment::None, 5862 }, 5863 )?, 5864 &AuthenticationVerificationOptions::<&str, &str>::default(), 5865 )?); 5866 Ok(()) 5867 } 5868 #[expect(clippy::panic_in_result_fn, reason = "OK in tests")] 5869 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 5870 #[expect(clippy::too_many_lines, reason = "a lot to test")] 5871 #[test] 5872 #[cfg(feature = "custom")] 5873 fn mldsa44_reg() -> Result<(), AggErr> { 5874 let id = UserHandle::from([0]); 5875 let mut opts = CredentialCreationOptions::passkey( 5876 RP_ID, 5877 PublicKeyCredentialUserEntity { 5878 name: "foo", 5879 id: &id, 5880 display_name: "", 5881 }, 5882 Vec::new(), 5883 ); 5884 opts.public_key.challenge = Challenge(0); 5885 let client_data_json = br#"{"type":"webauthn.create","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec(); 5886 // We over-allocate by 32 bytes. See [`AuthenticatorAttestation::new`] for more information. 5887 let mut attestation_object = Vec::with_capacity(1456); 5888 attestation_object.extend_from_slice( 5889 [ 5890 CBOR_MAP | 3, 5891 CBOR_TEXT | 3, 5892 b'f', 5893 b'm', 5894 b't', 5895 CBOR_TEXT | 4, 5896 b'n', 5897 b'o', 5898 b'n', 5899 b'e', 5900 CBOR_TEXT | 7, 5901 b'a', 5902 b't', 5903 b't', 5904 b'S', 5905 b't', 5906 b'm', 5907 b't', 5908 CBOR_MAP, 5909 CBOR_TEXT | 8, 5910 b'a', 5911 b'u', 5912 b't', 5913 b'h', 5914 b'D', 5915 b'a', 5916 b't', 5917 b'a', 5918 CBOR_BYTES | 25, 5919 5, 5920 113, 5921 // `rpIdHash`. 5922 0, 5923 0, 5924 0, 5925 0, 5926 0, 5927 0, 5928 0, 5929 0, 5930 0, 5931 0, 5932 0, 5933 0, 5934 0, 5935 0, 5936 0, 5937 0, 5938 0, 5939 0, 5940 0, 5941 0, 5942 0, 5943 0, 5944 0, 5945 0, 5946 0, 5947 0, 5948 0, 5949 0, 5950 0, 5951 0, 5952 0, 5953 0, 5954 // `flags`. 5955 0b0100_0101, 5956 // `signCount`. 5957 0, 5958 0, 5959 0, 5960 0, 5961 // `aaguid`. 5962 0, 5963 0, 5964 0, 5965 0, 5966 0, 5967 0, 5968 0, 5969 0, 5970 0, 5971 0, 5972 0, 5973 0, 5974 0, 5975 0, 5976 0, 5977 0, 5978 // `credentialIdLength`. 5979 0, 5980 16, 5981 // `credentialId`. 5982 0, 5983 0, 5984 0, 5985 0, 5986 0, 5987 0, 5988 0, 5989 0, 5990 0, 5991 0, 5992 0, 5993 0, 5994 0, 5995 0, 5996 0, 5997 0, 5998 // ML-DSA-44 COSE key. 5999 CBOR_MAP | 3, 6000 // COSE kty. 6001 CBOR_UINT | 1, 6002 // COSE AKP 6003 CBOR_UINT | 7, 6004 // COSE alg. 6005 CBOR_UINT | 3, 6006 CBOR_NEG | 24, 6007 // COSE ML-DSA-44. 6008 47, 6009 // `pub`. 6010 CBOR_NEG, 6011 CBOR_BYTES | 25, 6012 // Length is 1312 as 16-bit big-endian. 6013 5, 6014 32, 6015 // Encoded key. 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 ] 7329 .as_slice(), 7330 ); 7331 attestation_object[31..63].copy_from_slice(&Sha256::digest(RP_ID.as_ref().as_bytes())); 7332 assert!(matches!(opts.start_ceremony()?.0.verify( 7333 RP_ID, 7334 &Registration { 7335 response: AuthenticatorAttestation::new( 7336 client_data_json, 7337 attestation_object, 7338 AuthTransports::NONE, 7339 ), 7340 authenticator_attachment: AuthenticatorAttachment::None, 7341 client_extension_results: ClientExtensionsOutputs { 7342 cred_props: None, 7343 prf: None, 7344 }, 7345 }, 7346 &RegistrationVerificationOptions::<&str, &str>::default(), 7347 )?.static_state.credential_public_key, UncompressedPubKey::MlDsa44(k) if **k.inner() == [1; 1312])); 7348 Ok(()) 7349 } 7350 #[expect( 7351 clippy::panic_in_result_fn, 7352 clippy::unwrap_in_result, 7353 clippy::unwrap_used, 7354 reason = "OK in tests" 7355 )] 7356 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 7357 #[test] 7358 #[cfg(feature = "custom")] 7359 fn mldsa44_auth() -> Result<(), AggErr> { 7360 let mut opts = DiscoverableCredentialRequestOptions::passkey(RP_ID); 7361 opts.public_key.challenge = Challenge(0); 7362 let client_data_json = br#"{"type":"webauthn.get","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec(); 7363 // We over-allocate by 32 bytes. See [`AuthenticatorAssertion::new`] for more information. 7364 let mut authenticator_data = Vec::with_capacity(69); 7365 authenticator_data.extend_from_slice( 7366 [ 7367 // rpIdHash. 7368 // This will be overwritten later. 7369 0, 7370 0, 7371 0, 7372 0, 7373 0, 7374 0, 7375 0, 7376 0, 7377 0, 7378 0, 7379 0, 7380 0, 7381 0, 7382 0, 7383 0, 7384 0, 7385 0, 7386 0, 7387 0, 7388 0, 7389 0, 7390 0, 7391 0, 7392 0, 7393 0, 7394 0, 7395 0, 7396 0, 7397 0, 7398 0, 7399 0, 7400 0, 7401 // flags. 7402 // UP and UV (right-to-left). 7403 0b0000_0101, 7404 // signCount. 7405 // 0 as 32-bit big endian. 7406 0, 7407 0, 7408 0, 7409 0, 7410 ] 7411 .as_slice(), 7412 ); 7413 authenticator_data[..32].copy_from_slice(&Sha256::digest(RP_ID.as_ref().as_bytes())); 7414 authenticator_data.extend_from_slice(&Sha256::digest(client_data_json.as_slice())); 7415 let mldsa44_key = MlDsaSigKey::<MlDsa44>::from_seed((&[0; 32]).into()); 7416 let sig: MlDsaSignature<MlDsa44> = mldsa44_key.sign(authenticator_data.as_slice()); 7417 let pub_key = mldsa44_key.verifying_key().encode(); 7418 authenticator_data.truncate(37); 7419 assert!(!opts.start_ceremony()?.0.verify( 7420 RP_ID, 7421 &DiscoverableAuthentication { 7422 raw_id: CredentialId::try_from(vec![0; 16].into_boxed_slice())?, 7423 response: DiscoverableAuthenticatorAssertion::new( 7424 client_data_json, 7425 authenticator_data, 7426 sig.encode().0.to_vec(), 7427 UserHandle::from([0]), 7428 ), 7429 authenticator_attachment: AuthenticatorAttachment::None, 7430 }, 7431 &mut AuthenticatedCredential::new( 7432 CredentialId::try_from([0; 16].as_slice())?, 7433 &UserHandle::from([0]), 7434 StaticState { 7435 credential_public_key: CompressedPubKeyOwned::MlDsa44( 7436 MlDsa44PubKey::try_from(Box::from(pub_key.as_slice())).unwrap() 7437 ), 7438 extensions: AuthenticatorExtensionOutputStaticState { 7439 cred_protect: CredentialProtectionPolicy::None, 7440 hmac_secret: None, 7441 }, 7442 client_extension_results: ClientExtensionsOutputsStaticState { prf: None } 7443 }, 7444 DynamicState { 7445 user_verified: true, 7446 backup: Backup::NotEligible, 7447 sign_count: 0, 7448 authenticator_attachment: AuthenticatorAttachment::None, 7449 }, 7450 )?, 7451 &AuthenticationVerificationOptions::<&str, &str>::default(), 7452 )?); 7453 Ok(()) 7454 } 7455 #[expect( 7456 clippy::panic_in_result_fn, 7457 clippy::unwrap_in_result, 7458 clippy::unwrap_used, 7459 reason = "OK in tests" 7460 )] 7461 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 7462 #[expect(clippy::too_many_lines, reason = "a lot to test")] 7463 #[test] 7464 #[cfg(feature = "custom")] 7465 fn es256_reg() -> Result<(), AggErr> { 7466 let id = UserHandle::from([0]); 7467 let mut opts = CredentialCreationOptions::passkey( 7468 RP_ID, 7469 PublicKeyCredentialUserEntity { 7470 name: "foo", 7471 id: &id, 7472 display_name: "", 7473 }, 7474 Vec::new(), 7475 ); 7476 opts.public_key.challenge = Challenge(0); 7477 let client_data_json = br#"{"type":"webauthn.create","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec(); 7478 // We over-allocate by 32 bytes. See [`AuthenticatorAttestation::new`] for more information. 7479 let mut attestation_object = Vec::with_capacity(210); 7480 attestation_object.extend_from_slice( 7481 [ 7482 CBOR_MAP | 3, 7483 CBOR_TEXT | 3, 7484 b'f', 7485 b'm', 7486 b't', 7487 CBOR_TEXT | 4, 7488 b'n', 7489 b'o', 7490 b'n', 7491 b'e', 7492 CBOR_TEXT | 7, 7493 b'a', 7494 b't', 7495 b't', 7496 b'S', 7497 b't', 7498 b'm', 7499 b't', 7500 CBOR_MAP, 7501 CBOR_TEXT | 8, 7502 b'a', 7503 b'u', 7504 b't', 7505 b'h', 7506 b'D', 7507 b'a', 7508 b't', 7509 b'a', 7510 CBOR_BYTES | 24, 7511 // Length is 148. 7512 148, 7513 // RP ID HASH. 7514 // This will be overwritten later. 7515 0, 7516 0, 7517 0, 7518 0, 7519 0, 7520 0, 7521 0, 7522 0, 7523 0, 7524 0, 7525 0, 7526 0, 7527 0, 7528 0, 7529 0, 7530 0, 7531 0, 7532 0, 7533 0, 7534 0, 7535 0, 7536 0, 7537 0, 7538 0, 7539 0, 7540 0, 7541 0, 7542 0, 7543 0, 7544 0, 7545 0, 7546 0, 7547 // FLAGS. 7548 // UP, UV, and AT (right-to-left). 7549 0b0100_0101, 7550 // COUNTER. 7551 // 0 as 32-bit big endian. 7552 0, 7553 0, 7554 0, 7555 0, 7556 // AAGUID. 7557 0, 7558 0, 7559 0, 7560 0, 7561 0, 7562 0, 7563 0, 7564 0, 7565 0, 7566 0, 7567 0, 7568 0, 7569 0, 7570 0, 7571 0, 7572 0, 7573 // L. 7574 // CREDENTIAL ID length is 16 as 16-bit big endian. 7575 0, 7576 16, 7577 // CREDENTIAL ID. 7578 0, 7579 0, 7580 0, 7581 0, 7582 0, 7583 0, 7584 0, 7585 0, 7586 0, 7587 0, 7588 0, 7589 0, 7590 0, 7591 0, 7592 0, 7593 0, 7594 CBOR_MAP | 5, 7595 // COSE kty. 7596 CBOR_UINT | 1, 7597 // COSE EC2. 7598 CBOR_UINT | 2, 7599 // COSE alg. 7600 CBOR_UINT | 3, 7601 // COSE ES256. 7602 CBOR_NEG | 6, 7603 // COSE EC2 crv. 7604 CBOR_NEG, 7605 // COSE P-256. 7606 CBOR_UINT | 1, 7607 // COSE EC2 x. 7608 CBOR_NEG | 1, 7609 CBOR_BYTES | 24, 7610 // Length is 32. 7611 32, 7612 // X-coordinate. This will be overwritten later. 7613 0, 7614 0, 7615 0, 7616 0, 7617 0, 7618 0, 7619 0, 7620 0, 7621 0, 7622 0, 7623 0, 7624 0, 7625 0, 7626 0, 7627 0, 7628 0, 7629 0, 7630 0, 7631 0, 7632 0, 7633 0, 7634 0, 7635 0, 7636 0, 7637 0, 7638 0, 7639 0, 7640 0, 7641 0, 7642 0, 7643 0, 7644 0, 7645 // COSE EC2 y. 7646 CBOR_NEG | 2, 7647 CBOR_BYTES | 24, 7648 // Length is 32. 7649 32, 7650 // Y-coordinate. This will be overwritten later. 7651 0, 7652 0, 7653 0, 7654 0, 7655 0, 7656 0, 7657 0, 7658 0, 7659 0, 7660 0, 7661 0, 7662 0, 7663 0, 7664 0, 7665 0, 7666 0, 7667 0, 7668 0, 7669 0, 7670 0, 7671 0, 7672 0, 7673 0, 7674 0, 7675 0, 7676 0, 7677 0, 7678 0, 7679 0, 7680 0, 7681 0, 7682 0, 7683 ] 7684 .as_slice(), 7685 ); 7686 attestation_object[30..62].copy_from_slice(&Sha256::digest(RP_ID.as_ref().as_bytes())); 7687 let p256_key = P256Key::from_bytes( 7688 &[ 7689 137, 133, 36, 206, 163, 47, 255, 5, 76, 144, 163, 141, 40, 109, 108, 240, 246, 115, 7690 178, 237, 169, 68, 6, 129, 92, 21, 238, 127, 55, 158, 207, 95, 7691 ] 7692 .into(), 7693 ) 7694 .unwrap() 7695 .verifying_key() 7696 .to_sec1_point(false); 7697 let x = p256_key.x().unwrap(); 7698 let y = p256_key.y().unwrap(); 7699 attestation_object[111..143].copy_from_slice(x); 7700 attestation_object[146..].copy_from_slice(y); 7701 assert!(matches!(opts.start_ceremony()?.0.verify( 7702 RP_ID, 7703 &Registration { 7704 response: AuthenticatorAttestation::new( 7705 client_data_json, 7706 attestation_object, 7707 AuthTransports::NONE, 7708 ), 7709 authenticator_attachment: AuthenticatorAttachment::None, 7710 client_extension_results: ClientExtensionsOutputs { 7711 cred_props: None, 7712 prf: None, 7713 }, 7714 }, 7715 &RegistrationVerificationOptions::<&str, &str>::default(), 7716 )?.static_state.credential_public_key, UncompressedPubKey::P256(k) if *k.x() == **x && *k.y() == **y)); 7717 Ok(()) 7718 } 7719 #[expect( 7720 clippy::panic_in_result_fn, 7721 clippy::unwrap_in_result, 7722 clippy::unwrap_used, 7723 reason = "OK in tests" 7724 )] 7725 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 7726 #[test] 7727 #[cfg(feature = "custom")] 7728 fn es256_auth() -> Result<(), AggErr> { 7729 let mut opts = DiscoverableCredentialRequestOptions::passkey(RP_ID); 7730 opts.public_key.challenge = Challenge(0); 7731 let client_data_json = br#"{"type":"webauthn.get","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec(); 7732 // We over-allocate by 32 bytes. See [`AuthenticatorAssertion::new`] for more information. 7733 let mut authenticator_data = Vec::with_capacity(69); 7734 authenticator_data.extend_from_slice( 7735 [ 7736 // rpIdHash. 7737 // This will be overwritten later. 7738 0, 7739 0, 7740 0, 7741 0, 7742 0, 7743 0, 7744 0, 7745 0, 7746 0, 7747 0, 7748 0, 7749 0, 7750 0, 7751 0, 7752 0, 7753 0, 7754 0, 7755 0, 7756 0, 7757 0, 7758 0, 7759 0, 7760 0, 7761 0, 7762 0, 7763 0, 7764 0, 7765 0, 7766 0, 7767 0, 7768 0, 7769 0, 7770 // flags. 7771 // UP and UV (right-to-left). 7772 0b0000_0101, 7773 // signCount. 7774 // 0 as 32-bit big endian. 7775 0, 7776 0, 7777 0, 7778 0, 7779 ] 7780 .as_slice(), 7781 ); 7782 authenticator_data[..32].copy_from_slice(&Sha256::digest(RP_ID.as_ref().as_bytes())); 7783 authenticator_data.extend_from_slice(&Sha256::digest(client_data_json.as_slice())); 7784 let p256_key = P256Key::from_bytes( 7785 &[ 7786 137, 133, 36, 206, 163, 47, 255, 5, 76, 144, 163, 141, 40, 109, 108, 240, 246, 115, 7787 178, 237, 169, 68, 6, 129, 92, 21, 238, 127, 55, 158, 207, 95, 7788 ] 7789 .into(), 7790 ) 7791 .unwrap(); 7792 let der_sig: P256DerSig = p256_key.sign(authenticator_data.as_slice()); 7793 let pub_key = p256_key.verifying_key().to_sec1_point(true); 7794 authenticator_data.truncate(37); 7795 assert!(!opts.start_ceremony()?.0.verify( 7796 RP_ID, 7797 &DiscoverableAuthentication { 7798 raw_id: CredentialId::try_from(vec![0; 16].into_boxed_slice())?, 7799 response: DiscoverableAuthenticatorAssertion::new( 7800 client_data_json, 7801 authenticator_data, 7802 der_sig.as_bytes().into(), 7803 UserHandle::from([0]), 7804 ), 7805 authenticator_attachment: AuthenticatorAttachment::None, 7806 }, 7807 &mut AuthenticatedCredential::new( 7808 CredentialId::try_from([0; 16].as_slice())?, 7809 &UserHandle::from([0]), 7810 StaticState { 7811 credential_public_key: CompressedPubKeyOwned::P256(CompressedP256PubKey::from(( 7812 (*pub_key.x().unwrap()).into(), 7813 pub_key.tag() == Tag::CompressedOddY 7814 )),), 7815 extensions: AuthenticatorExtensionOutputStaticState { 7816 cred_protect: CredentialProtectionPolicy::None, 7817 hmac_secret: None, 7818 }, 7819 client_extension_results: ClientExtensionsOutputsStaticState { prf: None } 7820 }, 7821 DynamicState { 7822 user_verified: true, 7823 backup: Backup::NotEligible, 7824 sign_count: 0, 7825 authenticator_attachment: AuthenticatorAttachment::None, 7826 }, 7827 )?, 7828 &AuthenticationVerificationOptions::<&str, &str>::default(), 7829 )?); 7830 Ok(()) 7831 } 7832 #[expect( 7833 clippy::panic_in_result_fn, 7834 clippy::unwrap_in_result, 7835 clippy::unwrap_used, 7836 reason = "OK in tests" 7837 )] 7838 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 7839 #[expect(clippy::too_many_lines, reason = "a lot to test")] 7840 #[test] 7841 #[cfg(feature = "custom")] 7842 fn es384_reg() -> Result<(), AggErr> { 7843 let id = UserHandle::from([0]); 7844 let mut opts = CredentialCreationOptions::passkey( 7845 RP_ID, 7846 PublicKeyCredentialUserEntity { 7847 name: "foo", 7848 id: &id, 7849 display_name: "", 7850 }, 7851 Vec::new(), 7852 ); 7853 opts.public_key.challenge = Challenge(0); 7854 let client_data_json = br#"{"type":"webauthn.create","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec(); 7855 // We over-allocate by 32 bytes. See [`AuthenticatorAttestation::new`] for more information. 7856 let mut attestation_object = Vec::with_capacity(243); 7857 attestation_object.extend_from_slice( 7858 [ 7859 CBOR_MAP | 3, 7860 CBOR_TEXT | 3, 7861 b'f', 7862 b'm', 7863 b't', 7864 CBOR_TEXT | 4, 7865 b'n', 7866 b'o', 7867 b'n', 7868 b'e', 7869 // CBOR text of length 7. 7870 CBOR_TEXT | 7, 7871 b'a', 7872 b't', 7873 b't', 7874 b'S', 7875 b't', 7876 b'm', 7877 b't', 7878 CBOR_MAP, 7879 CBOR_TEXT | 8, 7880 b'a', 7881 b'u', 7882 b't', 7883 b'h', 7884 b'D', 7885 b'a', 7886 b't', 7887 b'a', 7888 CBOR_BYTES | 24, 7889 // Length is 181. 7890 181, 7891 // RP ID HASH. 7892 // This will be overwritten later. 7893 0, 7894 0, 7895 0, 7896 0, 7897 0, 7898 0, 7899 0, 7900 0, 7901 0, 7902 0, 7903 0, 7904 0, 7905 0, 7906 0, 7907 0, 7908 0, 7909 0, 7910 0, 7911 0, 7912 0, 7913 0, 7914 0, 7915 0, 7916 0, 7917 0, 7918 0, 7919 0, 7920 0, 7921 0, 7922 0, 7923 0, 7924 0, 7925 // FLAGS. 7926 // UP, UV, and AT (right-to-left). 7927 0b0100_0101, 7928 // COUNTER. 7929 // 0 as 32-bit big-endian. 7930 0, 7931 0, 7932 0, 7933 0, 7934 // AAGUID. 7935 0, 7936 0, 7937 0, 7938 0, 7939 0, 7940 0, 7941 0, 7942 0, 7943 0, 7944 0, 7945 0, 7946 0, 7947 0, 7948 0, 7949 0, 7950 0, 7951 // L. 7952 // CREDENTIAL ID length is 16 as 16-bit big endian. 7953 0, 7954 16, 7955 // CREDENTIAL ID. 7956 0, 7957 0, 7958 0, 7959 0, 7960 0, 7961 0, 7962 0, 7963 0, 7964 0, 7965 0, 7966 0, 7967 0, 7968 0, 7969 0, 7970 0, 7971 0, 7972 CBOR_MAP | 5, 7973 // COSE kty. 7974 CBOR_UINT | 1, 7975 // COSE EC2. 7976 CBOR_UINT | 2, 7977 // COSE alg. 7978 CBOR_UINT | 3, 7979 CBOR_NEG | 24, 7980 // COSE ES384. 7981 34, 7982 // COSE EC2 crv. 7983 CBOR_NEG, 7984 // COSE P-384. 7985 CBOR_UINT | 2, 7986 // COSE EC2 x. 7987 CBOR_NEG | 1, 7988 CBOR_BYTES | 24, 7989 // Length is 48. 7990 48, 7991 // X-coordinate. This will be overwritten later. 7992 0, 7993 0, 7994 0, 7995 0, 7996 0, 7997 0, 7998 0, 7999 0, 8000 0, 8001 0, 8002 0, 8003 0, 8004 0, 8005 0, 8006 0, 8007 0, 8008 0, 8009 0, 8010 0, 8011 0, 8012 0, 8013 0, 8014 0, 8015 0, 8016 0, 8017 0, 8018 0, 8019 0, 8020 0, 8021 0, 8022 0, 8023 0, 8024 0, 8025 0, 8026 0, 8027 0, 8028 0, 8029 0, 8030 0, 8031 0, 8032 0, 8033 0, 8034 0, 8035 0, 8036 0, 8037 0, 8038 0, 8039 0, 8040 // COSE EC2 y. 8041 CBOR_NEG | 2, 8042 CBOR_BYTES | 24, 8043 // Length is 48. 8044 48, 8045 // Y-coordinate. This will be overwritten later. 8046 0, 8047 0, 8048 0, 8049 0, 8050 0, 8051 0, 8052 0, 8053 0, 8054 0, 8055 0, 8056 0, 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 0, 8090 0, 8091 0, 8092 0, 8093 0, 8094 ] 8095 .as_slice(), 8096 ); 8097 attestation_object[30..62].copy_from_slice(&Sha256::digest(RP_ID.as_ref().as_bytes())); 8098 let p384_key = P384Key::from_bytes( 8099 &[ 8100 158, 99, 156, 49, 190, 211, 85, 167, 28, 2, 80, 57, 31, 22, 17, 38, 85, 78, 232, 42, 8101 45, 199, 154, 243, 136, 251, 84, 34, 5, 120, 208, 91, 61, 248, 64, 144, 87, 1, 32, 86, 8102 220, 68, 182, 11, 105, 223, 75, 70, 8103 ] 8104 .into(), 8105 ) 8106 .unwrap() 8107 .verifying_key() 8108 .to_sec1_point(false); 8109 let x = p384_key.x().unwrap(); 8110 let y = p384_key.y().unwrap(); 8111 attestation_object[112..160].copy_from_slice(x); 8112 attestation_object[163..].copy_from_slice(y); 8113 assert!(matches!(opts.start_ceremony()?.0.verify( 8114 RP_ID, 8115 &Registration { 8116 response: AuthenticatorAttestation::new( 8117 client_data_json, 8118 attestation_object, 8119 AuthTransports::NONE, 8120 ), 8121 authenticator_attachment: AuthenticatorAttachment::None, 8122 client_extension_results: ClientExtensionsOutputs { 8123 cred_props: None, 8124 prf: None, 8125 }, 8126 }, 8127 &RegistrationVerificationOptions::<&str, &str>::default(), 8128 )?.static_state.credential_public_key, UncompressedPubKey::P384(k) if *k.x() == **x && *k.y() == **y)); 8129 Ok(()) 8130 } 8131 #[expect( 8132 clippy::panic_in_result_fn, 8133 clippy::unwrap_in_result, 8134 clippy::unwrap_used, 8135 reason = "OK in tests" 8136 )] 8137 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 8138 #[test] 8139 #[cfg(feature = "custom")] 8140 fn es384_auth() -> Result<(), AggErr> { 8141 let mut opts = DiscoverableCredentialRequestOptions::passkey(RP_ID); 8142 opts.public_key.challenge = Challenge(0); 8143 let client_data_json = br#"{"type":"webauthn.get","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec(); 8144 // We over-allocate by 32 bytes. See [`AuthenticatorAssertion::new`] for more information. 8145 let mut authenticator_data = Vec::with_capacity(69); 8146 authenticator_data.extend_from_slice( 8147 [ 8148 // rpIdHash. 8149 // This will be overwritten later. 8150 0, 8151 0, 8152 0, 8153 0, 8154 0, 8155 0, 8156 0, 8157 0, 8158 0, 8159 0, 8160 0, 8161 0, 8162 0, 8163 0, 8164 0, 8165 0, 8166 0, 8167 0, 8168 0, 8169 0, 8170 0, 8171 0, 8172 0, 8173 0, 8174 0, 8175 0, 8176 0, 8177 0, 8178 0, 8179 0, 8180 0, 8181 0, 8182 // flags. 8183 // UP and UV (right-to-left). 8184 0b0000_0101, 8185 // signCount. 8186 // 0 as 32-bit big-endian. 8187 0, 8188 0, 8189 0, 8190 0, 8191 ] 8192 .as_slice(), 8193 ); 8194 authenticator_data[..32].copy_from_slice(&Sha256::digest(RP_ID.as_ref().as_bytes())); 8195 authenticator_data.extend_from_slice(&Sha256::digest(client_data_json.as_slice())); 8196 let p384_key = P384Key::from_bytes( 8197 &[ 8198 158, 99, 156, 49, 190, 211, 85, 167, 28, 2, 80, 57, 31, 22, 17, 38, 85, 78, 232, 42, 8199 45, 199, 154, 243, 136, 251, 84, 34, 5, 120, 208, 91, 61, 248, 64, 144, 87, 1, 32, 86, 8200 220, 68, 182, 11, 105, 223, 75, 70, 8201 ] 8202 .into(), 8203 ) 8204 .unwrap(); 8205 let der_sig: P384DerSig = p384_key.sign(authenticator_data.as_slice()); 8206 let pub_key = p384_key.verifying_key().to_sec1_point(true); 8207 authenticator_data.truncate(37); 8208 assert!(!opts.start_ceremony()?.0.verify( 8209 RP_ID, 8210 &DiscoverableAuthentication { 8211 raw_id: CredentialId::try_from(vec![0; 16].into_boxed_slice())?, 8212 response: DiscoverableAuthenticatorAssertion::new( 8213 client_data_json, 8214 authenticator_data, 8215 der_sig.as_bytes().into(), 8216 UserHandle::from([0]), 8217 ), 8218 authenticator_attachment: AuthenticatorAttachment::None, 8219 }, 8220 &mut AuthenticatedCredential::new( 8221 CredentialId::try_from([0; 16].as_slice())?, 8222 &UserHandle::from([0]), 8223 StaticState { 8224 credential_public_key: CompressedPubKeyOwned::P384(CompressedP384PubKey::from(( 8225 (*pub_key.x().unwrap()).into(), 8226 pub_key.tag() == Tag::CompressedOddY 8227 )),), 8228 extensions: AuthenticatorExtensionOutputStaticState { 8229 cred_protect: CredentialProtectionPolicy::None, 8230 hmac_secret: None, 8231 }, 8232 client_extension_results: ClientExtensionsOutputsStaticState { prf: None } 8233 }, 8234 DynamicState { 8235 user_verified: true, 8236 backup: Backup::NotEligible, 8237 sign_count: 0, 8238 authenticator_attachment: AuthenticatorAttachment::None, 8239 }, 8240 )?, 8241 &AuthenticationVerificationOptions::<&str, &str>::default(), 8242 )?); 8243 Ok(()) 8244 } 8245 #[expect( 8246 clippy::panic_in_result_fn, 8247 clippy::unwrap_in_result, 8248 clippy::unwrap_used, 8249 reason = "OK in tests" 8250 )] 8251 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 8252 #[expect(clippy::too_many_lines, reason = "a lot to test")] 8253 #[expect(clippy::many_single_char_names, reason = "fine")] 8254 #[test] 8255 #[cfg(feature = "custom")] 8256 fn rs256_reg() -> Result<(), AggErr> { 8257 let id = UserHandle::from([0]); 8258 let mut opts = CredentialCreationOptions::passkey( 8259 RP_ID, 8260 PublicKeyCredentialUserEntity { 8261 name: "foo", 8262 id: &id, 8263 display_name: "", 8264 }, 8265 Vec::new(), 8266 ); 8267 opts.public_key.challenge = Challenge(0); 8268 let client_data_json = br#"{"type":"webauthn.create","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec(); 8269 // We over-allocate by 32 bytes. See [`AuthenticatorAttestation::new`] for more information. 8270 let mut attestation_object = Vec::with_capacity(406); 8271 attestation_object.extend_from_slice( 8272 [ 8273 CBOR_MAP | 3, 8274 CBOR_TEXT | 3, 8275 b'f', 8276 b'm', 8277 b't', 8278 CBOR_TEXT | 4, 8279 b'n', 8280 b'o', 8281 b'n', 8282 b'e', 8283 CBOR_TEXT | 7, 8284 b'a', 8285 b't', 8286 b't', 8287 b'S', 8288 b't', 8289 b'm', 8290 b't', 8291 CBOR_MAP, 8292 CBOR_TEXT | 8, 8293 b'a', 8294 b'u', 8295 b't', 8296 b'h', 8297 b'D', 8298 b'a', 8299 b't', 8300 b'a', 8301 CBOR_BYTES | 25, 8302 // Length is 343 as 16-bit big-endian. 8303 1, 8304 87, 8305 // RP ID HASH. 8306 // This will be overwritten later. 8307 0, 8308 0, 8309 0, 8310 0, 8311 0, 8312 0, 8313 0, 8314 0, 8315 0, 8316 0, 8317 0, 8318 0, 8319 0, 8320 0, 8321 0, 8322 0, 8323 0, 8324 0, 8325 0, 8326 0, 8327 0, 8328 0, 8329 0, 8330 0, 8331 0, 8332 0, 8333 0, 8334 0, 8335 0, 8336 0, 8337 0, 8338 0, 8339 // FLAGS. 8340 // UP, UV, and AT (right-to-left). 8341 0b0100_0101, 8342 // COUNTER. 8343 // 0 as 32-bit big-endian. 8344 0, 8345 0, 8346 0, 8347 0, 8348 // AAGUID. 8349 0, 8350 0, 8351 0, 8352 0, 8353 0, 8354 0, 8355 0, 8356 0, 8357 0, 8358 0, 8359 0, 8360 0, 8361 0, 8362 0, 8363 0, 8364 0, 8365 // L. 8366 // CREDENTIAL ID length is 16 as 16-bit big endian. 8367 0, 8368 16, 8369 // CREDENTIAL ID. 8370 0, 8371 0, 8372 0, 8373 0, 8374 0, 8375 0, 8376 0, 8377 0, 8378 0, 8379 0, 8380 0, 8381 0, 8382 0, 8383 0, 8384 0, 8385 0, 8386 CBOR_MAP | 4, 8387 // COSE kty. 8388 CBOR_UINT | 1, 8389 // COSE RSA. 8390 CBOR_UINT | 3, 8391 // COSE alg. 8392 CBOR_UINT | 3, 8393 CBOR_NEG | 25, 8394 // COSE RS256. 8395 1, 8396 0, 8397 // COSE n. 8398 CBOR_NEG, 8399 CBOR_BYTES | 25, 8400 // Length is 256 as 16-bit big-endian. 8401 1, 8402 0, 8403 // N. This will be overwritten later. 8404 0, 8405 0, 8406 0, 8407 0, 8408 0, 8409 0, 8410 0, 8411 0, 8412 0, 8413 0, 8414 0, 8415 0, 8416 0, 8417 0, 8418 0, 8419 0, 8420 0, 8421 0, 8422 0, 8423 0, 8424 0, 8425 0, 8426 0, 8427 0, 8428 0, 8429 0, 8430 0, 8431 0, 8432 0, 8433 0, 8434 0, 8435 0, 8436 0, 8437 0, 8438 0, 8439 0, 8440 0, 8441 0, 8442 0, 8443 0, 8444 0, 8445 0, 8446 0, 8447 0, 8448 0, 8449 0, 8450 0, 8451 0, 8452 0, 8453 0, 8454 0, 8455 0, 8456 0, 8457 0, 8458 0, 8459 0, 8460 0, 8461 0, 8462 0, 8463 0, 8464 0, 8465 0, 8466 0, 8467 0, 8468 0, 8469 0, 8470 0, 8471 0, 8472 0, 8473 0, 8474 0, 8475 0, 8476 0, 8477 0, 8478 0, 8479 0, 8480 0, 8481 0, 8482 0, 8483 0, 8484 0, 8485 0, 8486 0, 8487 0, 8488 0, 8489 0, 8490 0, 8491 0, 8492 0, 8493 0, 8494 0, 8495 0, 8496 0, 8497 0, 8498 0, 8499 0, 8500 0, 8501 0, 8502 0, 8503 0, 8504 0, 8505 0, 8506 0, 8507 0, 8508 0, 8509 0, 8510 0, 8511 0, 8512 0, 8513 0, 8514 0, 8515 0, 8516 0, 8517 0, 8518 0, 8519 0, 8520 0, 8521 0, 8522 0, 8523 0, 8524 0, 8525 0, 8526 0, 8527 0, 8528 0, 8529 0, 8530 0, 8531 0, 8532 0, 8533 0, 8534 0, 8535 0, 8536 0, 8537 0, 8538 0, 8539 0, 8540 0, 8541 0, 8542 0, 8543 0, 8544 0, 8545 0, 8546 0, 8547 0, 8548 0, 8549 0, 8550 0, 8551 0, 8552 0, 8553 0, 8554 0, 8555 0, 8556 0, 8557 0, 8558 0, 8559 0, 8560 0, 8561 0, 8562 0, 8563 0, 8564 0, 8565 0, 8566 0, 8567 0, 8568 0, 8569 0, 8570 0, 8571 0, 8572 0, 8573 0, 8574 0, 8575 0, 8576 0, 8577 0, 8578 0, 8579 0, 8580 0, 8581 0, 8582 0, 8583 0, 8584 0, 8585 0, 8586 0, 8587 0, 8588 0, 8589 0, 8590 0, 8591 0, 8592 0, 8593 0, 8594 0, 8595 0, 8596 0, 8597 0, 8598 0, 8599 0, 8600 0, 8601 0, 8602 0, 8603 0, 8604 0, 8605 0, 8606 0, 8607 0, 8608 0, 8609 0, 8610 0, 8611 0, 8612 0, 8613 0, 8614 0, 8615 0, 8616 0, 8617 0, 8618 0, 8619 0, 8620 0, 8621 0, 8622 0, 8623 0, 8624 0, 8625 0, 8626 0, 8627 0, 8628 0, 8629 0, 8630 0, 8631 0, 8632 0, 8633 0, 8634 0, 8635 0, 8636 0, 8637 0, 8638 0, 8639 0, 8640 0, 8641 0, 8642 0, 8643 0, 8644 0, 8645 0, 8646 0, 8647 0, 8648 0, 8649 0, 8650 0, 8651 0, 8652 0, 8653 0, 8654 0, 8655 0, 8656 0, 8657 0, 8658 0, 8659 0, 8660 // COSE e. 8661 CBOR_NEG | 1, 8662 CBOR_BYTES | 3, 8663 // 65537 as 24-bit big-endian. 8664 1, 8665 0, 8666 1, 8667 ] 8668 .as_slice(), 8669 ); 8670 attestation_object[31..63].copy_from_slice(&Sha256::digest(RP_ID.as_ref().as_bytes())); 8671 let n = [ 8672 111, 183, 124, 133, 38, 167, 70, 148, 44, 50, 30, 60, 121, 14, 38, 37, 96, 114, 107, 195, 8673 248, 64, 79, 36, 237, 140, 43, 27, 94, 74, 102, 152, 135, 102, 184, 150, 186, 206, 185, 19, 8674 165, 209, 48, 98, 98, 9, 3, 205, 208, 82, 250, 105, 132, 201, 73, 62, 60, 165, 100, 128, 8675 153, 9, 41, 118, 66, 95, 236, 214, 73, 135, 197, 68, 184, 10, 27, 116, 204, 145, 50, 174, 8676 58, 42, 183, 181, 119, 232, 126, 252, 217, 96, 162, 190, 103, 122, 64, 87, 145, 45, 32, 8677 207, 17, 239, 223, 3, 35, 14, 112, 119, 124, 141, 123, 208, 239, 105, 81, 217, 151, 162, 8678 190, 17, 88, 182, 176, 158, 81, 200, 42, 166, 133, 48, 23, 236, 55, 117, 248, 233, 151, 8679 203, 122, 155, 231, 46, 177, 20, 20, 151, 64, 222, 239, 226, 7, 21, 254, 81, 202, 64, 232, 8680 161, 235, 22, 51, 246, 207, 213, 0, 229, 138, 46, 222, 205, 157, 108, 139, 253, 230, 80, 8681 50, 2, 122, 212, 163, 100, 180, 114, 12, 113, 52, 56, 99, 188, 42, 198, 212, 23, 182, 222, 8682 56, 221, 200, 79, 96, 239, 221, 135, 10, 17, 106, 183, 56, 104, 68, 94, 198, 196, 35, 200, 8683 83, 204, 26, 185, 204, 212, 31, 183, 19, 111, 233, 13, 72, 93, 53, 65, 111, 59, 242, 122, 8684 160, 244, 162, 126, 38, 235, 156, 47, 88, 39, 132, 153, 79, 0, 133, 78, 7, 218, 165, 241, 8685 ]; 8686 let e = 0x0001_0001; 8687 let d = [ 8688 145, 79, 21, 97, 233, 3, 192, 194, 177, 68, 181, 80, 120, 197, 23, 44, 185, 74, 144, 0, 8689 132, 149, 139, 11, 16, 224, 4, 112, 236, 94, 238, 97, 121, 124, 213, 145, 24, 253, 168, 35, 8690 190, 205, 132, 115, 33, 201, 38, 253, 246, 180, 66, 155, 165, 46, 3, 254, 68, 108, 154, 8691 247, 246, 45, 187, 0, 204, 96, 185, 157, 249, 174, 158, 38, 62, 244, 183, 76, 102, 6, 219, 8692 92, 212, 138, 59, 147, 163, 219, 111, 39, 105, 21, 236, 196, 38, 255, 114, 247, 82, 104, 8693 113, 204, 29, 152, 209, 219, 48, 239, 74, 129, 19, 247, 33, 239, 119, 166, 216, 152, 94, 8694 138, 238, 164, 242, 129, 50, 150, 57, 20, 53, 224, 56, 241, 138, 97, 111, 215, 107, 212, 8695 195, 146, 108, 143, 0, 229, 181, 171, 73, 152, 105, 146, 25, 243, 242, 140, 252, 248, 162, 8696 247, 63, 168, 180, 20, 153, 120, 10, 248, 211, 1, 71, 127, 212, 249, 237, 203, 202, 48, 26, 8697 216, 226, 228, 186, 13, 204, 70, 255, 240, 89, 255, 59, 83, 31, 253, 55, 43, 158, 90, 248, 8698 83, 32, 159, 105, 57, 134, 34, 96, 18, 255, 245, 153, 162, 60, 91, 99, 220, 51, 44, 85, 8699 114, 67, 125, 202, 65, 217, 245, 40, 8, 81, 165, 142, 24, 245, 127, 122, 247, 152, 212, 75, 8700 45, 59, 90, 184, 234, 31, 147, 36, 8, 212, 45, 50, 23, 3, 25, 253, 87, 227, 79, 119, 161, 8701 ]; 8702 let p = BoxedUint::from_le_slice_vartime( 8703 [ 8704 215, 166, 5, 21, 11, 179, 41, 77, 198, 92, 165, 48, 77, 162, 42, 41, 206, 141, 60, 69, 8705 47, 164, 19, 92, 46, 72, 100, 238, 100, 53, 214, 197, 163, 185, 6, 140, 229, 250, 195, 8706 77, 8, 12, 5, 236, 178, 173, 86, 201, 43, 213, 165, 51, 108, 101, 161, 99, 76, 240, 14, 8707 234, 76, 197, 137, 53, 198, 168, 135, 205, 212, 198, 120, 29, 16, 82, 98, 233, 236, 8708 177, 12, 171, 141, 100, 107, 146, 33, 176, 125, 202, 172, 79, 147, 179, 30, 62, 247, 8709 206, 169, 19, 168, 114, 26, 73, 108, 178, 105, 84, 89, 191, 168, 253, 228, 214, 54, 16, 8710 212, 199, 111, 72, 3, 41, 247, 227, 165, 244, 32, 188, 24, 247, 8711 ] 8712 .as_slice(), 8713 ); 8714 let p_2 = BoxedUint::from_le_slice_vartime( 8715 [ 8716 41, 25, 198, 240, 134, 206, 121, 57, 11, 5, 134, 192, 212, 77, 229, 197, 14, 78, 85, 8717 212, 190, 114, 179, 188, 21, 171, 174, 12, 104, 74, 15, 164, 136, 173, 62, 177, 141, 8718 213, 93, 102, 147, 83, 59, 124, 146, 59, 175, 213, 55, 27, 25, 248, 154, 29, 39, 85, 8719 50, 235, 134, 60, 203, 106, 186, 195, 190, 185, 71, 169, 142, 236, 92, 11, 250, 187, 8720 198, 8, 201, 184, 120, 178, 227, 87, 63, 243, 89, 227, 234, 184, 28, 252, 112, 211, 8721 193, 69, 23, 92, 5, 72, 93, 53, 69, 159, 73, 160, 105, 244, 249, 94, 214, 173, 9, 236, 8722 4, 255, 129, 11, 224, 140, 252, 168, 57, 143, 176, 241, 60, 219, 90, 250, 8723 ] 8724 .as_slice(), 8725 ); 8726 let rsa_key = RsaKey::<Sha256>::new( 8727 RsaPrivateKey::from_components( 8728 BoxedUint::from_le_slice_vartime(n.as_slice()), 8729 e.into(), 8730 BoxedUint::from_le_slice_vartime(d.as_slice()), 8731 vec![p, p_2], 8732 ) 8733 .unwrap(), 8734 ) 8735 .verifying_key(); 8736 let n_other = rsa_key.as_ref().n().to_be_bytes(); 8737 attestation_object[113..369].copy_from_slice(&n_other); 8738 assert!(matches!(opts.start_ceremony()?.0.verify( 8739 RP_ID, 8740 &Registration { 8741 response: AuthenticatorAttestation::new( 8742 client_data_json, 8743 attestation_object, 8744 AuthTransports::NONE, 8745 ), 8746 authenticator_attachment: AuthenticatorAttachment::None, 8747 client_extension_results: ClientExtensionsOutputs { 8748 cred_props: None, 8749 prf: None, 8750 }, 8751 }, 8752 &RegistrationVerificationOptions::<&str, &str>::default(), 8753 )?.static_state.credential_public_key, UncompressedPubKey::Rsa(k) if **k.n() == *n_other && k.e() == e)); 8754 Ok(()) 8755 } 8756 #[expect( 8757 clippy::panic_in_result_fn, 8758 clippy::unwrap_in_result, 8759 clippy::unwrap_used, 8760 reason = "OK in tests" 8761 )] 8762 #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] 8763 #[expect(clippy::too_many_lines, reason = "a lot to test")] 8764 #[test] 8765 #[cfg(feature = "custom")] 8766 fn rs256_auth() -> Result<(), AggErr> { 8767 let mut opts = DiscoverableCredentialRequestOptions::passkey(RP_ID); 8768 opts.public_key.challenge = Challenge(0); 8769 let client_data_json = br#"{"type":"webauthn.get","challenge":"AAAAAAAAAAAAAAAAAAAAAA","origin":"https://example.com","crossOrigin":false}"#.to_vec(); 8770 // We over-allocate by 32 bytes. See [`AuthenticatorAssertion::new`] for more information. 8771 let mut authenticator_data = Vec::with_capacity(69); 8772 authenticator_data.extend_from_slice( 8773 [ 8774 // rpIdHash. 8775 // This will be overwritten later. 8776 0, 8777 0, 8778 0, 8779 0, 8780 0, 8781 0, 8782 0, 8783 0, 8784 0, 8785 0, 8786 0, 8787 0, 8788 0, 8789 0, 8790 0, 8791 0, 8792 0, 8793 0, 8794 0, 8795 0, 8796 0, 8797 0, 8798 0, 8799 0, 8800 0, 8801 0, 8802 0, 8803 0, 8804 0, 8805 0, 8806 0, 8807 0, 8808 // flags. 8809 // UP and UV (right-to-left). 8810 0b0000_0101, 8811 // signCount. 8812 // 0 as 32-bit big-endian. 8813 0, 8814 0, 8815 0, 8816 0, 8817 ] 8818 .as_slice(), 8819 ); 8820 authenticator_data[..32].copy_from_slice(&Sha256::digest(RP_ID.as_ref().as_bytes())); 8821 authenticator_data.extend_from_slice(&Sha256::digest(client_data_json.as_slice())); 8822 let n = [ 8823 111, 183, 124, 133, 38, 167, 70, 148, 44, 50, 30, 60, 121, 14, 38, 37, 96, 114, 107, 195, 8824 248, 64, 79, 36, 237, 140, 43, 27, 94, 74, 102, 152, 135, 102, 184, 150, 186, 206, 185, 19, 8825 165, 209, 48, 98, 98, 9, 3, 205, 208, 82, 250, 105, 132, 201, 73, 62, 60, 165, 100, 128, 8826 153, 9, 41, 118, 66, 95, 236, 214, 73, 135, 197, 68, 184, 10, 27, 116, 204, 145, 50, 174, 8827 58, 42, 183, 181, 119, 232, 126, 252, 217, 96, 162, 190, 103, 122, 64, 87, 145, 45, 32, 8828 207, 17, 239, 223, 3, 35, 14, 112, 119, 124, 141, 123, 208, 239, 105, 81, 217, 151, 162, 8829 190, 17, 88, 182, 176, 158, 81, 200, 42, 166, 133, 48, 23, 236, 55, 117, 248, 233, 151, 8830 203, 122, 155, 231, 46, 177, 20, 20, 151, 64, 222, 239, 226, 7, 21, 254, 81, 202, 64, 232, 8831 161, 235, 22, 51, 246, 207, 213, 0, 229, 138, 46, 222, 205, 157, 108, 139, 253, 230, 80, 8832 50, 2, 122, 212, 163, 100, 180, 114, 12, 113, 52, 56, 99, 188, 42, 198, 212, 23, 182, 222, 8833 56, 221, 200, 79, 96, 239, 221, 135, 10, 17, 106, 183, 56, 104, 68, 94, 198, 196, 35, 200, 8834 83, 204, 26, 185, 204, 212, 31, 183, 19, 111, 233, 13, 72, 93, 53, 65, 111, 59, 242, 122, 8835 160, 244, 162, 126, 38, 235, 156, 47, 88, 39, 132, 153, 79, 0, 133, 78, 7, 218, 165, 241, 8836 ]; 8837 let e = 0x0001_0001; 8838 let d = [ 8839 145, 79, 21, 97, 233, 3, 192, 194, 177, 68, 181, 80, 120, 197, 23, 44, 185, 74, 144, 0, 8840 132, 149, 139, 11, 16, 224, 4, 112, 236, 94, 238, 97, 121, 124, 213, 145, 24, 253, 168, 35, 8841 190, 205, 132, 115, 33, 201, 38, 253, 246, 180, 66, 155, 165, 46, 3, 254, 68, 108, 154, 8842 247, 246, 45, 187, 0, 204, 96, 185, 157, 249, 174, 158, 38, 62, 244, 183, 76, 102, 6, 219, 8843 92, 212, 138, 59, 147, 163, 219, 111, 39, 105, 21, 236, 196, 38, 255, 114, 247, 82, 104, 8844 113, 204, 29, 152, 209, 219, 48, 239, 74, 129, 19, 247, 33, 239, 119, 166, 216, 152, 94, 8845 138, 238, 164, 242, 129, 50, 150, 57, 20, 53, 224, 56, 241, 138, 97, 111, 215, 107, 212, 8846 195, 146, 108, 143, 0, 229, 181, 171, 73, 152, 105, 146, 25, 243, 242, 140, 252, 248, 162, 8847 247, 63, 168, 180, 20, 153, 120, 10, 248, 211, 1, 71, 127, 212, 249, 237, 203, 202, 48, 26, 8848 216, 226, 228, 186, 13, 204, 70, 255, 240, 89, 255, 59, 83, 31, 253, 55, 43, 158, 90, 248, 8849 83, 32, 159, 105, 57, 134, 34, 96, 18, 255, 245, 153, 162, 60, 91, 99, 220, 51, 44, 85, 8850 114, 67, 125, 202, 65, 217, 245, 40, 8, 81, 165, 142, 24, 245, 127, 122, 247, 152, 212, 75, 8851 45, 59, 90, 184, 234, 31, 147, 36, 8, 212, 45, 50, 23, 3, 25, 253, 87, 227, 79, 119, 161, 8852 ]; 8853 let p = BoxedUint::from_le_slice_vartime( 8854 [ 8855 215, 166, 5, 21, 11, 179, 41, 77, 198, 92, 165, 48, 77, 162, 42, 41, 206, 141, 60, 69, 8856 47, 164, 19, 92, 46, 72, 100, 238, 100, 53, 214, 197, 163, 185, 6, 140, 229, 250, 195, 8857 77, 8, 12, 5, 236, 178, 173, 86, 201, 43, 213, 165, 51, 108, 101, 161, 99, 76, 240, 14, 8858 234, 76, 197, 137, 53, 198, 168, 135, 205, 212, 198, 120, 29, 16, 82, 98, 233, 236, 8859 177, 12, 171, 141, 100, 107, 146, 33, 176, 125, 202, 172, 79, 147, 179, 30, 62, 247, 8860 206, 169, 19, 168, 114, 26, 73, 108, 178, 105, 84, 89, 191, 168, 253, 228, 214, 54, 16, 8861 212, 199, 111, 72, 3, 41, 247, 227, 165, 244, 32, 188, 24, 247, 8862 ] 8863 .as_slice(), 8864 ); 8865 let p_2 = BoxedUint::from_le_slice_vartime( 8866 [ 8867 41, 25, 198, 240, 134, 206, 121, 57, 11, 5, 134, 192, 212, 77, 229, 197, 14, 78, 85, 8868 212, 190, 114, 179, 188, 21, 171, 174, 12, 104, 74, 15, 164, 136, 173, 62, 177, 141, 8869 213, 93, 102, 147, 83, 59, 124, 146, 59, 175, 213, 55, 27, 25, 248, 154, 29, 39, 85, 8870 50, 235, 134, 60, 203, 106, 186, 195, 190, 185, 71, 169, 142, 236, 92, 11, 250, 187, 8871 198, 8, 201, 184, 120, 178, 227, 87, 63, 243, 89, 227, 234, 184, 28, 252, 112, 211, 8872 193, 69, 23, 92, 5, 72, 93, 53, 69, 159, 73, 160, 105, 244, 249, 94, 214, 173, 9, 236, 8873 4, 255, 129, 11, 224, 140, 252, 168, 57, 143, 176, 241, 60, 219, 90, 250, 8874 ] 8875 .as_slice(), 8876 ); 8877 let rsa_key = RsaKey::<Sha256>::new( 8878 RsaPrivateKey::from_components( 8879 BoxedUint::from_le_slice_vartime(n.as_slice()), 8880 e.into(), 8881 BoxedUint::from_le_slice_vartime(d.as_slice()), 8882 vec![p, p_2], 8883 ) 8884 .unwrap(), 8885 ); 8886 let rsa_pub = rsa_key.verifying_key(); 8887 let sig = rsa_key.sign(authenticator_data.as_slice()).to_vec(); 8888 authenticator_data.truncate(37); 8889 assert!(!opts.start_ceremony()?.0.verify( 8890 RP_ID, 8891 &DiscoverableAuthentication { 8892 raw_id: CredentialId::try_from(vec![0; 16].into_boxed_slice())?, 8893 response: DiscoverableAuthenticatorAssertion::new( 8894 client_data_json, 8895 authenticator_data, 8896 sig, 8897 UserHandle::from([0]), 8898 ), 8899 authenticator_attachment: AuthenticatorAttachment::None, 8900 }, 8901 &mut AuthenticatedCredential::new( 8902 CredentialId::try_from([0; 16].as_slice())?, 8903 &UserHandle::from([0]), 8904 StaticState { 8905 credential_public_key: CompressedPubKeyOwned::Rsa( 8906 RsaPubKey::try_from((rsa_pub.as_ref().n().to_be_bytes(), e)).unwrap(), 8907 ), 8908 extensions: AuthenticatorExtensionOutputStaticState { 8909 cred_protect: CredentialProtectionPolicy::None, 8910 hmac_secret: None, 8911 }, 8912 client_extension_results: ClientExtensionsOutputsStaticState { prf: None } 8913 }, 8914 DynamicState { 8915 user_verified: true, 8916 backup: Backup::NotEligible, 8917 sign_count: 0, 8918 authenticator_attachment: AuthenticatorAttachment::None, 8919 }, 8920 )?, 8921 &AuthenticationVerificationOptions::<&str, &str>::default(), 8922 )?); 8923 Ok(()) 8924 } 8925 #[expect( 8926 clippy::cognitive_complexity, 8927 clippy::too_many_lines, 8928 reason = "a lot to test" 8929 )] 8930 #[test] 8931 fn hints() { 8932 assert_eq!(Hints::EMPTY.0, [None; 3]); 8933 assert!( 8934 Hints::EMPTY.first().is_none() 8935 && Hints::EMPTY.second().is_none() 8936 && Hints::EMPTY.third().is_none() 8937 ); 8938 assert_eq!(Hints::EMPTY.count(), 0); 8939 assert!(Hints::EMPTY.is_empty()); 8940 assert!( 8941 !(Hints::EMPTY.contains_cross_platform_hints() || Hints::EMPTY.contains_platform_hints()) 8942 ); 8943 assert!(!Hints::EMPTY.contains(PublicKeyCredentialHint::SecurityKey)); 8944 assert!(!Hints::EMPTY.contains(PublicKeyCredentialHint::ClientDevice)); 8945 assert!(!Hints::EMPTY.contains(PublicKeyCredentialHint::Hybrid)); 8946 let mut hints = Hints::EMPTY.add(PublicKeyCredentialHint::SecurityKey); 8947 assert_eq!( 8948 hints.0, 8949 [Some(PublicKeyCredentialHint::SecurityKey), None, None] 8950 ); 8951 assert_eq!(hints.0, hints.add(PublicKeyCredentialHint::SecurityKey).0); 8952 assert!( 8953 hints.first() == Some(PublicKeyCredentialHint::SecurityKey) 8954 && hints.second().is_none() 8955 && hints.third().is_none() 8956 ); 8957 assert_eq!(hints.count(), 1); 8958 assert!(!hints.is_empty()); 8959 assert!(hints.contains_cross_platform_hints() && !hints.contains_platform_hints()); 8960 assert!(hints.contains(PublicKeyCredentialHint::SecurityKey)); 8961 assert!(!hints.contains(PublicKeyCredentialHint::ClientDevice)); 8962 assert!(!hints.contains(PublicKeyCredentialHint::Hybrid)); 8963 hints = Hints::EMPTY.add(PublicKeyCredentialHint::Hybrid); 8964 assert_eq!(hints.0, [Some(PublicKeyCredentialHint::Hybrid), None, None]); 8965 assert_eq!(hints.0, hints.add(PublicKeyCredentialHint::Hybrid).0); 8966 assert!( 8967 hints.first() == Some(PublicKeyCredentialHint::Hybrid) 8968 && hints.second().is_none() 8969 && hints.third().is_none() 8970 ); 8971 assert_eq!(hints.count(), 1); 8972 assert!(!hints.is_empty()); 8973 assert!(hints.contains_cross_platform_hints() && !hints.contains_platform_hints()); 8974 assert!(hints.contains(PublicKeyCredentialHint::Hybrid)); 8975 assert!(!hints.contains(PublicKeyCredentialHint::SecurityKey)); 8976 assert!(!hints.contains(PublicKeyCredentialHint::ClientDevice)); 8977 hints = Hints::EMPTY.add(PublicKeyCredentialHint::ClientDevice); 8978 assert_eq!( 8979 hints.0, 8980 [Some(PublicKeyCredentialHint::ClientDevice), None, None] 8981 ); 8982 assert_eq!(hints.0, hints.add(PublicKeyCredentialHint::ClientDevice).0); 8983 assert!( 8984 hints.first() == Some(PublicKeyCredentialHint::ClientDevice) 8985 && hints.second().is_none() 8986 && hints.third().is_none() 8987 ); 8988 assert_eq!(hints.count(), 1); 8989 assert!(!hints.is_empty()); 8990 assert!(!hints.contains_cross_platform_hints() && hints.contains_platform_hints()); 8991 assert!(hints.contains(PublicKeyCredentialHint::ClientDevice)); 8992 assert!(!hints.contains(PublicKeyCredentialHint::SecurityKey)); 8993 assert!(!hints.contains(PublicKeyCredentialHint::Hybrid)); 8994 hints = hints.add(PublicKeyCredentialHint::Hybrid); 8995 assert_eq!(hints.0, hints.add(PublicKeyCredentialHint::ClientDevice).0); 8996 assert_eq!(hints.0, hints.add(PublicKeyCredentialHint::Hybrid).0); 8997 assert_eq!( 8998 hints.0, 8999 [ 9000 Some(PublicKeyCredentialHint::ClientDevice), 9001 Some(PublicKeyCredentialHint::Hybrid), 9002 None 9003 ] 9004 ); 9005 assert!( 9006 hints.first() == Some(PublicKeyCredentialHint::ClientDevice) 9007 && hints.second() == Some(PublicKeyCredentialHint::Hybrid) 9008 && hints.third().is_none() 9009 ); 9010 assert_eq!(hints.count(), 2); 9011 assert!(!hints.is_empty()); 9012 assert!(hints.contains_cross_platform_hints() && hints.contains_platform_hints()); 9013 assert!(hints.contains(PublicKeyCredentialHint::ClientDevice)); 9014 assert!(!hints.contains(PublicKeyCredentialHint::SecurityKey)); 9015 assert!(hints.contains(PublicKeyCredentialHint::Hybrid)); 9016 hints = hints.add(PublicKeyCredentialHint::SecurityKey); 9017 assert_eq!( 9018 hints.0, 9019 [ 9020 Some(PublicKeyCredentialHint::ClientDevice), 9021 Some(PublicKeyCredentialHint::Hybrid), 9022 Some(PublicKeyCredentialHint::SecurityKey), 9023 ] 9024 ); 9025 assert!( 9026 hints.first() == Some(PublicKeyCredentialHint::ClientDevice) 9027 && hints.second() == Some(PublicKeyCredentialHint::Hybrid) 9028 && hints.third() == Some(PublicKeyCredentialHint::SecurityKey) 9029 ); 9030 assert_eq!(hints.count(), 3); 9031 assert!(!hints.is_empty()); 9032 assert!(hints.contains_cross_platform_hints() && hints.contains_platform_hints()); 9033 assert!(hints.contains(PublicKeyCredentialHint::ClientDevice)); 9034 assert!(hints.contains(PublicKeyCredentialHint::SecurityKey)); 9035 assert!(hints.contains(PublicKeyCredentialHint::Hybrid)); 9036 assert_eq!(hints.0, hints.add(PublicKeyCredentialHint::SecurityKey).0); 9037 assert_eq!(hints.0, hints.add(PublicKeyCredentialHint::Hybrid).0); 9038 assert_eq!(hints.0, hints.add(PublicKeyCredentialHint::ClientDevice).0); 9039 hints = Hints::EMPTY 9040 .add(PublicKeyCredentialHint::Hybrid) 9041 .add(PublicKeyCredentialHint::SecurityKey); 9042 assert_eq!( 9043 hints.0, 9044 [ 9045 Some(PublicKeyCredentialHint::Hybrid), 9046 Some(PublicKeyCredentialHint::SecurityKey), 9047 None, 9048 ] 9049 ); 9050 assert!( 9051 hints.first() == Some(PublicKeyCredentialHint::Hybrid) 9052 && hints.second() == Some(PublicKeyCredentialHint::SecurityKey) 9053 && hints.third().is_none() 9054 ); 9055 assert_eq!(hints.count(), 2); 9056 assert!(!hints.is_empty()); 9057 assert!(hints.contains_cross_platform_hints() && !hints.contains_platform_hints()); 9058 assert!(!hints.contains(PublicKeyCredentialHint::ClientDevice)); 9059 assert!(hints.contains(PublicKeyCredentialHint::SecurityKey)); 9060 assert!(hints.contains(PublicKeyCredentialHint::Hybrid)); 9061 assert_eq!(hints.0, hints.add(PublicKeyCredentialHint::SecurityKey).0); 9062 assert_eq!(hints.0, hints.add(PublicKeyCredentialHint::Hybrid).0); 9063 }