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