ser.rs (64333B)
1 use super::{ 2 super::{ 3 super::response::ser::{Base64DecodedVal, PublicKeyCredential}, 4 ser::{ 5 AuthenticationExtensionsPrfOutputsHelper, AuthenticationExtensionsPrfValues, 6 ClientExtensions, 7 }, 8 }, 9 Authentication, AuthenticatorAssertion, UserHandle, 10 error::UnknownCredentialOptions, 11 }; 12 #[cfg(doc)] 13 use super::{AuthenticatorAttachment, CredentialId}; 14 use core::{ 15 fmt::{self, Formatter}, 16 marker::PhantomData, 17 str, 18 }; 19 use rsa::sha2::{Sha256, digest::OutputSizeUser as _}; 20 use serde::{ 21 de::{Deserialize, Deserializer, Error, IgnoredAny, MapAccess, Unexpected, Visitor}, 22 ser::{Serialize, SerializeStruct as _, Serializer}, 23 }; 24 /// Authenticator data. 25 pub(super) struct AuthData(pub Vec<u8>); 26 impl<'e> Deserialize<'e> for AuthData { 27 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 28 where 29 D: Deserializer<'e>, 30 { 31 /// `Visitor` for `AuthData`. 32 struct AuthDataVisitor; 33 impl Visitor<'_> for AuthDataVisitor { 34 type Value = AuthData; 35 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 36 formatter.write_str("AuthenticatorData") 37 } 38 #[expect( 39 clippy::arithmetic_side_effects, 40 reason = "comment justifies its correctness" 41 )] 42 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 43 where 44 E: Error, 45 { 46 base64url_nopad::decode_len(v.len()) 47 .ok_or_else(|| E::invalid_value(Unexpected::Str(v), &"base64url-encoded value")) 48 .and_then(|len| { 49 // The decoded length is 3/4 of the encoded length, so overflow could only occur 50 // if usize::MAX / 4 < 32 => usize::MAX < 128 < u16::MAX; thus overflow is not 51 // possible. 52 // We add 32 since the SHA-256 hash of `clientDataJSON` will be added to the 53 // raw authenticator data by `AuthenticatorDataAssertion::new`. 54 let mut auth_data = vec![0; len + Sha256::output_size()]; 55 auth_data.truncate(len); 56 base64url_nopad::decode_buffer_exact(v.as_bytes(), auth_data.as_mut_slice()) 57 .map_err(E::custom) 58 .map(|()| AuthData(auth_data)) 59 }) 60 } 61 } 62 deserializer.deserialize_str(AuthDataVisitor) 63 } 64 } 65 /// `Visitor` for `AuthenticatorAssertion`. 66 /// 67 /// Unknown fields are ignored and only `clientDataJSON`, `authenticatorData`, and `signature` are required iff 68 /// `RELAXED`. 69 pub(super) struct AuthenticatorAssertionVisitor< 70 const RELAXED: bool, 71 const USER_LEN: usize, 72 const DISCOVERABLE: bool, 73 >; 74 impl<'d, const R: bool, const LEN: usize, const DISC: bool> Visitor<'d> 75 for AuthenticatorAssertionVisitor<R, LEN, DISC> 76 where 77 UserHandle<LEN>: Deserialize<'d>, 78 { 79 type Value = AuthenticatorAssertion<LEN, DISC>; 80 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 81 formatter.write_str("AuthenticatorAssertion") 82 } 83 #[expect(clippy::too_many_lines, reason = "107 lines is fine")] 84 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 85 where 86 A: MapAccess<'d>, 87 { 88 /// Fields in `AuthenticatorAssertionResponseJSON`. 89 enum Field<const IGNORE_UNKNOWN: bool> { 90 /// `clientDataJSON`. 91 ClientDataJson, 92 /// `authenticatorData`. 93 AuthenticatorData, 94 /// `signature`. 95 Signature, 96 /// `userHandle`. 97 UserHandle, 98 /// Unknown field. 99 Other, 100 } 101 impl<'e, const I: bool> Deserialize<'e> for Field<I> { 102 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 103 where 104 D: Deserializer<'e>, 105 { 106 /// `Visitor` for `Field`. 107 struct FieldVisitor<const IGNORE_UNKNOWN: bool>; 108 impl<const IG: bool> Visitor<'_> for FieldVisitor<IG> { 109 type Value = Field<IG>; 110 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 111 write!( 112 formatter, 113 "'{CLIENT_DATA_JSON}', '{AUTHENTICATOR_DATA}', '{SIGNATURE}', or '{USER_HANDLE}'" 114 ) 115 } 116 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 117 where 118 E: Error, 119 { 120 match v { 121 CLIENT_DATA_JSON => Ok(Field::ClientDataJson), 122 AUTHENTICATOR_DATA => Ok(Field::AuthenticatorData), 123 SIGNATURE => Ok(Field::Signature), 124 USER_HANDLE => Ok(Field::UserHandle), 125 _ => { 126 if IG { 127 Ok(Field::Other) 128 } else { 129 Err(E::unknown_field(v, AUTH_ASSERT_FIELDS)) 130 } 131 } 132 } 133 } 134 } 135 deserializer.deserialize_identifier(FieldVisitor::<I>) 136 } 137 } 138 let mut client_data = None; 139 let mut auth = None; 140 let mut sig = None; 141 let mut user_handle = None; 142 while let Some(key) = map.next_key::<Field<R>>()? { 143 match key { 144 Field::ClientDataJson => { 145 if client_data.is_some() { 146 return Err(Error::duplicate_field(CLIENT_DATA_JSON)); 147 } 148 client_data = map 149 .next_value::<Base64DecodedVal>() 150 .map(|c_data| c_data.0) 151 .map(Some)?; 152 } 153 Field::AuthenticatorData => { 154 if auth.is_some() { 155 return Err(Error::duplicate_field(AUTHENTICATOR_DATA)); 156 } 157 auth = map 158 .next_value::<AuthData>() 159 .map(|auth_data| Some(auth_data.0))?; 160 } 161 Field::Signature => { 162 if sig.is_some() { 163 return Err(Error::duplicate_field(SIGNATURE)); 164 } 165 sig = map 166 .next_value::<Base64DecodedVal>() 167 .map(|signature| signature.0) 168 .map(Some)?; 169 } 170 Field::UserHandle => { 171 if user_handle.is_some() { 172 return Err(Error::duplicate_field(USER_HANDLE)); 173 } 174 user_handle = map.next_value().map(Some)?; 175 } 176 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 177 } 178 } 179 client_data 180 .ok_or_else(|| Error::missing_field(CLIENT_DATA_JSON)) 181 .and_then(|client_data_json| { 182 auth.ok_or_else(|| Error::missing_field(AUTHENTICATOR_DATA)) 183 .and_then(|authenticator_data| { 184 sig.ok_or_else(|| Error::missing_field(SIGNATURE)) 185 .and_then(|signature| { 186 if DISC { 187 user_handle.ok_or_else(|| Error::missing_field(USER_HANDLE)) 188 } else { 189 user_handle.map_or_else(|| Ok(None), Ok) 190 } 191 .map(|user| { 192 AuthenticatorAssertion::new_inner( 193 client_data_json, 194 authenticator_data, 195 signature, 196 user, 197 ) 198 }) 199 }) 200 }) 201 }) 202 } 203 } 204 /// `"clientDataJSON"`. 205 const CLIENT_DATA_JSON: &str = "clientDataJSON"; 206 /// `"authenticatorData"`. 207 const AUTHENTICATOR_DATA: &str = "authenticatorData"; 208 /// `"signature"`. 209 const SIGNATURE: &str = "signature"; 210 /// `"userHandle"`. 211 const USER_HANDLE: &str = "userHandle"; 212 /// Fields in `AuthenticatorAssertionResponseJSON`. 213 pub(super) const AUTH_ASSERT_FIELDS: &[&str; 4] = 214 &[CLIENT_DATA_JSON, AUTHENTICATOR_DATA, SIGNATURE, USER_HANDLE]; 215 impl<'de, const USER_LEN: usize, const DISCOVERABLE: bool> Deserialize<'de> 216 for AuthenticatorAssertion<USER_LEN, DISCOVERABLE> 217 where 218 UserHandle<USER_LEN>: Deserialize<'de>, 219 { 220 /// Deserializes a `struct` based on 221 /// [`AuthenticatorAssertionResponseJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticatorassertionresponsejson). 222 /// 223 /// Note unknown keys and duplicate keys are forbidden; 224 /// [`clientDataJSON`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorassertionresponsejson-clientdatajson), 225 /// [`authenticatorData`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorassertionresponsejson-authenticatordata), 226 /// and 227 /// [`signature`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorassertionresponsejson-signature) are 228 /// base64url-decoded; 229 /// [`userHandle`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorassertionresponsejson-userhandle) is 230 /// required and must not be `null` iff `DISCOVERABLE`. When it exists and is not `null`, it is deserialized 231 /// via [`UserHandle::deserialize`]. All `required` fields in the `AuthenticatorAssertionResponseJSON` Web IDL 232 /// `dictionary` exist (and are not `null`). 233 #[inline] 234 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 235 where 236 D: Deserializer<'de>, 237 { 238 deserializer.deserialize_struct( 239 "AuthenticatorAssertion", 240 AUTH_ASSERT_FIELDS, 241 AuthenticatorAssertionVisitor::<false, USER_LEN, DISCOVERABLE>, 242 ) 243 } 244 } 245 /// Empty map of client extensions. 246 pub(super) struct ClientExtensionsOutputs; 247 /// `Visitor` for `ClientExtensionsOutputs`. 248 /// 249 /// Unknown fields are ignored iff `RELAXED`. 250 pub(super) struct ClientExtensionsOutputsVisitor<const RELAXED: bool, PRF>( 251 pub PhantomData<fn() -> PRF>, 252 ); 253 impl<'d, const R: bool, P> Visitor<'d> for ClientExtensionsOutputsVisitor<R, P> 254 where 255 P: for<'a> Deserialize<'a>, 256 { 257 type Value = ClientExtensionsOutputs; 258 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 259 formatter.write_str("ClientExtensionsOutputs") 260 } 261 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 262 where 263 A: MapAccess<'d>, 264 { 265 /// Allowed fields. 266 enum Field<const IGNORE_UNKNOWN: bool> { 267 /// `prf`. 268 Prf, 269 /// Unknown field. 270 Other, 271 } 272 impl<'e, const I: bool> Deserialize<'e> for Field<I> { 273 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 274 where 275 D: Deserializer<'e>, 276 { 277 /// `Visitor` for `Field`. 278 /// 279 /// Unknown fields are ignored iff `IGNORE_UNKNOWN`. 280 struct FieldVisitor<const IGNORE_UNKNOWN: bool>; 281 impl<const IG: bool> Visitor<'_> for FieldVisitor<IG> { 282 type Value = Field<IG>; 283 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 284 write!(formatter, "'{PRF}'") 285 } 286 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 287 where 288 E: Error, 289 { 290 match v { 291 PRF => Ok(Field::Prf), 292 _ => { 293 if IG { 294 Ok(Field::Other) 295 } else { 296 Err(E::unknown_field(v, EXT_FIELDS)) 297 } 298 } 299 } 300 } 301 } 302 deserializer.deserialize_identifier(FieldVisitor::<I>) 303 } 304 } 305 let mut prf = None; 306 while let Some(key) = map.next_key::<Field<R>>()? { 307 match key { 308 Field::Prf => { 309 if prf.is_some() { 310 return Err(Error::duplicate_field(PRF)); 311 } 312 prf = map.next_value::<Option<P>>().map(Some)?; 313 } 314 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 315 } 316 } 317 Ok(ClientExtensionsOutputs) 318 } 319 } 320 impl ClientExtensions for ClientExtensionsOutputs { 321 fn empty() -> Self { 322 Self 323 } 324 } 325 /// `"prf"` 326 const PRF: &str = "prf"; 327 /// `AuthenticationExtensionsClientOutputsJSON` fields. 328 pub(super) const EXT_FIELDS: &[&str; 1] = &[PRF]; 329 impl<'de> Deserialize<'de> for ClientExtensionsOutputs { 330 /// Deserializes a `struct` based on 331 /// [`AuthenticationExtensionsClientOutputsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsclientoutputsjson). 332 /// 333 /// Note that unknown and duplicate keys are forbidden and 334 /// [`prf`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsclientoutputs-prf) is `null` 335 /// or deserialized via [`AuthenticationExtensionsPrfOutputs::deserialize`]. 336 #[inline] 337 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 338 where 339 D: Deserializer<'de>, 340 { 341 deserializer.deserialize_struct( 342 "ClientExtensionsOutputs", 343 EXT_FIELDS, 344 ClientExtensionsOutputsVisitor::< 345 false, 346 AuthenticationExtensionsPrfOutputsHelper< 347 false, 348 false, 349 AuthenticationExtensionsPrfValues, 350 >, 351 >(PhantomData), 352 ) 353 } 354 } 355 impl<'de, const USER_LEN: usize, const DISCOVERABLE: bool> Deserialize<'de> 356 for Authentication<USER_LEN, DISCOVERABLE> 357 where 358 UserHandle<USER_LEN>: Deserialize<'de>, 359 { 360 /// Deserializes a `struct` based on 361 /// [`AuthenticationResponseJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationresponsejson). 362 /// 363 /// Note that unknown and duplicate keys are forbidden; 364 /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-id) and 365 /// [`rawId`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-rawid) are deserialized 366 /// via [`CredentialId::deserialize`]; 367 /// [`response`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-response) is deserialized 368 /// via [`AuthenticatorAssertion::deserialize`]; 369 /// [`authenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-authenticatorattachment) 370 /// is `null` or deserialized via [`AuthenticatorAttachment::deserialize`]; 371 /// [`clientExtensionResults`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-clientextensionresults) 372 /// is deserialized such that it is an empty map or a map that only contains 373 /// [`prf`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsclientoutputs-prf) which additionally must be 374 /// `null` or an 375 /// [`AuthenticationExtensionsPRFOutputs`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfoutputs) 376 /// such that unknown and duplicate keys are forbidden, 377 /// [`enabled`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-enabled) 378 /// is forbidden (including being assigned `null`), 379 /// [`results`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-results) must not exist, 380 /// be `null`, or be an 381 /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues) 382 /// with no unknown or duplicate keys, 383 /// [`first`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-first) must exist but be 384 /// `null`, and 385 /// [`second`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfvalues-second) can exist but 386 /// must be `null` if so; all `required` fields in the `AuthenticationResponseJSON` Web IDL `dictionary` exist 387 /// (and are not `null`); [`type`](https://www.w3.org/TR/webauthn-3/#dom-authenticationresponsejson-type) is 388 /// `"public-key"`; and the decoded `id` and decoded `rawId` are the same. 389 #[expect(clippy::unreachable, reason = "when there is a bug, we want to crash")] 390 #[inline] 391 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 392 where 393 D: Deserializer<'de>, 394 { 395 PublicKeyCredential::< 396 false, 397 false, 398 AuthenticatorAssertion<USER_LEN, DISCOVERABLE>, 399 ClientExtensionsOutputs, 400 >::deserialize(deserializer) 401 .map(|cred| Self { 402 raw_id: cred.id.unwrap_or_else(|| { 403 unreachable!("there is a bug in PublicKeyCredential::deserialize") 404 }), 405 response: cred.response, 406 authenticator_attachment: cred.authenticator_attachment, 407 }) 408 } 409 } 410 impl Serialize for UnknownCredentialOptions<'_, '_> { 411 /// Serializes `self` to conform with 412 /// [`UnknownCredentialOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-unknowncredentialoptions). 413 /// 414 /// # Examples 415 /// 416 /// ``` 417 /// # use core::str::FromStr; 418 /// # use webauthn_rp::{request::{AsciiDomain, RpId}, response::{auth::error::UnknownCredentialOptions, CredentialId}}; 419 /// # #[cfg(feature = "custom")] 420 /// let credential_id = CredentialId::try_from(vec![0; 16])?; 421 /// # #[cfg(feature = "custom")] 422 /// assert_eq!( 423 /// serde_json::to_string(&UnknownCredentialOptions { 424 /// rp_id: &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?), 425 /// credential_id: (&credential_id).into(), 426 /// }) 427 /// .unwrap(), 428 /// r#"{"rpId":"example.com","credentialId":"AAAAAAAAAAAAAAAAAAAAAA"}"# 429 /// ); 430 /// # Ok::<_, webauthn_rp::AggErr>(()) 431 /// ``` 432 #[inline] 433 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 434 where 435 S: Serializer, 436 { 437 serializer 438 .serialize_struct("UnknownCredentialOptions", 2) 439 .and_then(|mut ser| { 440 ser.serialize_field("rpId", self.rp_id).and_then(|()| { 441 ser.serialize_field("credentialId", &self.credential_id) 442 .and_then(|()| ser.end()) 443 }) 444 }) 445 } 446 } 447 #[cfg(test)] 448 mod tests { 449 use super::super::{ 450 super::super::request::register::USER_HANDLE_MIN_LEN, AuthenticatorAttachment, 451 DiscoverableAuthentication, NonDiscoverableAuthentication, 452 }; 453 use rsa::sha2::{Digest as _, Sha256}; 454 use serde::de::{Error as _, Unexpected}; 455 use serde_json::Error; 456 #[test] 457 fn eddsa_authentication_deserialize_data_mismatch() { 458 let c_data_json = serde_json::json!({}).to_string(); 459 let auth_data = [ 460 // `rpIdHash`. 461 0, 462 0, 463 0, 464 0, 465 0, 466 0, 467 0, 468 0, 469 0, 470 0, 471 0, 472 0, 473 0, 474 0, 475 0, 476 0, 477 0, 478 0, 479 0, 480 0, 481 0, 482 0, 483 0, 484 0, 485 0, 486 0, 487 0, 488 0, 489 0, 490 0, 491 0, 492 0, 493 // `flags`. 494 0b0000_0101, 495 // `signCount`. 496 0, 497 0, 498 0, 499 0, 500 ]; 501 let b64_cdata = base64url_nopad::encode(c_data_json.as_bytes()); 502 let b64_adata = base64url_nopad::encode(auth_data.as_slice()); 503 let b64_sig = base64url_nopad::encode([].as_slice()); 504 let b64_user = base64url_nopad::encode(b"\x00".as_slice()); 505 // Base case is valid. 506 assert!( 507 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 508 serde_json::json!({ 509 "id": "AAAAAAAAAAAAAAAAAAAAAA", 510 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 511 "response": { 512 "clientDataJSON": b64_cdata, 513 "authenticatorData": b64_adata, 514 "signature": b64_sig, 515 "userHandle": b64_user, 516 }, 517 "authenticatorAttachment": "cross-platform", 518 "clientExtensionResults": {}, 519 "type": "public-key" 520 }) 521 .to_string() 522 .as_str() 523 ) 524 .map_or(false, |auth| auth.response.client_data_json 525 == c_data_json.as_bytes() 526 && auth.response.authenticator_data_and_c_data_hash[..37] == auth_data 527 && auth.response.authenticator_data_and_c_data_hash[37..] 528 == *Sha256::digest(c_data_json.as_bytes()).as_slice() 529 && matches!( 530 auth.authenticator_attachment, 531 AuthenticatorAttachment::CrossPlatform 532 )) 533 ); 534 // `id` and `rawId` mismatch. 535 let mut err = Error::invalid_value( 536 Unexpected::Bytes( 537 base64url_nopad::decode("ABABABABABABABABABABAA".as_bytes()) 538 .unwrap() 539 .as_slice(), 540 ), 541 &format!("id and rawId to match: CredentialId({:?})", [0; 16]).as_str(), 542 ) 543 .to_string() 544 .into_bytes(); 545 assert_eq!( 546 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 547 serde_json::json!({ 548 "id": "AAAAAAAAAAAAAAAAAAAAAA", 549 "rawId": "ABABABABABABABABABABAA", 550 "response": { 551 "clientDataJSON": b64_cdata, 552 "authenticatorData": b64_adata, 553 "signature": b64_sig, 554 "userHandle": b64_user, 555 }, 556 "authenticatorAttachment": "cross-platform", 557 "clientExtensionResults": {}, 558 "type": "public-key" 559 }) 560 .to_string() 561 .as_str() 562 ) 563 .unwrap_err() 564 .to_string() 565 .into_bytes()[..err.len()], 566 err 567 ); 568 // missing `id`. 569 err = Error::missing_field("id").to_string().into_bytes(); 570 assert_eq!( 571 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 572 serde_json::json!({ 573 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 574 "response": { 575 "clientDataJSON": b64_cdata, 576 "authenticatorData": b64_adata, 577 "signature": b64_sig, 578 "userHandle": b64_user, 579 }, 580 "authenticatorAttachment": "cross-platform", 581 "clientExtensionResults": {}, 582 "type": "public-key" 583 }) 584 .to_string() 585 .as_str() 586 ) 587 .unwrap_err() 588 .to_string() 589 .into_bytes()[..err.len()], 590 err 591 ); 592 // `null` `id`. 593 err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId") 594 .to_string() 595 .into_bytes(); 596 assert_eq!( 597 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 598 serde_json::json!({ 599 "id": null, 600 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 601 "response": { 602 "clientDataJSON": b64_cdata, 603 "authenticatorData": b64_adata, 604 "signature": b64_sig, 605 "userHandle": b64_user, 606 }, 607 "clientExtensionResults": {}, 608 "type": "public-key" 609 }) 610 .to_string() 611 .as_str() 612 ) 613 .unwrap_err() 614 .to_string() 615 .into_bytes()[..err.len()], 616 err 617 ); 618 // missing `rawId`. 619 err = Error::missing_field("rawId").to_string().into_bytes(); 620 assert_eq!( 621 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 622 serde_json::json!({ 623 "id": "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 // `null` `rawId`. 642 err = Error::invalid_type(Unexpected::Other("null"), &"CredentialId") 643 .to_string() 644 .into_bytes(); 645 assert_eq!( 646 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 647 serde_json::json!({ 648 "id": "AAAAAAAAAAAAAAAAAAAAAA", 649 "rawId": null, 650 "response": { 651 "clientDataJSON": b64_cdata, 652 "authenticatorData": b64_adata, 653 "signature": b64_sig, 654 "userHandle": b64_user, 655 }, 656 "clientExtensionResults": {}, 657 "type": "public-key" 658 }) 659 .to_string() 660 .as_str() 661 ) 662 .unwrap_err() 663 .to_string() 664 .into_bytes()[..err.len()], 665 err 666 ); 667 // Missing `authenticatorData`. 668 err = Error::missing_field("authenticatorData") 669 .to_string() 670 .into_bytes(); 671 assert_eq!( 672 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 673 serde_json::json!({ 674 "id": "AAAAAAAAAAAAAAAAAAAAAA", 675 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 676 "response": { 677 "clientDataJSON": b64_cdata, 678 "signature": b64_sig, 679 "userHandle": b64_user, 680 }, 681 "clientExtensionResults": {}, 682 "type": "public-key" 683 }) 684 .to_string() 685 .as_str() 686 ) 687 .unwrap_err() 688 .to_string() 689 .into_bytes()[..err.len()], 690 err 691 ); 692 // `null` `authenticatorData`. 693 err = Error::invalid_type(Unexpected::Other("null"), &"AuthenticatorData") 694 .to_string() 695 .into_bytes(); 696 assert_eq!( 697 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 698 serde_json::json!({ 699 "id": "AAAAAAAAAAAAAAAAAAAAAA", 700 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 701 "response": { 702 "clientDataJSON": b64_cdata, 703 "authenticatorData": null, 704 "signature": b64_sig, 705 "userHandle": b64_user, 706 }, 707 "clientExtensionResults": {}, 708 "type": "public-key" 709 }) 710 .to_string() 711 .as_str() 712 ) 713 .unwrap_err() 714 .to_string() 715 .into_bytes()[..err.len()], 716 err 717 ); 718 // Missing `signature`. 719 err = Error::missing_field("signature").to_string().into_bytes(); 720 assert_eq!( 721 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 722 serde_json::json!({ 723 "id": "AAAAAAAAAAAAAAAAAAAAAA", 724 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 725 "response": { 726 "clientDataJSON": b64_cdata, 727 "authenticatorData": b64_adata, 728 "userHandle": b64_user, 729 }, 730 "clientExtensionResults": {}, 731 "type": "public-key" 732 }) 733 .to_string() 734 .as_str() 735 ) 736 .unwrap_err() 737 .to_string() 738 .into_bytes()[..err.len()], 739 err 740 ); 741 // `null` `signature`. 742 err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data") 743 .to_string() 744 .into_bytes(); 745 assert_eq!( 746 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 747 serde_json::json!({ 748 "id": "AAAAAAAAAAAAAAAAAAAAAA", 749 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 750 "response": { 751 "clientDataJSON": b64_cdata, 752 "authenticatorData": b64_adata, 753 "signature": null, 754 "userHandle": b64_user, 755 }, 756 "clientExtensionResults": {}, 757 "type": "public-key" 758 }) 759 .to_string() 760 .as_str() 761 ) 762 .unwrap_err() 763 .to_string() 764 .into_bytes()[..err.len()], 765 err 766 ); 767 // Missing `userHandle`. 768 assert!( 769 serde_json::from_str::<NonDiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 770 serde_json::json!({ 771 "id": "AAAAAAAAAAAAAAAAAAAAAA", 772 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 773 "response": { 774 "clientDataJSON": b64_cdata, 775 "authenticatorData": b64_adata, 776 "signature": b64_sig, 777 }, 778 "clientExtensionResults": {}, 779 "type": "public-key" 780 }) 781 .to_string() 782 .as_str() 783 ) 784 .is_ok() 785 ); 786 // `null` `userHandle`. 787 assert!( 788 serde_json::from_str::<NonDiscoverableAuthentication<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 "userHandle": null, 797 }, 798 "clientExtensionResults": {}, 799 "type": "public-key" 800 }) 801 .to_string() 802 .as_str() 803 ) 804 .is_ok() 805 ); 806 // `null` `authenticatorAttachment`. 807 assert!( 808 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 809 serde_json::json!({ 810 "id": "AAAAAAAAAAAAAAAAAAAAAA", 811 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 812 "response": { 813 "clientDataJSON": b64_cdata, 814 "authenticatorData": b64_adata, 815 "signature": b64_sig, 816 "userHandle": b64_user, 817 }, 818 "authenticatorAttachment": null, 819 "clientExtensionResults": {}, 820 "type": "public-key" 821 }) 822 .to_string() 823 .as_str() 824 ) 825 .map_or(false, |auth| matches!( 826 auth.authenticator_attachment, 827 AuthenticatorAttachment::None 828 )) 829 ); 830 // Unknown `authenticatorAttachment`. 831 err = Error::invalid_value( 832 Unexpected::Str("Platform"), 833 &"'platform' or 'cross-platform'", 834 ) 835 .to_string() 836 .into_bytes(); 837 assert_eq!( 838 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 839 serde_json::json!({ 840 "id": "AAAAAAAAAAAAAAAAAAAAAA", 841 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 842 "response": { 843 "clientDataJSON": b64_cdata, 844 "authenticatorData": b64_adata, 845 "signature": b64_sig, 846 "userHandle": b64_user, 847 }, 848 "authenticatorAttachment": "Platform", 849 "clientExtensionResults": {}, 850 "type": "public-key" 851 }) 852 .to_string() 853 .as_str() 854 ) 855 .unwrap_err() 856 .to_string() 857 .into_bytes()[..err.len()], 858 err 859 ); 860 // Missing `clientDataJSON`. 861 err = Error::missing_field("clientDataJSON") 862 .to_string() 863 .into_bytes(); 864 assert_eq!( 865 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 866 serde_json::json!({ 867 "id": "AAAAAAAAAAAAAAAAAAAAAA", 868 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 869 "response": { 870 "authenticatorData": b64_adata, 871 "signature": b64_sig, 872 "userHandle": b64_user, 873 }, 874 "clientExtensionResults": {}, 875 "type": "public-key" 876 }) 877 .to_string() 878 .as_str() 879 ) 880 .unwrap_err() 881 .to_string() 882 .into_bytes()[..err.len()], 883 err 884 ); 885 // `null` `clientDataJSON`. 886 err = Error::invalid_type(Unexpected::Other("null"), &"base64url-encoded data") 887 .to_string() 888 .into_bytes(); 889 assert_eq!( 890 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 891 serde_json::json!({ 892 "id": "AAAAAAAAAAAAAAAAAAAAAA", 893 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 894 "response": { 895 "clientDataJSON": null, 896 "authenticatorData": b64_adata, 897 "signature": b64_sig, 898 "userHandle": b64_user, 899 }, 900 "clientExtensionResults": {}, 901 "type": "public-key" 902 }) 903 .to_string() 904 .as_str() 905 ) 906 .unwrap_err() 907 .to_string() 908 .into_bytes()[..err.len()], 909 err 910 ); 911 // Missing `response`. 912 err = Error::missing_field("response").to_string().into_bytes(); 913 assert_eq!( 914 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 915 serde_json::json!({ 916 "id": "AAAAAAAAAAAAAAAAAAAAAA", 917 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 918 "clientExtensionResults": {}, 919 "type": "public-key" 920 }) 921 .to_string() 922 .as_str() 923 ) 924 .unwrap_err() 925 .to_string() 926 .into_bytes()[..err.len()], 927 err 928 ); 929 // `null` `response`. 930 err = Error::invalid_type(Unexpected::Other("null"), &"AuthenticatorAssertion") 931 .to_string() 932 .into_bytes(); 933 assert_eq!( 934 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 935 serde_json::json!({ 936 "id": "AAAAAAAAAAAAAAAAAAAAAA", 937 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 938 "response": null, 939 "clientExtensionResults": {}, 940 "type": "public-key" 941 }) 942 .to_string() 943 .as_str() 944 ) 945 .unwrap_err() 946 .to_string() 947 .into_bytes()[..err.len()], 948 err 949 ); 950 // Empty `response`. 951 err = Error::missing_field("clientDataJSON") 952 .to_string() 953 .into_bytes(); 954 assert_eq!( 955 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 956 serde_json::json!({ 957 "id": "AAAAAAAAAAAAAAAAAAAAAA", 958 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 959 "response": {}, 960 "clientExtensionResults": {}, 961 "type": "public-key" 962 }) 963 .to_string() 964 .as_str() 965 ) 966 .unwrap_err() 967 .to_string() 968 .into_bytes()[..err.len()], 969 err 970 ); 971 // Missing `clientExtensionResults`. 972 err = Error::missing_field("clientExtensionResults") 973 .to_string() 974 .into_bytes(); 975 assert_eq!( 976 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 977 serde_json::json!({ 978 "id": "AAAAAAAAAAAAAAAAAAAAAA", 979 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 980 "response": { 981 "clientDataJSON": b64_cdata, 982 "authenticatorData": b64_adata, 983 "signature": b64_sig, 984 "userHandle": b64_user, 985 }, 986 "type": "public-key" 987 }) 988 .to_string() 989 .as_str() 990 ) 991 .unwrap_err() 992 .to_string() 993 .into_bytes()[..err.len()], 994 err 995 ); 996 // `null` `clientExtensionResults`. 997 err = Error::invalid_type( 998 Unexpected::Other("null"), 999 &"clientExtensionResults to be a map of allowed client extensions", 1000 ) 1001 .to_string() 1002 .into_bytes(); 1003 assert_eq!( 1004 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1005 serde_json::json!({ 1006 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1007 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1008 "response": { 1009 "clientDataJSON": b64_cdata, 1010 "authenticatorData": b64_adata, 1011 "signature": b64_sig, 1012 "userHandle": b64_user, 1013 }, 1014 "clientExtensionResults": null, 1015 "type": "public-key" 1016 }) 1017 .to_string() 1018 .as_str() 1019 ) 1020 .unwrap_err() 1021 .to_string() 1022 .into_bytes()[..err.len()], 1023 err 1024 ); 1025 // Missing `type`. 1026 err = Error::missing_field("type").to_string().into_bytes(); 1027 assert_eq!( 1028 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1029 serde_json::json!({ 1030 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1031 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1032 "response": { 1033 "clientDataJSON": b64_cdata, 1034 "authenticatorData": b64_adata, 1035 "signature": b64_sig, 1036 "userHandle": b64_user, 1037 }, 1038 "clientExtensionResults": {}, 1039 }) 1040 .to_string() 1041 .as_str() 1042 ) 1043 .unwrap_err() 1044 .to_string() 1045 .into_bytes()[..err.len()], 1046 err 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::<DiscoverableAuthentication<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::<DiscoverableAuthentication<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::<DiscoverableAuthentication<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::<DiscoverableAuthentication<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 err = Error::unknown_field( 1126 "foo", 1127 [ 1128 "clientDataJSON", 1129 "authenticatorData", 1130 "signature", 1131 "userHandle", 1132 ] 1133 .as_slice(), 1134 ) 1135 .to_string() 1136 .into_bytes(); 1137 assert_eq!( 1138 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1139 serde_json::json!({ 1140 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1141 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1142 "response": { 1143 "clientDataJSON": b64_cdata, 1144 "authenticatorData": b64_adata, 1145 "signature": b64_sig, 1146 "userHandle": b64_user, 1147 "foo": true, 1148 }, 1149 "clientExtensionResults": {}, 1150 "type": "public-key" 1151 }) 1152 .to_string() 1153 .as_str() 1154 ) 1155 .unwrap_err() 1156 .to_string() 1157 .into_bytes()[..err.len()], 1158 err 1159 ); 1160 // Duplicate field in `response`. 1161 err = Error::duplicate_field("userHandle") 1162 .to_string() 1163 .into_bytes(); 1164 assert_eq!( 1165 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1166 format!( 1167 "{{ 1168 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1169 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1170 \"response\": {{ 1171 \"clientDataJSON\": \"{b64_cdata}\", 1172 \"authenticatorData\": \"{b64_adata}\", 1173 \"signature\": \"{b64_sig}\", 1174 \"userHandle\": \"{b64_user}\", 1175 \"userHandle\": \"{b64_user}\" 1176 }}, 1177 \"clientExtensionResults\": {{}}, 1178 \"type\": \"public-key\" 1179 1180 }}" 1181 ) 1182 .as_str() 1183 ) 1184 .unwrap_err() 1185 .to_string() 1186 .into_bytes()[..err.len()], 1187 err 1188 ); 1189 // Unknown field in `PublicKeyCredential`. 1190 err = Error::unknown_field( 1191 "foo", 1192 [ 1193 "id", 1194 "type", 1195 "rawId", 1196 "response", 1197 "authenticatorAttachment", 1198 "clientExtensionResults", 1199 ] 1200 .as_slice(), 1201 ) 1202 .to_string() 1203 .into_bytes(); 1204 assert_eq!( 1205 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1206 serde_json::json!({ 1207 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1208 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1209 "response": { 1210 "clientDataJSON": b64_cdata, 1211 "authenticatorData": b64_adata, 1212 "signature": b64_sig, 1213 "userHandle": b64_user, 1214 }, 1215 "clientExtensionResults": {}, 1216 "type": "public-key", 1217 "foo": true, 1218 }) 1219 .to_string() 1220 .as_str() 1221 ) 1222 .unwrap_err() 1223 .to_string() 1224 .into_bytes()[..err.len()], 1225 err 1226 ); 1227 // Duplicate field in `PublicKeyCredential`. 1228 err = Error::duplicate_field("id").to_string().into_bytes(); 1229 assert_eq!( 1230 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1231 format!( 1232 "{{ 1233 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1234 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1235 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1236 \"response\": {{ 1237 \"clientDataJSON\": \"{b64_cdata}\", 1238 \"authenticatorData\": \"{b64_adata}\", 1239 \"signature\": \"{b64_sig}\", 1240 \"userHandle\": \"{b64_user}\" 1241 }}, 1242 \"clientExtensionResults\": {{}}, 1243 \"type\": \"public-key\" 1244 1245 }}" 1246 ) 1247 .as_str() 1248 ) 1249 .unwrap_err() 1250 .to_string() 1251 .into_bytes()[..err.len()], 1252 err 1253 ); 1254 } 1255 #[test] 1256 fn client_extensions() { 1257 let c_data_json = serde_json::json!({}).to_string(); 1258 let auth_data = [ 1259 // `rpIdHash`. 1260 0, 1261 0, 1262 0, 1263 0, 1264 0, 1265 0, 1266 0, 1267 0, 1268 0, 1269 0, 1270 0, 1271 0, 1272 0, 1273 0, 1274 0, 1275 0, 1276 0, 1277 0, 1278 0, 1279 0, 1280 0, 1281 0, 1282 0, 1283 0, 1284 0, 1285 0, 1286 0, 1287 0, 1288 0, 1289 0, 1290 0, 1291 0, 1292 // `flags`. 1293 0b0000_0101, 1294 // `signCount`. 1295 0, 1296 0, 1297 0, 1298 0, 1299 ]; 1300 let b64_cdata = base64url_nopad::encode(c_data_json.as_bytes()); 1301 let b64_adata = base64url_nopad::encode(auth_data.as_slice()); 1302 let b64_sig = base64url_nopad::encode([].as_slice()); 1303 let b64_user = base64url_nopad::encode(b"\x00".as_slice()); 1304 // Base case is valid. 1305 assert!( 1306 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1307 serde_json::json!({ 1308 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1309 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1310 "response": { 1311 "clientDataJSON": b64_cdata, 1312 "authenticatorData": b64_adata, 1313 "signature": b64_sig, 1314 "userHandle": b64_user, 1315 }, 1316 "authenticatorAttachment": "cross-platform", 1317 "clientExtensionResults": {}, 1318 "type": "public-key" 1319 }) 1320 .to_string() 1321 .as_str() 1322 ) 1323 .map_or(false, |auth| auth.response.client_data_json 1324 == c_data_json.as_bytes() 1325 && auth.response.authenticator_data_and_c_data_hash[..37] == auth_data 1326 && auth.response.authenticator_data_and_c_data_hash[37..] 1327 == *Sha256::digest(c_data_json.as_bytes()).as_slice() 1328 && matches!( 1329 auth.authenticator_attachment, 1330 AuthenticatorAttachment::CrossPlatform 1331 )) 1332 ); 1333 // `null` `prf`. 1334 assert!( 1335 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1336 serde_json::json!({ 1337 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1338 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1339 "response": { 1340 "clientDataJSON": b64_cdata, 1341 "authenticatorData": b64_adata, 1342 "signature": b64_sig, 1343 "userHandle": b64_user, 1344 }, 1345 "clientExtensionResults": { 1346 "prf": null 1347 }, 1348 "type": "public-key" 1349 }) 1350 .to_string() 1351 .as_str() 1352 ) 1353 .is_ok() 1354 ); 1355 // Unknown `clientExtensionResults`. 1356 let mut err = Error::unknown_field("Prf", ["prf"].as_slice()) 1357 .to_string() 1358 .into_bytes(); 1359 assert_eq!( 1360 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1361 serde_json::json!({ 1362 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1363 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1364 "response": { 1365 "clientDataJSON": b64_cdata, 1366 "authenticatorData": b64_adata, 1367 "signature": b64_sig, 1368 "userHandle": b64_user, 1369 }, 1370 "clientExtensionResults": { 1371 "Prf": null 1372 }, 1373 "type": "public-key" 1374 }) 1375 .to_string() 1376 .as_str() 1377 ) 1378 .unwrap_err() 1379 .to_string() 1380 .into_bytes()[..err.len()], 1381 err 1382 ); 1383 // Duplicate field. 1384 err = Error::duplicate_field("prf").to_string().into_bytes(); 1385 assert_eq!( 1386 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1387 format!( 1388 "{{ 1389 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1390 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1391 \"response\": {{ 1392 \"clientDataJSON\": \"{b64_cdata}\", 1393 \"authenticatorData\": \"{b64_adata}\", 1394 \"signature\": \"{b64_sig}\", 1395 \"userHandle\": \"{b64_user}\" 1396 }}, 1397 \"clientExtensionResults\": {{ 1398 \"prf\": null, 1399 \"prf\": null 1400 }}, 1401 \"type\": \"public-key\" 1402 }}" 1403 ) 1404 .as_str() 1405 ) 1406 .unwrap_err() 1407 .to_string() 1408 .into_bytes()[..err.len()], 1409 err 1410 ); 1411 // `null` `results`. 1412 assert!( 1413 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1414 serde_json::json!({ 1415 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1416 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1417 "response": { 1418 "clientDataJSON": b64_cdata, 1419 "authenticatorData": b64_adata, 1420 "signature": b64_sig, 1421 "userHandle": b64_user, 1422 }, 1423 "clientExtensionResults": { 1424 "prf": { 1425 "results": null, 1426 } 1427 }, 1428 "type": "public-key" 1429 }) 1430 .to_string() 1431 .as_str() 1432 ) 1433 .is_ok() 1434 ); 1435 // Duplicate field in `prf`. 1436 err = Error::duplicate_field("results").to_string().into_bytes(); 1437 assert_eq!( 1438 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1439 format!( 1440 "{{ 1441 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1442 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1443 \"response\": {{ 1444 \"clientDataJSON\": \"{b64_cdata}\", 1445 \"authenticatorData\": \"{b64_adata}\", 1446 \"signature\": \"{b64_sig}\", 1447 \"userHandle\": \"{b64_user}\" 1448 }}, 1449 \"clientExtensionResults\": {{ 1450 \"prf\": {{ 1451 \"results\": null, 1452 \"results\": null 1453 }} 1454 }}, 1455 \"type\": \"public-key\" 1456 }}" 1457 ) 1458 .as_str() 1459 ) 1460 .unwrap_err() 1461 .to_string() 1462 .into_bytes()[..err.len()], 1463 err 1464 ); 1465 // Missing `first`. 1466 err = Error::missing_field("first").to_string().into_bytes(); 1467 assert_eq!( 1468 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1469 serde_json::json!({ 1470 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1471 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1472 "response": { 1473 "clientDataJSON": b64_cdata, 1474 "authenticatorData": b64_adata, 1475 "signature": b64_sig, 1476 "userHandle": b64_user, 1477 }, 1478 "clientExtensionResults": { 1479 "prf": { 1480 "results": {}, 1481 } 1482 }, 1483 "type": "public-key" 1484 }) 1485 .to_string() 1486 .as_str() 1487 ) 1488 .unwrap_err() 1489 .to_string() 1490 .into_bytes()[..err.len()], 1491 err 1492 ); 1493 // `null` `first`. 1494 assert!( 1495 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1496 serde_json::json!({ 1497 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1498 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1499 "response": { 1500 "clientDataJSON": b64_cdata, 1501 "authenticatorData": b64_adata, 1502 "signature": b64_sig, 1503 "userHandle": b64_user, 1504 }, 1505 "clientExtensionResults": { 1506 "prf": { 1507 "results": { 1508 "first": null 1509 }, 1510 } 1511 }, 1512 "type": "public-key" 1513 }) 1514 .to_string() 1515 .as_str() 1516 ) 1517 .is_ok() 1518 ); 1519 // `null` `second`. 1520 assert!( 1521 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1522 serde_json::json!({ 1523 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1524 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1525 "response": { 1526 "clientDataJSON": b64_cdata, 1527 "authenticatorData": b64_adata, 1528 "signature": b64_sig, 1529 "userHandle": b64_user, 1530 }, 1531 "clientExtensionResults": { 1532 "prf": { 1533 "results": { 1534 "first": null, 1535 "second": null 1536 }, 1537 } 1538 }, 1539 "type": "public-key" 1540 }) 1541 .to_string() 1542 .as_str() 1543 ) 1544 .is_ok() 1545 ); 1546 // Non-`null` `first`. 1547 err = Error::invalid_type(Unexpected::Option, &"null") 1548 .to_string() 1549 .into_bytes(); 1550 assert_eq!( 1551 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1552 serde_json::json!({ 1553 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1554 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1555 "response": { 1556 "clientDataJSON": b64_cdata, 1557 "authenticatorData": b64_adata, 1558 "signature": b64_sig, 1559 "userHandle": b64_user, 1560 }, 1561 "clientExtensionResults": { 1562 "prf": { 1563 "results": { 1564 "first": "" 1565 }, 1566 } 1567 }, 1568 "type": "public-key" 1569 }) 1570 .to_string() 1571 .as_str() 1572 ) 1573 .unwrap_err() 1574 .to_string() 1575 .into_bytes()[..err.len()], 1576 err 1577 ); 1578 // Non-`null` `second`. 1579 err = Error::invalid_type(Unexpected::Option, &"null") 1580 .to_string() 1581 .into_bytes(); 1582 assert_eq!( 1583 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1584 serde_json::json!({ 1585 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1586 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1587 "response": { 1588 "clientDataJSON": b64_cdata, 1589 "authenticatorData": b64_adata, 1590 "signature": b64_sig, 1591 "userHandle": b64_user, 1592 }, 1593 "clientExtensionResults": { 1594 "prf": { 1595 "results": { 1596 "first": null, 1597 "second": "" 1598 }, 1599 } 1600 }, 1601 "type": "public-key" 1602 }) 1603 .to_string() 1604 .as_str() 1605 ) 1606 .unwrap_err() 1607 .to_string() 1608 .into_bytes()[..err.len()], 1609 err 1610 ); 1611 // Unknown `prf` field. 1612 err = Error::unknown_field("enabled", ["results"].as_slice()) 1613 .to_string() 1614 .into_bytes(); 1615 assert_eq!( 1616 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1617 serde_json::json!({ 1618 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1619 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1620 "response": { 1621 "clientDataJSON": b64_cdata, 1622 "authenticatorData": b64_adata, 1623 "signature": b64_sig, 1624 "userHandle": b64_user, 1625 }, 1626 "clientExtensionResults": { 1627 "prf": { 1628 "enabled": true, 1629 "results": null 1630 } 1631 }, 1632 "type": "public-key" 1633 }) 1634 .to_string() 1635 .as_str() 1636 ) 1637 .unwrap_err() 1638 .to_string() 1639 .into_bytes()[..err.len()], 1640 err 1641 ); 1642 // Unknown `results` field. 1643 err = Error::unknown_field("Second", ["first", "second"].as_slice()) 1644 .to_string() 1645 .into_bytes(); 1646 assert_eq!( 1647 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1648 serde_json::json!({ 1649 "id": "AAAAAAAAAAAAAAAAAAAAAA", 1650 "rawId": "AAAAAAAAAAAAAAAAAAAAAA", 1651 "response": { 1652 "clientDataJSON": b64_cdata, 1653 "authenticatorData": b64_adata, 1654 "signature": b64_sig, 1655 "userHandle": b64_user, 1656 }, 1657 "clientExtensionResults": { 1658 "prf": { 1659 "results": { 1660 "first": null, 1661 "Second": null 1662 } 1663 } 1664 }, 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 in `results`. 1676 err = Error::duplicate_field("first").to_string().into_bytes(); 1677 assert_eq!( 1678 serde_json::from_str::<DiscoverableAuthentication<USER_HANDLE_MIN_LEN>>( 1679 format!( 1680 "{{ 1681 \"id\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1682 \"rawId\": \"AAAAAAAAAAAAAAAAAAAAAA\", 1683 \"response\": {{ 1684 \"clientDataJSON\": \"{b64_cdata}\", 1685 \"authenticatorData\": \"{b64_adata}\", 1686 \"signature\": \"{b64_sig}\", 1687 \"userHandle\": \"{b64_user}\" 1688 }}, 1689 \"clientExtensionResults\": {{ 1690 \"prf\": {{ 1691 \"results\": {{ 1692 \"first\": null, 1693 \"first\": null 1694 }} 1695 }} 1696 }}, 1697 \"type\": \"public-key\" 1698 }}" 1699 ) 1700 .as_str() 1701 ) 1702 .unwrap_err() 1703 .to_string() 1704 .into_bytes()[..err.len()], 1705 err 1706 ); 1707 } 1708 }