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