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