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