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