ser_relaxed.rs (84669B)
1 #[cfg(doc)] 2 use super::super::{Challenge, CredentialId}; 3 use super::{ 4 super::{ 5 super::request::register::{USER_HANDLE_MAX_LEN, UserHandle}, 6 auth::ser::{ 7 AUTH_ASSERT_FIELDS, AuthData, AuthenticatorAssertionVisitor, ClientExtensionsOutputs, 8 ClientExtensionsOutputsVisitor, EXT_FIELDS, 9 }, 10 ser::{ 11 AuthenticationExtensionsPrfOutputsHelper, Base64DecodedVal, ClientExtensions, 12 PublicKeyCredential, Type, 13 }, 14 ser_relaxed::AuthenticationExtensionsPrfValuesRelaxed, 15 }, 16 Authentication, AuthenticatorAssertion, AuthenticatorAttachment, 17 }; 18 use core::{ 19 fmt::{self, Formatter}, 20 marker::PhantomData, 21 }; 22 use serde::de::{Deserialize, Deserializer, Error, MapAccess, Visitor}; 23 /// `newtype` around `ClientExtensionsOutputs` with a "relaxed" [`Self::deserialize`] implementation. 24 struct ClientExtensionsOutputsRelaxed(pub ClientExtensionsOutputs); 25 impl ClientExtensions for ClientExtensionsOutputsRelaxed { 26 fn empty() -> Self { 27 Self(ClientExtensionsOutputs::empty()) 28 } 29 } 30 impl<'de> Deserialize<'de> for ClientExtensionsOutputsRelaxed { 31 /// Same as [`ClientExtensionsOutputs::deserialize`] except unknown keys are ignored. 32 /// 33 /// Note that duplicate keys are still forbidden. 34 #[inline] 35 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 36 where 37 D: Deserializer<'de>, 38 { 39 deserializer 40 .deserialize_struct( 41 "ClientExtensionsOutputsRelaxed", 42 EXT_FIELDS, 43 ClientExtensionsOutputsVisitor::< 44 true, 45 AuthenticationExtensionsPrfOutputsHelper< 46 true, 47 false, 48 AuthenticationExtensionsPrfValuesRelaxed, 49 >, 50 >(PhantomData), 51 ) 52 .map(Self) 53 } 54 } 55 /// `newtype` around `AuthenticatorAssertion` with a "relaxed" [`Self::deserialize`] implementation. 56 #[derive(Debug)] 57 pub struct AuthenticatorAssertionRelaxed<const USER_LEN: usize, const DISCOVERABLE: bool>( 58 pub AuthenticatorAssertion<USER_LEN, DISCOVERABLE>, 59 ); 60 impl<'de, const USER_LEN: usize, const DISCOVERABLE: bool> Deserialize<'de> 61 for AuthenticatorAssertionRelaxed<USER_LEN, DISCOVERABLE> 62 where 63 UserHandle<USER_LEN>: Deserialize<'de>, 64 { 65 /// Same as [`AuthenticatorAssertion::deserialize`] except unknown keys are ignored. 66 /// 67 /// Note that duplicate keys are still forbidden. 68 #[inline] 69 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 70 where 71 D: Deserializer<'de>, 72 { 73 deserializer 74 .deserialize_struct( 75 "AuthenticatorAssertionRelaxed", 76 AUTH_ASSERT_FIELDS, 77 AuthenticatorAssertionVisitor::<true, USER_LEN, DISCOVERABLE>, 78 ) 79 .map(Self) 80 } 81 } 82 /// `newtype` around `Authentication` with a "relaxed" [`Self::deserialize`] implementation. 83 #[derive(Debug)] 84 pub struct AuthenticationRelaxed<const USER_LEN: usize, const DISCOVERABLE: bool>( 85 pub Authentication<USER_LEN, DISCOVERABLE>, 86 ); 87 impl<'de, const USER_LEN: usize, const DISCOVERABLE: bool> Deserialize<'de> 88 for AuthenticationRelaxed<USER_LEN, DISCOVERABLE> 89 where 90 UserHandle<USER_LEN>: Deserialize<'de>, 91 { 92 /// Same as [`Authentication::deserialize`] except unknown keys are ignored; 93 /// [`response`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-response) is deserialized 94 /// via [`AuthenticatorAssertionRelaxed::deserialize`]; 95 /// [`clientExtensionResults`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-clientextensionresults) 96 /// is deserialized such unknown keys are ignored but duplicate keys are forbidden, 97 /// [`prf`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsclientoutputs-prf) is `null` or an 98 /// [`AuthenticationExtensionsPRFOutputs`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfoutputs) 99 /// such that unknown keys are allowed but duplicate keys are forbidden, 100 /// [`enabled`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-enabled) 101 /// is forbidden (including being assigned `null`), 102 /// [`results`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-results) must not exist, 103 /// be `null`, or be an 104 /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues) 105 /// where unknown keys are ignored, duplicate keys are forbidden, 106 /// [`first`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-first) is not required but 107 /// if it exists it must be `null`, and 108 /// [`second`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-second) can exist but 109 /// must be `null` if so; and only 110 /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-id) and `response` are required. 111 /// `rawId` and `type` and allowed to not exist. For the other fields, they are allowed to not exist or be `null`. 112 /// 113 /// Note that duplicate keys are still forbidden, and data matching still applies when applicable. 114 #[expect(clippy::unreachable, reason = "when there is a bug, we want to crash")] 115 #[inline] 116 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 117 where 118 D: Deserializer<'de>, 119 { 120 PublicKeyCredential::< 121 true, 122 false, 123 AuthenticatorAssertionRelaxed<USER_LEN, DISCOVERABLE>, 124 ClientExtensionsOutputsRelaxed, 125 >::deserialize(deserializer) 126 .map(|cred| { 127 Self(Authentication { 128 raw_id: cred.id.unwrap_or_else(|| { 129 unreachable!("there is a bug in PublicKeyCredential::deserialize") 130 }), 131 response: cred.response.0, 132 authenticator_attachment: cred.authenticator_attachment, 133 }) 134 }) 135 } 136 } 137 /// `AuthenticationRelaxed` with a required `UserHandle`. 138 pub type DiscoverableAuthenticationRelaxed<const USER_LEN: usize> = 139 AuthenticationRelaxed<USER_LEN, true>; 140 /// `AuthenticationRelaxed` with a required `UserHandle64`. 141 pub type DiscoverableAuthenticationRelaxed64 = AuthenticationRelaxed<USER_HANDLE_MAX_LEN, true>; 142 /// `AuthenticationRelaxed` with a required `UserHandle16`. 143 pub type DiscoverableAuthenticationRelaxed16 = AuthenticationRelaxed<16, true>; 144 /// `AuthenticationRelaxed` with an optional `UserHandle`. 145 pub type NonDiscoverableAuthenticationRelaxed<const USER_LEN: usize> = 146 AuthenticationRelaxed<USER_LEN, false>; 147 /// `AuthenticationRelaxed` with an optional `UserHandle64`. 148 pub type NonDiscoverableAuthenticationRelaxed64 = AuthenticationRelaxed<USER_HANDLE_MAX_LEN, false>; 149 /// `AuthenticationRelaxed` with an optional `UserHandle16`. 150 pub type NonDiscoverableAuthenticationRelaxed16 = AuthenticationRelaxed<16, false>; 151 /// `newtype` around `Authentication` with a custom [`Self::deserialize`] implementation. 152 #[derive(Debug)] 153 pub struct CustomAuthentication<const USER_LEN: usize, const DISCOVERABLE: bool>( 154 pub Authentication<USER_LEN, DISCOVERABLE>, 155 ); 156 impl<'de, const USER_LEN: usize, const DISCOVERABLE: bool> Deserialize<'de> 157 for CustomAuthentication<USER_LEN, DISCOVERABLE> 158 where 159 UserHandle<USER_LEN>: Deserialize<'de>, 160 { 161 /// Despite the spec having a 162 /// [pre-defined format](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationresponsejson) that clients 163 /// can follow, the downside is the superfluous data it contains. 164 /// 165 /// There simply is no reason to send the [`CredentialId`] twice. This redundant data puts RPs in 166 /// a position where they either ignore the data or parse the data to ensure no contradictions exist 167 /// (e.g., [FIDO conformance requires one to verify `id` and `rawId` exist and match](https://github.com/w3c/webauthn/issues/2119#issuecomment-2287875401)). 168 /// 169 /// While [`Authentication::deserialize`] _strictly_ adheres to the JSON definition, this implementation 170 /// strictly disallows superfluous data. Specifically the following JSON is required to be sent where duplicate 171 /// and unknown keys are disallowed: 172 /// 173 /// ```json 174 /// { 175 /// "authenticatorAttachment": null | "platform" | "cross-platform", 176 /// "authenticatorData": <base64url string>, 177 /// "clientDataJSON": <base64url string>, 178 /// "clientExtensionResults": { 179 /// "prf": null | PRFJSON 180 /// }, 181 /// "id": <see CredentialId::deserialize>, 182 /// "signature": <base64url string>, 183 /// "type": "public-key", 184 /// "userHandle": null | <see UserHandle::deserialize> 185 /// } 186 /// // PRFJSON: 187 /// { 188 /// "results": null | PRFOutputsJSON 189 /// } 190 /// // PRFOutputsJSON: 191 /// { 192 /// "first": null, 193 /// "second": null 194 /// } 195 /// ``` 196 /// 197 /// `"userHandle"` is required to exist and not be `null` iff `DISCOVERABLE`. When it does exist and 198 /// is not `null`, then it is deserialized via [`UserHandle::deserialize`]. All of the remaining keys are 199 /// required with the exceptions of `"authenticatorAttachment"` and `"type"`. `"prf"` is not required in the 200 /// `clientExtensionResults` object, `"results"` is required in the `PRFJSON` object, and `"first"` 201 /// (but not `"second"`) is required in `PRFOutputsJSON`. 202 /// 203 /// # Examples 204 /// 205 /// ``` 206 /// # use webauthn_rp::{request::register::{UserHandle, USER_HANDLE_MIN_LEN}, response::auth::ser_relaxed::CustomAuthentication}; 207 /// assert!( 208 /// // The below payload is technically valid, but `AuthenticationServerState::verify` will fail 209 /// // since the authenticatorData is not valid. This is true for `Authentication::deserialize` 210 /// // as well since authenticatorData parsing is always deferred. 211 /// serde_json::from_str::<CustomAuthentication<USER_HANDLE_MIN_LEN, true>>( 212 /// r#"{ 213 /// "authenticatorData": "AA", 214 /// "authenticatorAttachment": "cross-platform", 215 /// "clientExtensionResults": {}, 216 /// "clientDataJSON": "AA", 217 /// "id": "AAAAAAAAAAAAAAAAAAAAAA", 218 /// "signature": "AA", 219 /// "type": "public-key", 220 /// "userHandle": "AA" 221 /// }"# 222 /// ).is_ok()); 223 /// ``` 224 #[expect( 225 clippy::too_many_lines, 226 reason = "want to hide; thus don't put in outer scope" 227 )] 228 #[inline] 229 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 230 where 231 D: Deserializer<'de>, 232 { 233 /// `Visitor` for `CustomAuthentication`. 234 struct CustomAuthenticationVisitor<const LEN: usize, const DISC: bool>; 235 impl<'d, const LEN: usize, const DISC: bool> Visitor<'d> for CustomAuthenticationVisitor<LEN, DISC> 236 where 237 UserHandle<LEN>: Deserialize<'d>, 238 { 239 type Value = CustomAuthentication<LEN, DISC>; 240 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 241 formatter.write_str("CustomAuthentication") 242 } 243 #[expect( 244 clippy::too_many_lines, 245 reason = "want to hide; thus don't put in outer scope" 246 )] 247 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 248 where 249 A: MapAccess<'d>, 250 { 251 /// Fields in the JSON. 252 enum Field { 253 /// `authenticatorAttachment` key. 254 AuthenticatorAttachment, 255 /// `authenticatorData` key. 256 AuthenticatorData, 257 /// `clientDataJSON` key. 258 ClientDataJson, 259 /// `clientExtensionResults` key. 260 ClientExtensionResults, 261 /// `id` key. 262 Id, 263 /// `signature` key. 264 Signature, 265 /// `type` key. 266 Type, 267 /// `userHandle` key. 268 UserHandle, 269 } 270 impl<'e> Deserialize<'e> for Field { 271 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 272 where 273 D: Deserializer<'e>, 274 { 275 /// `Visitor` for `Field`. 276 struct FieldVisitor; 277 impl Visitor<'_> for FieldVisitor { 278 type Value = Field; 279 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 280 write!( 281 formatter, 282 "'{AUTHENTICATOR_ATTACHMENT}', '{AUTHENTICATOR_DATA}', '{CLIENT_DATA_JSON}', '{CLIENT_EXTENSION_RESULTS}', '{ID}', '{SIGNATURE}', '{TYPE}', or '{USER_HANDLE}'" 283 ) 284 } 285 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 286 where 287 E: Error, 288 { 289 match v { 290 AUTHENTICATOR_ATTACHMENT => Ok(Field::AuthenticatorAttachment), 291 AUTHENTICATOR_DATA => Ok(Field::AuthenticatorData), 292 CLIENT_DATA_JSON => Ok(Field::ClientDataJson), 293 CLIENT_EXTENSION_RESULTS => Ok(Field::ClientExtensionResults), 294 ID => Ok(Field::Id), 295 SIGNATURE => Ok(Field::Signature), 296 TYPE => Ok(Field::Type), 297 USER_HANDLE => Ok(Field::UserHandle), 298 _ => Err(E::unknown_field(v, FIELDS)), 299 } 300 } 301 } 302 deserializer.deserialize_identifier(FieldVisitor) 303 } 304 } 305 let mut authenticator_attachment = None; 306 let mut authenticator_data = None; 307 let mut client_data_json = None; 308 let mut ext = false; 309 let mut id = None; 310 let mut signature = None; 311 let mut typ = false; 312 let mut user_handle = None; 313 while let Some(key) = map.next_key()? { 314 match key { 315 Field::AuthenticatorAttachment => { 316 if authenticator_attachment.is_some() { 317 return Err(Error::duplicate_field(AUTHENTICATOR_ATTACHMENT)); 318 } 319 authenticator_attachment = map.next_value::<Option<_>>().map(Some)?; 320 } 321 Field::AuthenticatorData => { 322 if authenticator_data.is_some() { 323 return Err(Error::duplicate_field(AUTHENTICATOR_DATA)); 324 } 325 authenticator_data = 326 map.next_value::<AuthData>().map(|val| Some(val.0))?; 327 } 328 Field::ClientDataJson => { 329 if client_data_json.is_some() { 330 return Err(Error::duplicate_field(CLIENT_DATA_JSON)); 331 } 332 client_data_json = map 333 .next_value::<Base64DecodedVal>() 334 .map(|val| Some(val.0))?; 335 } 336 Field::ClientExtensionResults => { 337 if ext { 338 return Err(Error::duplicate_field(CLIENT_EXTENSION_RESULTS)); 339 } 340 ext = map.next_value::<ClientExtensionsOutputs>().map(|_| true)?; 341 } 342 Field::Id => { 343 if id.is_some() { 344 return Err(Error::duplicate_field(ID)); 345 } 346 id = map.next_value().map(Some)?; 347 } 348 Field::Signature => { 349 if signature.is_some() { 350 return Err(Error::duplicate_field(SIGNATURE)); 351 } 352 signature = map 353 .next_value::<Base64DecodedVal>() 354 .map(|val| Some(val.0))?; 355 } 356 Field::Type => { 357 if typ { 358 return Err(Error::duplicate_field(TYPE)); 359 } 360 typ = map.next_value::<Type>().map(|_| true)?; 361 } 362 Field::UserHandle => { 363 if user_handle.is_some() { 364 return Err(Error::duplicate_field(USER_HANDLE)); 365 } 366 user_handle = map.next_value().map(Some)?; 367 } 368 } 369 } 370 authenticator_data 371 .ok_or_else(|| Error::missing_field(AUTHENTICATOR_DATA)) 372 .and_then(|auth_data| { 373 client_data_json 374 .ok_or_else(|| Error::missing_field(CLIENT_DATA_JSON)) 375 .and_then(|c_data| { 376 id.ok_or_else(|| Error::missing_field(ID)) 377 .and_then(|raw_id| { 378 signature 379 .ok_or_else(|| Error::missing_field(SIGNATURE)) 380 .and_then(|sig| { 381 if ext { 382 if DISC { 383 user_handle.ok_or_else(|| Error::missing_field(USER_HANDLE)) 384 } else { 385 user_handle.map_or_else(|| Ok(None), Ok) 386 }.map(|user| { 387 CustomAuthentication(Authentication { 388 response: AuthenticatorAssertion::new_inner( 389 c_data, 390 auth_data, 391 sig, 392 user, 393 ), 394 authenticator_attachment: 395 authenticator_attachment.map_or( 396 AuthenticatorAttachment::None, 397 |auth_attach| { 398 auth_attach.unwrap_or( 399 AuthenticatorAttachment::None, 400 ) 401 }, 402 ), 403 raw_id, 404 }) 405 }) 406 } else { 407 Err(Error::missing_field( 408 CLIENT_EXTENSION_RESULTS, 409 )) 410 } 411 }) 412 }) 413 }) 414 }) 415 } 416 } 417 /// `authenticatorAttachment` key. 418 const AUTHENTICATOR_ATTACHMENT: &str = "authenticatorAttachment"; 419 /// `authenticatorData` key. 420 const AUTHENTICATOR_DATA: &str = "authenticatorData"; 421 /// `clientDataJSON` key. 422 const CLIENT_DATA_JSON: &str = "clientDataJSON"; 423 /// `clientExtensionResults` key. 424 const CLIENT_EXTENSION_RESULTS: &str = "clientExtensionResults"; 425 /// `id` key. 426 const ID: &str = "id"; 427 /// `signature` key. 428 const SIGNATURE: &str = "signature"; 429 /// `type` key. 430 const TYPE: &str = "type"; 431 /// `userHandle` key. 432 const USER_HANDLE: &str = "userHandle"; 433 /// Fields. 434 const FIELDS: &[&str; 8] = &[ 435 AUTHENTICATOR_ATTACHMENT, 436 AUTHENTICATOR_DATA, 437 CLIENT_DATA_JSON, 438 CLIENT_EXTENSION_RESULTS, 439 ID, 440 SIGNATURE, 441 TYPE, 442 USER_HANDLE, 443 ]; 444 deserializer.deserialize_struct("CustomAuthentication", FIELDS, CustomAuthenticationVisitor) 445 } 446 } 447 /// `CustomAuthentication` with a required `UserHandle`. 448 pub type DiscoverableCustomAuthentication<const USER_LEN: usize> = 449 CustomAuthentication<USER_LEN, true>; 450 /// `CustomAuthentication` with a required `UserHandle64`. 451 pub type DiscoverableCustomAuthentication64 = CustomAuthentication<USER_HANDLE_MAX_LEN, true>; 452 /// `CustomAuthentication` with a required `UserHandle16`. 453 pub type DiscoverableCustomAuthentication16 = CustomAuthentication<16, true>; 454 /// `CustomAuthentication` with an optional `UserHandle`. 455 pub type NonDiscoverableCustomAuthentication<const USER_LEN: usize> = 456 CustomAuthentication<USER_LEN, false>; 457 /// `CustomAuthentication` with an optional `UserHandle64`. 458 pub type NonDiscoverableCustomAuthentication64 = CustomAuthentication<USER_HANDLE_MAX_LEN, false>; 459 /// `CustomAuthentication` with an optional `UserHandle16`. 460 pub type NonDiscoverableCustomAuthentication16 = CustomAuthentication<16, false>; 461 #[cfg(test)] 462 mod tests { 463 use super::{ 464 super::{super::super::request::register::USER_HANDLE_MIN_LEN, AuthenticatorAttachment}, 465 DiscoverableAuthenticationRelaxed, DiscoverableCustomAuthentication, 466 NonDiscoverableAuthenticationRelaxed, NonDiscoverableCustomAuthentication, 467 }; 468 use rsa::sha2::{Digest as _, Sha256}; 469 use serde::de::{Error as _, Unexpected}; 470 use serde_json::Error; 471 #[test] 472 fn eddsa_authentication_deserialize_data_mismatch() { 473 let c_data_json = serde_json::json!({}).to_string(); 474 let auth_data = [ 475 // `rpIdHash`. 476 0, 477 0, 478 0, 479 0, 480 0, 481 0, 482 0, 483 0, 484 0, 485 0, 486 0, 487 0, 488 0, 489 0, 490 0, 491 0, 492 0, 493 0, 494 0, 495 0, 496 0, 497 0, 498 0, 499 0, 500 0, 501 0, 502 0, 503 0, 504 0, 505 0, 506 0, 507 0, 508 // `flags`. 509 0b0000_0101, 510 // `signCount`. 511 0, 512 0, 513 0, 514 0, 515 ]; 516 let b64_cdata = base64url_nopad::encode(c_data_json.as_bytes()); 517 let b64_adata = base64url_nopad::encode(auth_data.as_slice()); 518 let b64_sig = base64url_nopad::encode([].as_slice()); 519 let b64_user = base64url_nopad::encode(b"\x00".as_slice()); 520 // Base case is valid. 521 assert!( 522 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 523 serde_json::json!({ 524 "id": "AAAAAAAAAAAAAAAAAAAAAA", 525 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 526 "response": { 527 "clientDataJSON": b64_cdata, 528 "authenticatorData": b64_adata, 529 "signature": b64_sig, 530 "userHandle": b64_user, 531 }, 532 "authenticatorAttachment": "cross-platform", 533 "clientExtensionResults": {}, 534 "type": "public-key" 535 }) 536 .to_string() 537 .as_str() 538 ) 539 .map_or(false, |auth| auth.0.response.client_data_json 540 == c_data_json.as_bytes() 541 && auth.0.response.authenticator_data_and_c_data_hash[..37] == auth_data 542 && auth.0.response.authenticator_data_and_c_data_hash[37..] 543 == *Sha256::digest(c_data_json.as_bytes()).as_slice() 544 && matches!( 545 auth.0.authenticator_attachment, 546 AuthenticatorAttachment::CrossPlatform 547 )) 548 ); 549 // `id` and `rawId` mismatch. 550 let mut err = Error::invalid_value( 551 Unexpected::Bytes( 552 base64url_nopad::decode("ABABABABABABABABABABAA".as_bytes()) 553 .unwrap() 554 .as_slice(), 555 ), 556 &format!("id and rawId to match: CredentialId({:?})", [0; 16]).as_str(), 557 ) 558 .to_string() 559 .into_bytes(); 560 assert_eq!( 561 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 562 serde_json::json!({ 563 "id": "AAAAAAAAAAAAAAAAAAAAAA", 564 "rawId": "ABABABABABABABABABABAA", 565 "response": { 566 "clientDataJSON": b64_cdata, 567 "authenticatorData": b64_adata, 568 "signature": b64_sig, 569 "userHandle": b64_user, 570 }, 571 "authenticatorAttachment": "cross-platform", 572 "clientExtensionResults": {}, 573 "type": "public-key" 574 }) 575 .to_string() 576 .as_str() 577 ) 578 .unwrap_err() 579 .to_string() 580 .into_bytes()[..err.len()], 581 err 582 ); 583 // missing `id`. 584 err = Error::missing_field("id").to_string().into_bytes(); 585 assert_eq!( 586 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 587 serde_json::json!({ 588 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 589 "response": { 590 "clientDataJSON": b64_cdata, 591 "authenticatorData": b64_adata, 592 "signature": b64_sig, 593 "userHandle": b64_user, 594 }, 595 "authenticatorAttachment": "cross-platform", 596 "clientExtensionResults": {}, 597 "type": "public-key" 598 }) 599 .to_string() 600 .as_str() 601 ) 602 .unwrap_err() 603 .to_string() 604 .into_bytes()[..err.len()], 605 err 606 ); 607 // `null` `id`. 608 err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId") 609 .to_string() 610 .into_bytes(); 611 assert_eq!( 612 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 613 serde_json::json!({ 614 "id": null, 615 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 616 "response": { 617 "clientDataJSON": b64_cdata, 618 "authenticatorData": b64_adata, 619 "signature": b64_sig, 620 "userHandle": b64_user, 621 }, 622 "clientExtensionResults": {}, 623 "type": "public-key" 624 }) 625 .to_string() 626 .as_str() 627 ) 628 .unwrap_err() 629 .to_string() 630 .into_bytes()[..err.len()], 631 err 632 ); 633 // missing `rawId`. 634 assert!( 635 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 636 serde_json::json!({ 637 "id": "AAAAAAAAAAAAAAAAAAAAAA", 638 "response": { 639 "clientDataJSON": b64_cdata, 640 "authenticatorData": b64_adata, 641 "signature": b64_sig, 642 "userHandle": b64_user, 643 }, 644 "clientExtensionResults": {}, 645 "type": "public-key" 646 }) 647 .to_string() 648 .as_str() 649 ) 650 .is_ok() 651 ); 652 // `null` `rawId`. 653 err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId") 654 .to_string() 655 .into_bytes(); 656 assert_eq!( 657 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 658 serde_json::json!({ 659 "id": "AAAAAAAAAAAAAAAAAAAAAA", 660 "rawId": null, 661 "response": { 662 "clientDataJSON": b64_cdata, 663 "authenticatorData": b64_adata, 664 "signature": b64_sig, 665 "userHandle": b64_user, 666 }, 667 "clientExtensionResults": {}, 668 "type": "public-key" 669 }) 670 .to_string() 671 .as_str() 672 ) 673 .unwrap_err() 674 .to_string() 675 .into_bytes()[..err.len()], 676 err 677 ); 678 // Missing `authenticatorData`. 679 err = Error::missing_field("authenticatorData") 680 .to_string() 681 .into_bytes(); 682 assert_eq!( 683 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 684 serde_json::json!({ 685 "id": "AAAAAAAAAAAAAAAAAAAAAA", 686 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 687 "response": { 688 "clientDataJSON": b64_cdata, 689 "signature": b64_sig, 690 "userHandle": b64_user, 691 }, 692 "clientExtensionResults": {}, 693 "type": "public-key" 694 }) 695 .to_string() 696 .as_str() 697 ) 698 .unwrap_err() 699 .to_string() 700 .into_bytes()[..err.len()], 701 err 702 ); 703 // `null` `authenticatorData`. 704 err = Error::invalid_type(Unexpected::Other("null"), &"AuthenticatorData") 705 .to_string() 706 .into_bytes(); 707 assert_eq!( 708 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 709 serde_json::json!({ 710 "id": "AAAAAAAAAAAAAAAAAAAAAA", 711 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 712 "response": { 713 "clientDataJSON": b64_cdata, 714 "authenticatorData": null, 715 "signature": b64_sig, 716 "userHandle": b64_user, 717 }, 718 "clientExtensionResults": {}, 719 "type": "public-key" 720 }) 721 .to_string() 722 .as_str() 723 ) 724 .unwrap_err() 725 .to_string() 726 .into_bytes()[..err.len()], 727 err 728 ); 729 // Missing `signature`. 730 err = Error::missing_field("signature").to_string().into_bytes(); 731 assert_eq!( 732 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 733 serde_json::json!({ 734 "id": "AAAAAAAAAAAAAAAAAAAAAA", 735 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 736 "response": { 737 "clientDataJSON": b64_cdata, 738 "authenticatorData": b64_adata, 739 "userHandle": b64_user, 740 }, 741 "clientExtensionResults": {}, 742 "type": "public-key" 743 }) 744 .to_string() 745 .as_str() 746 ) 747 .unwrap_err() 748 .to_string() 749 .into_bytes()[..err.len()], 750 err 751 ); 752 // `null` `signature`. 753 err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data") 754 .to_string() 755 .into_bytes(); 756 assert_eq!( 757 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 758 serde_json::json!({ 759 "id": "AAAAAAAAAAAAAAAAAAAAAA", 760 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 761 "response": { 762 "clientDataJSON": b64_cdata, 763 "authenticatorData": b64_adata, 764 "signature": null, 765 "userHandle": b64_user, 766 }, 767 "clientExtensionResults": {}, 768 "type": "public-key" 769 }) 770 .to_string() 771 .as_str() 772 ) 773 .unwrap_err() 774 .to_string() 775 .into_bytes()[..err.len()], 776 err 777 ); 778 // Missing `userHandle`. 779 assert!( 780 serde_json::from_str::<NonDiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 781 serde_json::json!({ 782 "id": "AAAAAAAAAAAAAAAAAAAAAA", 783 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 784 "response": { 785 "clientDataJSON": b64_cdata, 786 "authenticatorData": b64_adata, 787 "signature": b64_sig, 788 }, 789 "clientExtensionResults": {}, 790 "type": "public-key" 791 }) 792 .to_string() 793 .as_str() 794 ) 795 .is_ok() 796 ); 797 // `null` `userHandle`. 798 assert!( 799 serde_json::from_str::<NonDiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 800 serde_json::json!({ 801 "id": "AAAAAAAAAAAAAAAAAAAAAA", 802 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 803 "response": { 804 "clientDataJSON": b64_cdata, 805 "authenticatorData": b64_adata, 806 "signature": b64_sig, 807 "userHandle": null, 808 }, 809 "clientExtensionResults": {}, 810 "type": "public-key" 811 }) 812 .to_string() 813 .as_str() 814 ) 815 .is_ok() 816 ); 817 // `null` `authenticatorAttachment`. 818 assert!( 819 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 820 serde_json::json!({ 821 "id": "AAAAAAAAAAAAAAAAAAAAAA", 822 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 823 "response": { 824 "clientDataJSON": b64_cdata, 825 "authenticatorData": b64_adata, 826 "signature": b64_sig, 827 "userHandle": b64_user, 828 }, 829 "authenticatorAttachment": null, 830 "clientExtensionResults": {}, 831 "type": "public-key" 832 }) 833 .to_string() 834 .as_str() 835 ) 836 .map_or(false, |auth| matches!( 837 auth.0.authenticator_attachment, 838 AuthenticatorAttachment::None 839 )) 840 ); 841 // Unknown `authenticatorAttachment`. 842 err = Error::invalid_value( 843 Unexpected::Str("Platform"), 844 &"'platform' or 'cross-platform'", 845 ) 846 .to_string() 847 .into_bytes(); 848 assert_eq!( 849 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 850 serde_json::json!({ 851 "id": "AAAAAAAAAAAAAAAAAAAAAA", 852 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 853 "response": { 854 "clientDataJSON": b64_cdata, 855 "authenticatorData": b64_adata, 856 "signature": b64_sig, 857 "userHandle": b64_user, 858 }, 859 "authenticatorAttachment": "Platform", 860 "clientExtensionResults": {}, 861 "type": "public-key" 862 }) 863 .to_string() 864 .as_str() 865 ) 866 .unwrap_err() 867 .to_string() 868 .into_bytes()[..err.len()], 869 err 870 ); 871 // Missing `clientDataJSON`. 872 err = Error::missing_field("clientDataJSON") 873 .to_string() 874 .into_bytes(); 875 assert_eq!( 876 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 877 serde_json::json!({ 878 "id": "AAAAAAAAAAAAAAAAAAAAAA", 879 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 880 "response": { 881 "authenticatorData": b64_adata, 882 "signature": b64_sig, 883 "userHandle": b64_user, 884 }, 885 "clientExtensionResults": {}, 886 "type": "public-key" 887 }) 888 .to_string() 889 .as_str() 890 ) 891 .unwrap_err() 892 .to_string() 893 .into_bytes()[..err.len()], 894 err 895 ); 896 // `null` `clientDataJSON`. 897 err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data") 898 .to_string() 899 .into_bytes(); 900 assert_eq!( 901 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 902 serde_json::json!({ 903 "id": "AAAAAAAAAAAAAAAAAAAAAA", 904 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 905 "response": { 906 "clientDataJSON": null, 907 "authenticatorData": b64_adata, 908 "signature": b64_sig, 909 "userHandle": b64_user, 910 }, 911 "clientExtensionResults": {}, 912 "type": "public-key" 913 }) 914 .to_string() 915 .as_str() 916 ) 917 .unwrap_err() 918 .to_string() 919 .into_bytes()[..err.len()], 920 err 921 ); 922 // Missing `response`. 923 err = Error::missing_field("response").to_string().into_bytes(); 924 assert_eq!( 925 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 926 serde_json::json!({ 927 "id": "AAAAAAAAAAAAAAAAAAAAAA", 928 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 929 "clientExtensionResults": {}, 930 "type": "public-key" 931 }) 932 .to_string() 933 .as_str() 934 ) 935 .unwrap_err() 936 .to_string() 937 .into_bytes()[..err.len()], 938 err 939 ); 940 // `null` `response`. 941 err = Error::invalid_type(Unexpected::Other("null"), &"AuthenticatorAssertion") 942 .to_string() 943 .into_bytes(); 944 assert_eq!( 945 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 946 serde_json::json!({ 947 "id": "AAAAAAAAAAAAAAAAAAAAAA", 948 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 949 "response": null, 950 "clientExtensionResults": {}, 951 "type": "public-key" 952 }) 953 .to_string() 954 .as_str() 955 ) 956 .unwrap_err() 957 .to_string() 958 .into_bytes()[..err.len()], 959 err 960 ); 961 // Empty `response`. 962 err = Error::missing_field("clientDataJSON") 963 .to_string() 964 .into_bytes(); 965 assert_eq!( 966 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 967 serde_json::json!({ 968 "id": "AAAAAAAAAAAAAAAAAAAAAA", 969 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 970 "response": {}, 971 "clientExtensionResults": {}, 972 "type": "public-key" 973 }) 974 .to_string() 975 .as_str() 976 ) 977 .unwrap_err() 978 .to_string() 979 .into_bytes()[..err.len()], 980 err 981 ); 982 // Missing `clientExtensionResults`. 983 assert!( 984 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 985 serde_json::json!({ 986 "id": "AAAAAAAAAAAAAAAAAAAAAA", 987 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 988 "response": { 989 "clientDataJSON": b64_cdata, 990 "authenticatorData": b64_adata, 991 "signature": b64_sig, 992 "userHandle": b64_user, 993 }, 994 "type": "public-key" 995 }) 996 .to_string() 997 .as_str() 998 ) 999 .is_ok() 1000 ); 1001 // `null` `clientExtensionResults`. 1002 assert!( 1003 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1004 serde_json::json!({ 1005 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1006 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1007 "response": { 1008 "clientDataJSON": b64_cdata, 1009 "authenticatorData": b64_adata, 1010 "signature": b64_sig, 1011 "userHandle": b64_user, 1012 }, 1013 "clientExtensionResults": null, 1014 "type": "public-key" 1015 }) 1016 .to_string() 1017 .as_str() 1018 ) 1019 .is_ok() 1020 ); 1021 // Missing `type`. 1022 assert!( 1023 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1024 serde_json::json!({ 1025 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1026 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1027 "response": { 1028 "clientDataJSON": b64_cdata, 1029 "authenticatorData": b64_adata, 1030 "signature": b64_sig, 1031 "userHandle": b64_user, 1032 }, 1033 "clientExtensionResults": {}, 1034 }) 1035 .to_string() 1036 .as_str() 1037 ) 1038 .is_ok() 1039 ); 1040 // `null` `type`. 1041 err = Error::invalid_type(Unexpected::Other("null"), &"public-key") 1042 .to_string() 1043 .into_bytes(); 1044 assert_eq!( 1045 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1046 serde_json::json!({ 1047 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1048 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1049 "response": { 1050 "clientDataJSON": b64_cdata, 1051 "authenticatorData": b64_adata, 1052 "signature": b64_sig, 1053 "userHandle": b64_user, 1054 }, 1055 "clientExtensionResults": {}, 1056 "type": null 1057 }) 1058 .to_string() 1059 .as_str() 1060 ) 1061 .unwrap_err() 1062 .to_string() 1063 .into_bytes()[..err.len()], 1064 err 1065 ); 1066 // Not exactly `public-type` `type`. 1067 err = Error::invalid_value(Unexpected::Str("Public-key"), &"public-key") 1068 .to_string() 1069 .into_bytes(); 1070 assert_eq!( 1071 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1072 serde_json::json!({ 1073 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1074 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1075 "response": { 1076 "clientDataJSON": b64_cdata, 1077 "authenticatorData": b64_adata, 1078 "signature": b64_sig, 1079 "userHandle": b64_user, 1080 }, 1081 "clientExtensionResults": {}, 1082 "type": "Public-key" 1083 }) 1084 .to_string() 1085 .as_str() 1086 ) 1087 .unwrap_err() 1088 .to_string() 1089 .into_bytes()[..err.len()], 1090 err 1091 ); 1092 // `null`. 1093 err = Error::invalid_type(Unexpected::Other("null"), &"PublicKeyCredential") 1094 .to_string() 1095 .into_bytes(); 1096 assert_eq!( 1097 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1098 serde_json::json!(null).to_string().as_str() 1099 ) 1100 .unwrap_err() 1101 .to_string() 1102 .into_bytes()[..err.len()], 1103 err 1104 ); 1105 // Empty. 1106 err = Error::missing_field("response").to_string().into_bytes(); 1107 assert_eq!( 1108 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1109 serde_json::json!({}).to_string().as_str() 1110 ) 1111 .unwrap_err() 1112 .to_string() 1113 .into_bytes()[..err.len()], 1114 err 1115 ); 1116 // Unknown field in `response`. 1117 assert!( 1118 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1119 serde_json::json!({ 1120 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1121 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1122 "response": { 1123 "clientDataJSON": b64_cdata, 1124 "authenticatorData": b64_adata, 1125 "signature": b64_sig, 1126 "userHandle": b64_user, 1127 "foo": true, 1128 }, 1129 "clientExtensionResults": {}, 1130 "type": "public-key" 1131 }) 1132 .to_string() 1133 .as_str() 1134 ) 1135 .is_ok() 1136 ); 1137 // Duplicate field in `response`. 1138 err = Error::duplicate_field("userHandle") 1139 .to_string() 1140 .into_bytes(); 1141 assert_eq!( 1142 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1143 format!( 1144 "{{ 1145 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1146 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1147 \"response\": {{ 1148 \"clientDataJSON\": \"{b64_cdata}\", 1149 \"authenticatorData\": \"{b64_adata}\", 1150 \"signature\": \"{b64_sig}\", 1151 \"userHandle\": \"{b64_user}\", 1152 \"userHandle\": \"{b64_user}\" 1153 }}, 1154 \"clientExtensionResults\": {{}}, 1155 \"type\": \"public-key\" 1156 1157 }}" 1158 ) 1159 .as_str() 1160 ) 1161 .unwrap_err() 1162 .to_string() 1163 .into_bytes()[..err.len()], 1164 err 1165 ); 1166 // Unknown field in `PublicKeyCredential`. 1167 assert!( 1168 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1169 serde_json::json!({ 1170 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1171 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1172 "response": { 1173 "clientDataJSON": b64_cdata, 1174 "authenticatorData": b64_adata, 1175 "signature": b64_sig, 1176 "userHandle": b64_user, 1177 }, 1178 "clientExtensionResults": {}, 1179 "type": "public-key", 1180 "foo": true, 1181 }) 1182 .to_string() 1183 .as_str() 1184 ) 1185 .is_ok() 1186 ); 1187 // Duplicate field in `PublicKeyCredential`. 1188 err = Error::duplicate_field("id").to_string().into_bytes(); 1189 assert_eq!( 1190 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1191 format!( 1192 "{{ 1193 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1194 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1195 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1196 \"response\": {{ 1197 \"clientDataJSON\": \"{b64_cdata}\", 1198 \"authenticatorData\": \"{b64_adata}\", 1199 \"signature\": \"{b64_sig}\", 1200 \"userHandle\": \"{b64_user}\" 1201 }}, 1202 \"clientExtensionResults\": {{}}, 1203 \"type\": \"public-key\" 1204 1205 }}" 1206 ) 1207 .as_str() 1208 ) 1209 .unwrap_err() 1210 .to_string() 1211 .into_bytes()[..err.len()], 1212 err 1213 ); 1214 // Base case is valid. 1215 assert!( 1216 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1217 serde_json::json!({ 1218 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1219 "clientDataJSON": b64_cdata, 1220 "authenticatorData": b64_adata, 1221 "signature": b64_sig, 1222 "userHandle": b64_user, 1223 "authenticatorAttachment": "cross-platform", 1224 "clientExtensionResults": {}, 1225 "type": "public-key" 1226 }) 1227 .to_string() 1228 .as_str() 1229 ) 1230 .map_or(false, |auth| auth.0.response.client_data_json 1231 == c_data_json.as_bytes() 1232 && auth.0.response.authenticator_data_and_c_data_hash[..37] == auth_data 1233 && auth.0.response.authenticator_data_and_c_data_hash[37..] 1234 == *Sha256::digest(c_data_json.as_bytes()).as_slice() 1235 && matches!( 1236 auth.0.authenticator_attachment, 1237 AuthenticatorAttachment::CrossPlatform 1238 )) 1239 ); 1240 // missing `id`. 1241 err = Error::missing_field("id").to_string().into_bytes(); 1242 assert_eq!( 1243 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1244 serde_json::json!({ 1245 "clientDataJSON": b64_cdata, 1246 "authenticatorData": b64_adata, 1247 "signature": b64_sig, 1248 "userHandle": b64_user, 1249 "authenticatorAttachment": "cross-platform", 1250 "clientExtensionResults": {}, 1251 "type": "public-key" 1252 }) 1253 .to_string() 1254 .as_str() 1255 ) 1256 .unwrap_err() 1257 .to_string() 1258 .into_bytes()[..err.len()], 1259 err 1260 ); 1261 // `null` `id`. 1262 err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId") 1263 .to_string() 1264 .into_bytes(); 1265 assert_eq!( 1266 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1267 serde_json::json!({ 1268 "id": null, 1269 "clientDataJSON": b64_cdata, 1270 "authenticatorData": b64_adata, 1271 "signature": b64_sig, 1272 "userHandle": b64_user, 1273 "clientExtensionResults": {}, 1274 "type": "public-key" 1275 }) 1276 .to_string() 1277 .as_str() 1278 ) 1279 .unwrap_err() 1280 .to_string() 1281 .into_bytes()[..err.len()], 1282 err 1283 ); 1284 // Missing `authenticatorData`. 1285 err = Error::missing_field("authenticatorData") 1286 .to_string() 1287 .into_bytes(); 1288 assert_eq!( 1289 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1290 serde_json::json!({ 1291 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1292 "clientDataJSON": b64_cdata, 1293 "signature": b64_sig, 1294 "userHandle": b64_user, 1295 "clientExtensionResults": {}, 1296 "type": "public-key" 1297 }) 1298 .to_string() 1299 .as_str() 1300 ) 1301 .unwrap_err() 1302 .to_string() 1303 .into_bytes()[..err.len()], 1304 err 1305 ); 1306 // `null` `authenticatorData`. 1307 err = Error::invalid_type(Unexpected::Other("null"), &"AuthenticatorData") 1308 .to_string() 1309 .into_bytes(); 1310 assert_eq!( 1311 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1312 serde_json::json!({ 1313 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1314 "clientDataJSON": b64_cdata, 1315 "authenticatorData": null, 1316 "signature": b64_sig, 1317 "userHandle": b64_user, 1318 "clientExtensionResults": {}, 1319 "type": "public-key" 1320 }) 1321 .to_string() 1322 .as_str() 1323 ) 1324 .unwrap_err() 1325 .to_string() 1326 .into_bytes()[..err.len()], 1327 err 1328 ); 1329 // Missing `signature`. 1330 err = Error::missing_field("signature").to_string().into_bytes(); 1331 assert_eq!( 1332 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1333 serde_json::json!({ 1334 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1335 "clientDataJSON": b64_cdata, 1336 "authenticatorData": b64_adata, 1337 "userHandle": b64_user, 1338 "clientExtensionResults": {}, 1339 "type": "public-key" 1340 }) 1341 .to_string() 1342 .as_str() 1343 ) 1344 .unwrap_err() 1345 .to_string() 1346 .into_bytes()[..err.len()], 1347 err 1348 ); 1349 // `null` `signature`. 1350 err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data") 1351 .to_string() 1352 .into_bytes(); 1353 assert_eq!( 1354 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1355 serde_json::json!({ 1356 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1357 "clientDataJSON": b64_cdata, 1358 "authenticatorData": b64_adata, 1359 "signature": null, 1360 "userHandle": b64_user, 1361 "clientExtensionResults": {}, 1362 "type": "public-key" 1363 }) 1364 .to_string() 1365 .as_str() 1366 ) 1367 .unwrap_err() 1368 .to_string() 1369 .into_bytes()[..err.len()], 1370 err 1371 ); 1372 // Missing `userHandle`. 1373 assert!( 1374 serde_json::from_str::<NonDiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1375 serde_json::json!({ 1376 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1377 "clientDataJSON": b64_cdata, 1378 "authenticatorData": b64_adata, 1379 "signature": b64_sig, 1380 "clientExtensionResults": {}, 1381 "type": "public-key" 1382 }) 1383 .to_string() 1384 .as_str() 1385 ) 1386 .is_ok() 1387 ); 1388 // `null` `userHandle`. 1389 assert!( 1390 serde_json::from_str::<NonDiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1391 serde_json::json!({ 1392 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1393 "clientDataJSON": b64_cdata, 1394 "authenticatorData": b64_adata, 1395 "signature": b64_sig, 1396 "userHandle": null, 1397 "clientExtensionResults": {}, 1398 "type": "public-key" 1399 }) 1400 .to_string() 1401 .as_str() 1402 ) 1403 .is_ok() 1404 ); 1405 // `null` `authenticatorAttachment`. 1406 assert!( 1407 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1408 serde_json::json!({ 1409 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1410 "clientDataJSON": b64_cdata, 1411 "authenticatorData": b64_adata, 1412 "signature": b64_sig, 1413 "userHandle": b64_user, 1414 "authenticatorAttachment": null, 1415 "clientExtensionResults": {}, 1416 "type": "public-key" 1417 }) 1418 .to_string() 1419 .as_str() 1420 ) 1421 .map_or(false, |auth| matches!( 1422 auth.0.authenticator_attachment, 1423 AuthenticatorAttachment::None 1424 )) 1425 ); 1426 // Unknown `authenticatorAttachment`. 1427 err = Error::invalid_value( 1428 Unexpected::Str("Platform"), 1429 &"'platform' or 'cross-platform'", 1430 ) 1431 .to_string() 1432 .into_bytes(); 1433 assert_eq!( 1434 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1435 serde_json::json!({ 1436 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1437 "clientDataJSON": b64_cdata, 1438 "authenticatorData": b64_adata, 1439 "signature": b64_sig, 1440 "userHandle": b64_user, 1441 "authenticatorAttachment": "Platform", 1442 "clientExtensionResults": {}, 1443 "type": "public-key" 1444 }) 1445 .to_string() 1446 .as_str() 1447 ) 1448 .unwrap_err() 1449 .to_string() 1450 .into_bytes()[..err.len()], 1451 err 1452 ); 1453 // Missing `clientDataJSON`. 1454 err = Error::missing_field("clientDataJSON") 1455 .to_string() 1456 .into_bytes(); 1457 assert_eq!( 1458 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1459 serde_json::json!({ 1460 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1461 "authenticatorData": b64_adata, 1462 "signature": b64_sig, 1463 "userHandle": b64_user, 1464 "clientExtensionResults": {}, 1465 "type": "public-key" 1466 }) 1467 .to_string() 1468 .as_str() 1469 ) 1470 .unwrap_err() 1471 .to_string() 1472 .into_bytes()[..err.len()], 1473 err 1474 ); 1475 // `null` `clientDataJSON`. 1476 err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data") 1477 .to_string() 1478 .into_bytes(); 1479 assert_eq!( 1480 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1481 serde_json::json!({ 1482 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1483 "clientDataJSON": null, 1484 "authenticatorData": b64_adata, 1485 "signature": b64_sig, 1486 "userHandle": b64_user, 1487 "clientExtensionResults": {}, 1488 "type": "public-key" 1489 }) 1490 .to_string() 1491 .as_str() 1492 ) 1493 .unwrap_err() 1494 .to_string() 1495 .into_bytes()[..err.len()], 1496 err 1497 ); 1498 // Empty. 1499 err = Error::missing_field("authenticatorData") 1500 .to_string() 1501 .into_bytes(); 1502 assert_eq!( 1503 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1504 serde_json::json!({}).to_string().as_str() 1505 ) 1506 .unwrap_err() 1507 .to_string() 1508 .into_bytes()[..err.len()], 1509 err 1510 ); 1511 // Missing `clientExtensionResults`. 1512 err = Error::missing_field("clientExtensionResults") 1513 .to_string() 1514 .into_bytes(); 1515 assert_eq!( 1516 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1517 serde_json::json!({ 1518 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1519 "clientDataJSON": b64_cdata, 1520 "authenticatorData": b64_adata, 1521 "signature": b64_sig, 1522 "userHandle": b64_user, 1523 "type": "public-key" 1524 }) 1525 .to_string() 1526 .as_str() 1527 ) 1528 .unwrap_err() 1529 .to_string() 1530 .into_bytes()[..err.len()], 1531 err 1532 ); 1533 // `null` `clientExtensionResults`. 1534 err = Error::invalid_type(Unexpected::Other("null"), &"ClientExtensionsOutputs") 1535 .to_string() 1536 .into_bytes(); 1537 assert_eq!( 1538 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1539 serde_json::json!({ 1540 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1541 "clientDataJSON": b64_cdata, 1542 "authenticatorData": b64_adata, 1543 "signature": b64_sig, 1544 "userHandle": b64_user, 1545 "clientExtensionResults": null, 1546 "type": "public-key" 1547 }) 1548 .to_string() 1549 .as_str() 1550 ) 1551 .unwrap_err() 1552 .to_string() 1553 .into_bytes()[..err.len()], 1554 err 1555 ); 1556 assert!( 1557 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1558 serde_json::json!({ 1559 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1560 "clientDataJSON": b64_cdata, 1561 "authenticatorData": b64_adata, 1562 "signature": b64_sig, 1563 "userHandle": b64_user, 1564 "clientExtensionResults": {}, 1565 }) 1566 .to_string() 1567 .as_str() 1568 ) 1569 .is_ok() 1570 ); 1571 // `null` `type`. 1572 err = Error::invalid_type(Unexpected::Other("null"), &"public-key") 1573 .to_string() 1574 .into_bytes(); 1575 assert_eq!( 1576 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1577 serde_json::json!({ 1578 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1579 "clientDataJSON": b64_cdata, 1580 "authenticatorData": b64_adata, 1581 "signature": b64_sig, 1582 "userHandle": b64_user, 1583 "clientExtensionResults": {}, 1584 "type": null 1585 }) 1586 .to_string() 1587 .as_str() 1588 ) 1589 .unwrap_err() 1590 .to_string() 1591 .into_bytes()[..err.len()], 1592 err 1593 ); 1594 // Not exactly `public-type` `type`. 1595 err = Error::invalid_value(Unexpected::Str("Public-key"), &"public-key") 1596 .to_string() 1597 .into_bytes(); 1598 assert_eq!( 1599 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1600 serde_json::json!({ 1601 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1602 "clientDataJSON": b64_cdata, 1603 "authenticatorData": b64_adata, 1604 "signature": b64_sig, 1605 "userHandle": b64_user, 1606 "clientExtensionResults": {}, 1607 "type": "Public-key" 1608 }) 1609 .to_string() 1610 .as_str() 1611 ) 1612 .unwrap_err() 1613 .to_string() 1614 .into_bytes()[..err.len()], 1615 err 1616 ); 1617 // `null`. 1618 err = Error::invalid_type(Unexpected::Other("null"), &"CustomAuthentication") 1619 .to_string() 1620 .into_bytes(); 1621 assert_eq!( 1622 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1623 serde_json::json!(null).to_string().as_str() 1624 ) 1625 .unwrap_err() 1626 .to_string() 1627 .into_bytes()[..err.len()], 1628 err 1629 ); 1630 // Unknown field. 1631 err = Error::unknown_field( 1632 "foo", 1633 [ 1634 "authenticatorAttachment", 1635 "authenticatorData", 1636 "clientDataJSON", 1637 "clientExtensionResults", 1638 "id", 1639 "signature", 1640 "type", 1641 "userHandle", 1642 ] 1643 .as_slice(), 1644 ) 1645 .to_string() 1646 .into_bytes(); 1647 assert_eq!( 1648 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1649 serde_json::json!({ 1650 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1651 "clientDataJSON": b64_cdata, 1652 "authenticatorData": b64_adata, 1653 "signature": b64_sig, 1654 "userHandle": b64_user, 1655 "foo": true, 1656 "clientExtensionResults": {}, 1657 "type": "public-key" 1658 }) 1659 .to_string() 1660 .as_str() 1661 ) 1662 .unwrap_err() 1663 .to_string() 1664 .into_bytes()[..err.len()], 1665 err 1666 ); 1667 // Duplicate field. 1668 err = Error::duplicate_field("userHandle") 1669 .to_string() 1670 .into_bytes(); 1671 assert_eq!( 1672 serde_json::from_str::<DiscoverableCustomAuthentication<USER_HANDLE_MIN_LEN>>( 1673 format!( 1674 "{{ 1675 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1676 \"clientDataJSON\": \"{b64_cdata}\", 1677 \"authenticatorData\": \"{b64_adata}\", 1678 \"signature\": \"{b64_sig}\", 1679 \"userHandle\": \"{b64_user}\", 1680 \"userHandle\": \"{b64_user}\" 1681 \"clientExtensionResults\": {{}}, 1682 \"type\": \"public-key\" 1683 1684 }}" 1685 ) 1686 .as_str() 1687 ) 1688 .unwrap_err() 1689 .to_string() 1690 .into_bytes()[..err.len()], 1691 err 1692 ); 1693 } 1694 #[test] 1695 fn client_extensions() { 1696 let c_data_json = serde_json::json!({}).to_string(); 1697 let auth_data = [ 1698 // `rpIdHash`. 1699 0, 1700 0, 1701 0, 1702 0, 1703 0, 1704 0, 1705 0, 1706 0, 1707 0, 1708 0, 1709 0, 1710 0, 1711 0, 1712 0, 1713 0, 1714 0, 1715 0, 1716 0, 1717 0, 1718 0, 1719 0, 1720 0, 1721 0, 1722 0, 1723 0, 1724 0, 1725 0, 1726 0, 1727 0, 1728 0, 1729 0, 1730 0, 1731 // `flags`. 1732 0b0000_0101, 1733 // `signCount`. 1734 0, 1735 0, 1736 0, 1737 0, 1738 ]; 1739 let b64_cdata = base64url_nopad::encode(c_data_json.as_bytes()); 1740 let b64_adata = base64url_nopad::encode(auth_data.as_slice()); 1741 let b64_sig = base64url_nopad::encode([].as_slice()); 1742 let b64_user = base64url_nopad::encode(b"\x00".as_slice()); 1743 // Base case is valid. 1744 assert!( 1745 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1746 serde_json::json!({ 1747 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1748 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1749 "response": { 1750 "clientDataJSON": b64_cdata, 1751 "authenticatorData": b64_adata, 1752 "signature": b64_sig, 1753 "userHandle": b64_user, 1754 }, 1755 "authenticatorAttachment": "cross-platform", 1756 "clientExtensionResults": {}, 1757 "type": "public-key" 1758 }) 1759 .to_string() 1760 .as_str() 1761 ) 1762 .map_or(false, |auth| auth.0.response.client_data_json 1763 == c_data_json.as_bytes() 1764 && auth.0.response.authenticator_data_and_c_data_hash[..37] == auth_data 1765 && auth.0.response.authenticator_data_and_c_data_hash[37..] 1766 == *Sha256::digest(c_data_json.as_bytes()).as_slice() 1767 && matches!( 1768 auth.0.authenticator_attachment, 1769 AuthenticatorAttachment::CrossPlatform 1770 )) 1771 ); 1772 // `null` `prf`. 1773 assert!( 1774 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1775 serde_json::json!({ 1776 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1777 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1778 "response": { 1779 "clientDataJSON": b64_cdata, 1780 "authenticatorData": b64_adata, 1781 "signature": b64_sig, 1782 "userHandle": b64_user, 1783 }, 1784 "clientExtensionResults": { 1785 "prf": null 1786 }, 1787 "type": "public-key" 1788 }) 1789 .to_string() 1790 .as_str() 1791 ) 1792 .is_ok() 1793 ); 1794 // Unknown `clientExtensionResults`. 1795 assert!( 1796 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1797 serde_json::json!({ 1798 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1799 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1800 "response": { 1801 "clientDataJSON": b64_cdata, 1802 "authenticatorData": b64_adata, 1803 "signature": b64_sig, 1804 "userHandle": b64_user, 1805 }, 1806 "clientExtensionResults": { 1807 "Prf": null 1808 }, 1809 "type": "public-key" 1810 }) 1811 .to_string() 1812 .as_str() 1813 ) 1814 .is_ok() 1815 ); 1816 // Duplicate field. 1817 let mut err = Error::duplicate_field("prf").to_string().into_bytes(); 1818 assert_eq!( 1819 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1820 format!( 1821 "{{ 1822 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1823 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1824 \"response\": {{ 1825 \"clientDataJSON\": \"{b64_cdata}\", 1826 \"authenticatorData\": \"{b64_adata}\", 1827 \"signature\": \"{b64_sig}\", 1828 \"userHandle\": \"{b64_user}\" 1829 }}, 1830 \"clientExtensionResults\": {{ 1831 \"prf\": null, 1832 \"prf\": null 1833 }}, 1834 \"type\": \"public-key\" 1835 }}" 1836 ) 1837 .as_str() 1838 ) 1839 .unwrap_err() 1840 .to_string() 1841 .into_bytes()[..err.len()], 1842 err 1843 ); 1844 // `null` `results`. 1845 assert!( 1846 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1847 serde_json::json!({ 1848 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1849 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1850 "response": { 1851 "clientDataJSON": b64_cdata, 1852 "authenticatorData": b64_adata, 1853 "signature": b64_sig, 1854 "userHandle": b64_user, 1855 }, 1856 "clientExtensionResults": { 1857 "prf": { 1858 "results": null, 1859 } 1860 }, 1861 "type": "public-key" 1862 }) 1863 .to_string() 1864 .as_str() 1865 ) 1866 .is_ok() 1867 ); 1868 // Duplicate field in `prf`. 1869 err = Error::duplicate_field("results").to_string().into_bytes(); 1870 assert_eq!( 1871 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1872 format!( 1873 "{{ 1874 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1875 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1876 \"response\": {{ 1877 \"clientDataJSON\": \"{b64_cdata}\", 1878 \"authenticatorData\": \"{b64_adata}\", 1879 \"signature\": \"{b64_sig}\", 1880 \"userHandle\": \"{b64_user}\" 1881 }}, 1882 \"clientExtensionResults\": {{ 1883 \"prf\": {{ 1884 \"results\": null, 1885 \"results\": null 1886 }} 1887 }}, 1888 \"type\": \"public-key\" 1889 }}" 1890 ) 1891 .as_str() 1892 ) 1893 .unwrap_err() 1894 .to_string() 1895 .into_bytes()[..err.len()], 1896 err 1897 ); 1898 // Missing `first`. 1899 assert!( 1900 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1901 serde_json::json!({ 1902 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1903 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1904 "response": { 1905 "clientDataJSON": b64_cdata, 1906 "authenticatorData": b64_adata, 1907 "signature": b64_sig, 1908 "userHandle": b64_user, 1909 }, 1910 "clientExtensionResults": { 1911 "prf": { 1912 "results": {}, 1913 } 1914 }, 1915 "type": "public-key" 1916 }) 1917 .to_string() 1918 .as_str() 1919 ) 1920 .is_ok() 1921 ); 1922 // `null` `first`. 1923 assert!( 1924 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1925 serde_json::json!({ 1926 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1927 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1928 "response": { 1929 "clientDataJSON": b64_cdata, 1930 "authenticatorData": b64_adata, 1931 "signature": b64_sig, 1932 "userHandle": b64_user, 1933 }, 1934 "clientExtensionResults": { 1935 "prf": { 1936 "results": { 1937 "first": null 1938 }, 1939 } 1940 }, 1941 "type": "public-key" 1942 }) 1943 .to_string() 1944 .as_str() 1945 ) 1946 .is_ok() 1947 ); 1948 // `null` `second`. 1949 assert!( 1950 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1951 serde_json::json!({ 1952 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1953 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1954 "response": { 1955 "clientDataJSON": b64_cdata, 1956 "authenticatorData": b64_adata, 1957 "signature": b64_sig, 1958 "userHandle": b64_user, 1959 }, 1960 "clientExtensionResults": { 1961 "prf": { 1962 "results": { 1963 "first": null, 1964 "second": null 1965 }, 1966 } 1967 }, 1968 "type": "public-key" 1969 }) 1970 .to_string() 1971 .as_str() 1972 ) 1973 .is_ok() 1974 ); 1975 // Non-`null` `first`. 1976 err = Error::invalid_type(Unexpected::Option, &"null") 1977 .to_string() 1978 .into_bytes(); 1979 assert_eq!( 1980 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 1981 serde_json::json!({ 1982 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1983 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1984 "response": { 1985 "clientDataJSON": b64_cdata, 1986 "authenticatorData": b64_adata, 1987 "signature": b64_sig, 1988 "userHandle": b64_user, 1989 }, 1990 "clientExtensionResults": { 1991 "prf": { 1992 "results": { 1993 "first": "" 1994 }, 1995 } 1996 }, 1997 "type": "public-key" 1998 }) 1999 .to_string() 2000 .as_str() 2001 ) 2002 .unwrap_err() 2003 .to_string() 2004 .into_bytes()[..err.len()], 2005 err 2006 ); 2007 // Non-`null` `second`. 2008 err = Error::invalid_type(Unexpected::Option, &"null") 2009 .to_string() 2010 .into_bytes(); 2011 assert_eq!( 2012 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 2013 serde_json::json!({ 2014 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2015 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2016 "response": { 2017 "clientDataJSON": b64_cdata, 2018 "authenticatorData": b64_adata, 2019 "signature": b64_sig, 2020 "userHandle": b64_user, 2021 }, 2022 "clientExtensionResults": { 2023 "prf": { 2024 "results": { 2025 "first": null, 2026 "second": "" 2027 }, 2028 } 2029 }, 2030 "type": "public-key" 2031 }) 2032 .to_string() 2033 .as_str() 2034 ) 2035 .unwrap_err() 2036 .to_string() 2037 .into_bytes()[..err.len()], 2038 err 2039 ); 2040 // `enabled` is still not allowed. 2041 err = Error::unknown_field("enabled", ["results"].as_slice()) 2042 .to_string() 2043 .into_bytes(); 2044 assert_eq!( 2045 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 2046 serde_json::json!({ 2047 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2048 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2049 "response": { 2050 "clientDataJSON": b64_cdata, 2051 "authenticatorData": b64_adata, 2052 "signature": b64_sig, 2053 "userHandle": b64_user, 2054 }, 2055 "clientExtensionResults": { 2056 "prf": { 2057 "enabled": true, 2058 "results": null 2059 } 2060 }, 2061 "type": "public-key" 2062 }) 2063 .to_string() 2064 .as_str() 2065 ) 2066 .unwrap_err() 2067 .to_string() 2068 .into_bytes()[..err.len()], 2069 err 2070 ); 2071 // Unknown `prf` field. 2072 assert!( 2073 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 2074 serde_json::json!({ 2075 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2076 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2077 "response": { 2078 "clientDataJSON": b64_cdata, 2079 "authenticatorData": b64_adata, 2080 "signature": b64_sig, 2081 "userHandle": b64_user, 2082 }, 2083 "clientExtensionResults": { 2084 "prf": { 2085 "foo": true, 2086 "results": null 2087 } 2088 }, 2089 "type": "public-key" 2090 }) 2091 .to_string() 2092 .as_str() 2093 ) 2094 .is_ok() 2095 ); 2096 // Unknown `results` field. 2097 assert!( 2098 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 2099 serde_json::json!({ 2100 "id": "AAAAAAAAAAAAAAAAAAAAAA", 2101 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 2102 "response": { 2103 "clientDataJSON": b64_cdata, 2104 "authenticatorData": b64_adata, 2105 "signature": b64_sig, 2106 "userHandle": b64_user, 2107 }, 2108 "clientExtensionResults": { 2109 "prf": { 2110 "results": { 2111 "first": null, 2112 "Second": null 2113 } 2114 } 2115 }, 2116 "type": "public-key" 2117 }) 2118 .to_string() 2119 .as_str() 2120 ) 2121 .is_ok() 2122 ); 2123 // Duplicate field in `results`. 2124 err = Error::duplicate_field("first").to_string().into_bytes(); 2125 assert_eq!( 2126 serde_json::from_str::<DiscoverableAuthenticationRelaxed<USER_HANDLE_MIN_LEN>>( 2127 format!( 2128 "{{ 2129 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 2130 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 2131 \"response\": {{ 2132 \"clientDataJSON\": \"{b64_cdata}\", 2133 \"authenticatorData\": \"{b64_adata}\", 2134 \"signature\": \"{b64_sig}\", 2135 \"userHandle\": \"{b64_user}\" 2136 }}, 2137 \"clientExtensionResults\": {{ 2138 \"prf\": {{ 2139 \"results\": {{ 2140 \"first\": null, 2141 \"first\": null 2142 }} 2143 }} 2144 }}, 2145 \"type\": \"public-key\" 2146 }}" 2147 ) 2148 .as_str() 2149 ) 2150 .unwrap_err() 2151 .to_string() 2152 .into_bytes()[..err.len()], 2153 err 2154 ); 2155 } 2156 }