ser.rs (48738B)
1 #![expect( 2 clippy::question_mark_used, 3 reason = "noisy, opinionated, and likely doesn't prevent bugs or improve APIs" 4 )] 5 extern crate alloc; 6 use super::{ 7 AllAcceptedCredentialsOptions, AuthTransports, AuthenticatorAttachment, AuthenticatorTransport, 8 Challenge, CredentialId, CurrentUserDetailsOptions, Origin, SentChallenge, BASE64URL_NOPAD_ENC, 9 }; 10 use alloc::borrow::Cow; 11 use core::{ 12 fmt::{self, Formatter}, 13 marker::PhantomData, 14 }; 15 #[cfg(doc)] 16 use data_encoding::BASE64URL_NOPAD; 17 use serde::{ 18 de::{Deserialize, Deserializer, Error, IgnoredAny, MapAccess, SeqAccess, Unexpected, Visitor}, 19 ser::{Serialize, SerializeSeq as _, SerializeStruct as _, Serializer}, 20 }; 21 /// [`"ble"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-ble). 22 const BLE: &str = "ble"; 23 /// [`"hybrid"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-hybrid). 24 const HYBRID: &str = "hybrid"; 25 /// [`"internal"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-internal). 26 const INTERNAL: &str = "internal"; 27 /// [`"nfc"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-nfc). 28 const NFC: &str = "nfc"; 29 /// [`"smart-card"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-smart-card). 30 const SMART_CARD: &str = "smart-card"; 31 /// [`"usb"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-usb). 32 const USB: &str = "usb"; 33 impl Serialize for AuthenticatorTransport { 34 /// Serializes `self` as 35 /// [`AuthenticatorTransport`](https://www.w3.org/TR/webauthn-3/#enumdef-authenticatortransport). 36 /// 37 /// # Examples 38 /// 39 /// ``` 40 /// # use webauthn_rp::response::AuthenticatorTransport; 41 /// assert_eq!( 42 /// serde_json::to_string(&AuthenticatorTransport::Usb)?, 43 /// r#""usb""# 44 /// ); 45 /// # Ok::<_, serde_json::Error>(()) 46 /// ``` 47 #[inline] 48 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 49 where 50 S: Serializer, 51 { 52 serializer.serialize_str(match *self { 53 Self::Ble => BLE, 54 Self::Hybrid => HYBRID, 55 Self::Internal => INTERNAL, 56 Self::Nfc => NFC, 57 Self::SmartCard => SMART_CARD, 58 Self::Usb => USB, 59 }) 60 } 61 } 62 impl<'de> Deserialize<'de> for AuthenticatorTransport { 63 /// Deserializes [`prim@str`] based on 64 /// [`AuthenticatorTransport`](https://www.w3.org/TR/webauthn-3/#enumdef-authenticatortransport). 65 /// 66 /// # Examples 67 /// 68 /// ``` 69 /// # use webauthn_rp::response::AuthenticatorTransport; 70 /// assert!(matches!( 71 /// serde_json::from_str::<AuthenticatorTransport>(r#""usb""#)?, 72 /// AuthenticatorTransport::Usb 73 /// )); 74 /// // Case matters. 75 /// assert!(serde_json::from_str::<AuthenticatorTransport>(r#""Usb""#).is_err()); 76 /// # Ok::<_, serde_json::Error>(()) 77 /// ``` 78 #[inline] 79 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 80 where 81 D: Deserializer<'de>, 82 { 83 /// `Visitor` for `AuthenticatorTransport`. 84 struct AuthenticatorTransportVisitor; 85 impl Visitor<'_> for AuthenticatorTransportVisitor { 86 type Value = AuthenticatorTransport; 87 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 88 formatter.write_str("AuthenticatorTransport") 89 } 90 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 91 where 92 E: Error, 93 { 94 match v { 95 BLE => Ok(AuthenticatorTransport::Ble), 96 HYBRID => Ok(AuthenticatorTransport::Hybrid), 97 INTERNAL => Ok(AuthenticatorTransport::Internal), 98 NFC => Ok(AuthenticatorTransport::Nfc), 99 SMART_CARD => Ok(AuthenticatorTransport::SmartCard), 100 USB => Ok(AuthenticatorTransport::Usb), 101 _ => Err(E::invalid_value( 102 Unexpected::Str(v), 103 &format!("'{BLE}', '{HYBRID}', '{INTERNAL}', '{NFC}', '{SMART_CARD}', or '{USB}'").as_str(), 104 )), 105 } 106 } 107 } 108 deserializer.deserialize_str(AuthenticatorTransportVisitor) 109 } 110 } 111 impl Serialize for AuthTransports { 112 /// Serializes `self` based on 113 /// [`transports`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-transports). 114 /// 115 /// # Examples 116 /// 117 /// ``` 118 /// # use webauthn_rp::response::{AuthTransports, AuthenticatorTransport}; 119 /// # #[cfg(feature = "custom")] 120 /// assert_eq!( 121 /// serde_json::to_string(&AuthTransports::ALL)?, 122 /// r#"["ble","hybrid","internal","nfc","smart-card","usb"]"# 123 /// ); 124 /// # Ok::<_, serde_json::Error>(()) 125 /// ``` 126 #[expect(clippy::unreachable, reason = "there is a bug, so we want to crash")] 127 #[inline] 128 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 129 where 130 S: Serializer, 131 { 132 let count = usize::try_from(self.count()) 133 .unwrap_or_else(|_e| unreachable!("there is a bug in AuthenticatorTransports::count")); 134 serializer.serialize_seq(Some(count)).and_then(|mut ser| { 135 if self.contains(AuthenticatorTransport::Ble) { 136 ser.serialize_element(&AuthenticatorTransport::Ble) 137 } else { 138 Ok(()) 139 } 140 .and_then(|()| { 141 if self.contains(AuthenticatorTransport::Hybrid) { 142 ser.serialize_element(&AuthenticatorTransport::Hybrid) 143 } else { 144 Ok(()) 145 } 146 .and_then(|()| { 147 if self.contains(AuthenticatorTransport::Internal) { 148 ser.serialize_element(&AuthenticatorTransport::Internal) 149 } else { 150 Ok(()) 151 } 152 .and_then(|()| { 153 if self.contains(AuthenticatorTransport::Nfc) { 154 ser.serialize_element(&AuthenticatorTransport::Nfc) 155 } else { 156 Ok(()) 157 } 158 .and_then(|()| { 159 if self.contains(AuthenticatorTransport::SmartCard) { 160 ser.serialize_element(&AuthenticatorTransport::SmartCard) 161 } else { 162 Ok(()) 163 } 164 .and_then(|()| { 165 if self.contains(AuthenticatorTransport::Usb) { 166 ser.serialize_element(&AuthenticatorTransport::Usb) 167 } else { 168 Ok(()) 169 } 170 .and_then(|()| ser.end()) 171 }) 172 }) 173 }) 174 }) 175 }) 176 }) 177 } 178 } 179 impl<'de> Deserialize<'de> for AuthTransports { 180 /// Deserializes a sequence based on 181 /// [`transports`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-transports). 182 /// 183 /// # Examples 184 /// 185 /// ``` 186 /// # use webauthn_rp::response::{AuthTransports, AuthenticatorTransport}; 187 /// # #[cfg(feature = "custom")] 188 /// assert_eq!( 189 /// serde_json::from_str::<AuthTransports>( 190 /// r#"["ble","hybrid","internal","nfc","smart-card","usb"]"# 191 /// ) 192 /// ?.count(), 193 /// 6 194 /// ); 195 /// // Errors since `"foo"` is not valid. 196 /// assert!(serde_json::from_str::<AuthTransports>(r#"["foo"]"#).is_err()); 197 /// # Ok::<_, serde_json::Error>(()) 198 /// ``` 199 #[inline] 200 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 201 where 202 D: Deserializer<'de>, 203 { 204 /// `Visitor` for `AuthTransports`. 205 struct AuthTransportsVisitor; 206 impl<'d> Visitor<'d> for AuthTransportsVisitor { 207 type Value = AuthTransports; 208 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 209 formatter.write_str("AuthTransports") 210 } 211 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> 212 where 213 A: SeqAccess<'d>, 214 { 215 let mut transports = AuthTransports::new(); 216 while let Some(val) = seq.next_element::<AuthenticatorTransport>()? { 217 transports = transports.add_transport(val); 218 } 219 Ok(transports) 220 } 221 } 222 deserializer.deserialize_seq(AuthTransportsVisitor) 223 } 224 } 225 impl<T: AsRef<[u8]>> Serialize for CredentialId<T> { 226 /// Serializes `self` into a [`prim@str`] based on 227 /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptorjson-id). 228 /// 229 /// # Examples 230 /// 231 /// ``` 232 /// # use webauthn_rp::response::CredentialId; 233 /// // `CredentialId::try_from` only exists when `custom` is enabled; and even then, it is 234 /// // likely never needed since the `CredentialId` was originally sent from the client and is likely 235 /// // stored in a database which would be fetched by `UserHandle` or `Authentication::raw_id`. 236 /// # #[cfg(feature = "custom")] 237 /// assert_eq!( 238 /// serde_json::to_string(&CredentialId::try_from(vec![0; 16])?).unwrap(), 239 /// r#""AAAAAAAAAAAAAAAAAAAAAA""# 240 /// ); 241 /// # Ok::<_, webauthn_rp::AggErr>(()) 242 ///``` 243 #[inline] 244 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 245 where 246 S: Serializer, 247 { 248 serializer.serialize_str(BASE64URL_NOPAD_ENC.encode(self.0.as_ref()).as_str()) 249 } 250 } 251 impl<'de> Deserialize<'de> for CredentialId<Vec<u8>> { 252 /// Deserializes [`prim@str`] based on 253 /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptorjson-id). 254 /// 255 /// # Examples 256 /// 257 /// ``` 258 /// # use webauthn_rp::response::CredentialId; 259 /// # #[cfg(feature = "custom")] 260 /// assert_eq!( 261 /// serde_json::from_str::<CredentialId<_>>(r#""AAAAAAAAAAAAAAAAAAAAAA""#).unwrap(), 262 /// CredentialId::try_from(vec![0; 16])? 263 /// ); 264 /// # Ok::<_, webauthn_rp::AggErr>(()) 265 ///``` 266 #[inline] 267 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 268 where 269 D: Deserializer<'de>, 270 { 271 /// `Visitor` for `CredentialId`. 272 struct CredentialIdVisitor; 273 impl Visitor<'_> for CredentialIdVisitor { 274 type Value = CredentialId<Vec<u8>>; 275 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 276 formatter.write_str("CredentialId") 277 } 278 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 279 where 280 E: Error, 281 { 282 // `CRED_ID_MIN_LEN` and `CRED_ID_MAX_LEN` are less than 283 // `0x4000`, so this won't `panic`. 284 if (crate::base64url_nopad_len(super::CRED_ID_MIN_LEN) 285 ..=crate::base64url_nopad_len(super::CRED_ID_MAX_LEN)) 286 .contains(&v.len()) 287 { 288 BASE64URL_NOPAD_ENC 289 .decode(v.as_bytes()) 290 .map_err(E::custom) 291 .map(CredentialId) 292 } else { 293 Err(E::invalid_value( 294 Unexpected::Str(v), 295 &"16 to 1023 bytes encoded in base64url without padding", 296 )) 297 } 298 } 299 } 300 deserializer.deserialize_str(CredentialIdVisitor) 301 } 302 } 303 impl<'de> Deserialize<'de> for AuthenticatorAttachment { 304 /// Deserializes [`prim@str`] based on 305 /// [`AuthenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#enumdef-authenticatorattachment). 306 /// 307 /// # Examples 308 /// 309 /// ``` 310 /// # use webauthn_rp::response::AuthenticatorAttachment; 311 /// assert!(matches!( 312 /// serde_json::from_str::<AuthenticatorAttachment>(r#""cross-platform""#)?, 313 /// AuthenticatorAttachment::CrossPlatform) 314 /// ); 315 /// assert!(matches!( 316 /// serde_json::from_str::<AuthenticatorAttachment>(r#""platform""#)?, 317 /// AuthenticatorAttachment::Platform) 318 /// ); 319 /// // Case matters. 320 /// assert!(serde_json::from_str::<AuthenticatorAttachment>(r#""Platform""#).is_err()); 321 /// // `AuthenticatorAttachment::None` is not deserializable. 322 /// assert!(serde_json::from_str::<AuthenticatorAttachment>(r#""""#).is_err()); 323 /// assert!(serde_json::from_str::<AuthenticatorAttachment>("null").is_err()); 324 /// assert!(serde_json::from_str::<AuthenticatorAttachment>(r#""none""#).is_err()); 325 /// assert!(serde_json::from_str::<AuthenticatorAttachment>(r#""None""#).is_err()); 326 /// # Ok::<_, serde_json::Error>(()) 327 ///``` 328 #[inline] 329 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 330 where 331 D: Deserializer<'de>, 332 { 333 /// `Visitor` for `AuthenticatorAttachment`. 334 struct AuthenticatorAttachmentVisitor; 335 impl Visitor<'_> for AuthenticatorAttachmentVisitor { 336 type Value = AuthenticatorAttachment; 337 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 338 formatter.write_str("AuthenticatorAttachment") 339 } 340 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 341 where 342 E: Error, 343 { 344 /// `"platform"` 345 const PLATFORM: &str = "platform"; 346 /// `"cross-platform"` 347 const CROSS_PLATFORM: &str = "cross-platform"; 348 match v { 349 PLATFORM => Ok(AuthenticatorAttachment::Platform), 350 CROSS_PLATFORM => Ok(AuthenticatorAttachment::CrossPlatform), 351 _ => Err(E::invalid_value( 352 Unexpected::Str(v), 353 &format!("'{PLATFORM}' or '{CROSS_PLATFORM}'").as_str(), 354 )), 355 } 356 } 357 } 358 deserializer.deserialize_str(AuthenticatorAttachmentVisitor) 359 } 360 } 361 /// Container of data that was encoded in base64url. 362 pub(super) struct Base64DecodedVal(pub Vec<u8>); 363 impl<'de> Deserialize<'de> for Base64DecodedVal { 364 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 365 where 366 D: Deserializer<'de>, 367 { 368 /// `Visitor` for `Base64DecodedVal`. 369 struct Base64DecodedValVisitor; 370 impl Visitor<'_> for Base64DecodedValVisitor { 371 type Value = Base64DecodedVal; 372 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 373 formatter.write_str("base64url-encoded data") 374 } 375 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 376 where 377 E: Error, 378 { 379 BASE64URL_NOPAD_ENC 380 .decode(v.as_bytes()) 381 .map_err(E::custom) 382 .map(Base64DecodedVal) 383 } 384 } 385 deserializer.deserialize_str(Base64DecodedValVisitor) 386 } 387 } 388 impl<'de> Deserialize<'de> for SentChallenge { 389 /// Deserializes `[u8]` or [`prim@str`] based on 390 /// [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-collectedclientdata-challenge). 391 /// 392 /// Specifically a `[u8]` or `str` is base64url-decoded and interpreted as a little-endian 393 /// `u128`. 394 /// 395 /// # Examples 396 /// 397 /// ``` 398 /// # use webauthn_rp::response::SentChallenge; 399 /// assert_eq!( 400 /// serde_json::from_slice::<SentChallenge>(br#""AAAAAAAAAAAAAAAAAAAAAA""#)?, 401 /// SentChallenge(0) 402 /// ); 403 /// # Ok::<_, serde_json::Error>(()) 404 ///``` 405 #[inline] 406 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 407 where 408 D: Deserializer<'de>, 409 { 410 /// `Visitor` for `SentChallenge`. 411 struct ChallengeVisitor; 412 impl Visitor<'_> for ChallengeVisitor { 413 type Value = SentChallenge; 414 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 415 formatter.write_str( 416 "base64 encoding of the 16-byte challenge in a URL safe way without padding", 417 ) 418 } 419 #[expect( 420 clippy::panic_in_result_fn, 421 reason = "we want to crash when there is a bug" 422 )] 423 #[expect( 424 clippy::little_endian_bytes, 425 reason = "SentChallenge::deserialize and Challenge::serialize need to be consistent across architectures" 426 )] 427 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> 428 where 429 E: Error, 430 { 431 // We try to avoid decoding when possible by at least ensuring the input length is correct. 432 if v.len() == Challenge::BASE64_LEN { 433 let mut data = [0; 16]; 434 BASE64URL_NOPAD_ENC 435 .decode_mut(v, data.as_mut_slice()) 436 .map_err(|err| E::custom(err.error)) 437 .map(|len| { 438 assert_eq!(len, 16, "there is a bug in BASE64URL_NOPAD::decode_mut"); 439 SentChallenge(u128::from_le_bytes(data)) 440 }) 441 } else { 442 Err(E::invalid_value( 443 Unexpected::Bytes(v), 444 &"22 bytes encoded in base64url without padding", 445 )) 446 } 447 } 448 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 449 where 450 E: Error, 451 { 452 self.visit_bytes(v.as_bytes()) 453 } 454 } 455 deserializer.deserialize_bytes(ChallengeVisitor) 456 } 457 } 458 impl<'de: 'a, 'a> Deserialize<'de> for Origin<'a> { 459 /// Deserializes [`prim@str`] by borrowing the data when possible. 460 /// 461 /// # Examples 462 /// 463 /// ``` 464 /// # extern crate alloc; 465 /// # use alloc::borrow::Cow; 466 /// # use webauthn_rp::response::Origin; 467 /// let origin_borrowed = "https://example.com"; 468 /// let origin_owned = "\\\\https://example.com"; 469 /// assert!( 470 /// matches!(serde_json::from_str::<Origin<'_>>(format!("\"{origin_borrowed}\"").as_str())?.0, Cow::Borrowed(val) if val == origin_borrowed) 471 /// ); 472 /// assert!( 473 /// matches!(serde_json::from_str::<Origin<'_>>(format!("\"{origin_owned}\"").as_str())?.0, Cow::Owned(val) if *val.as_bytes() == origin_owned.as_bytes()[1..]) 474 /// ); 475 /// # Ok::<_, serde_json::Error>(()) 476 ///``` 477 #[inline] 478 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 479 where 480 D: Deserializer<'de>, 481 { 482 /// `Visitor` for `Origin`. 483 struct OriginVisitor<'b>(PhantomData<fn() -> &'b ()>); 484 impl<'d: 'b, 'b> Visitor<'d> for OriginVisitor<'b> { 485 type Value = Origin<'b>; 486 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 487 formatter.write_str("Origin") 488 } 489 fn visit_borrowed_str<E>(self, v: &'d str) -> Result<Self::Value, E> 490 where 491 E: Error, 492 { 493 Ok(Origin(Cow::Borrowed(v))) 494 } 495 fn visit_string<E>(self, v: String) -> Result<Self::Value, E> 496 where 497 E: Error, 498 { 499 Ok(Origin(Cow::Owned(v))) 500 } 501 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 502 where 503 E: Error, 504 { 505 self.visit_string(v.to_owned()) 506 } 507 } 508 deserializer.deserialize_str(OriginVisitor(PhantomData)) 509 } 510 } 511 /// `trait` that returns an empty instance of `Self`. 512 pub(super) trait ClientExtensions: Sized { 513 /// Returns an empty instance of `Self`. 514 fn empty() -> Self; 515 } 516 /// Response for both registration and authentication ceremonies. 517 /// 518 /// [`Self::raw_id`] is always `Some` when `!RELAXED` or `!REG`. 519 /// 520 /// `RELAXED` and `REG` are used purely for deserialization purposes. 521 pub(super) struct PublicKeyCredential<const RELAXED: bool, const REG: bool, AuthResp, Ext> { 522 /// [`rawId`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-rawid). 523 pub id: Option<CredentialId<Vec<u8>>>, 524 /// [`response`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-response). 525 pub response: AuthResp, 526 /// [`authenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-authenticatorattachment). 527 pub authenticator_attachment: AuthenticatorAttachment, 528 /// [`getClientExtensionResults`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-getclientextensionresults). 529 pub client_extension_results: Ext, 530 } 531 /// `Visitor` for `PublicKeyCredential`. 532 /// 533 /// When `!RELAXED`, `REG` is ignored and all fields must exist and unknown fields are not allowed. 534 /// When `RELAXED`, unknown fields are ignored. 535 /// When `RELAXED` and `REG`, only `response` is required. 536 /// When `RELAXED` and `!REG`, only `id` and `response` are required. 537 struct PublicKeyCredentialVisitor<const RELAXED: bool, const REG: bool, R, E>( 538 pub PhantomData<fn() -> (R, E)>, 539 ); 540 impl<'d, const REL: bool, const REGI: bool, R, E> Visitor<'d> 541 for PublicKeyCredentialVisitor<REL, REGI, R, E> 542 where 543 R: for<'a> Deserialize<'a>, 544 E: for<'a> Deserialize<'a> + ClientExtensions, 545 { 546 type Value = PublicKeyCredential<REL, REGI, R, E>; 547 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 548 formatter.write_str("PublicKeyCredential") 549 } 550 #[expect( 551 clippy::too_many_lines, 552 reason = "rather hide all the internal logic instead instead of moving into an outer scope" 553 )] 554 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 555 where 556 A: MapAccess<'d>, 557 { 558 /// `PublicKeyCredentialJSON` fields. 559 enum Field<const IGNORE_UNKNOWN: bool> { 560 /// `id`. 561 Id, 562 /// `type`. 563 Type, 564 /// `rawId`. 565 RawId, 566 /// `response`. 567 Response, 568 /// `authenticatorAttachment`. 569 AuthenticatorAttachment, 570 /// `clientExtensionResults`. 571 ClientExtensionResults, 572 /// Unknown field. 573 Other, 574 } 575 impl<'e, const IGNORE: bool> Deserialize<'e> for Field<IGNORE> { 576 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 577 where 578 D: Deserializer<'e>, 579 { 580 /// `Visitor` for `Field`. 581 struct FieldVisitor<const IGNORE_UNKNOWN: bool>; 582 impl<const IGN: bool> Visitor<'_> for FieldVisitor<IGN> { 583 type Value = Field<IGN>; 584 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 585 write!(formatter, "'{ID}', '{TYPE}', '{RAW_ID}', '{RESPONSE}', '{AUTHENTICATOR_ATTACHMENT}', or '{CLIENT_EXTENSION_RESULTS}'") 586 } 587 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 588 where 589 E: Error, 590 { 591 match v { 592 ID => Ok(Field::Id), 593 TYPE => Ok(Field::Type), 594 RAW_ID => Ok(Field::RawId), 595 RESPONSE => Ok(Field::Response), 596 AUTHENTICATOR_ATTACHMENT => Ok(Field::AuthenticatorAttachment), 597 CLIENT_EXTENSION_RESULTS => Ok(Field::ClientExtensionResults), 598 _ => { 599 if IGN { 600 Ok(Field::Other) 601 } else { 602 Err(E::unknown_field(v, REG_FIELDS)) 603 } 604 } 605 } 606 } 607 } 608 deserializer.deserialize_identifier(FieldVisitor::<IGNORE>) 609 } 610 } 611 /// Deserializes the value for type. 612 struct Type; 613 impl<'e> Deserialize<'e> for Type { 614 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 615 where 616 D: Deserializer<'e>, 617 { 618 /// `Visitor` for `Type`. 619 struct TypeVisitor; 620 impl Visitor<'_> for TypeVisitor { 621 type Value = Type; 622 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 623 formatter.write_str(PUBLIC_KEY) 624 } 625 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 626 where 627 E: Error, 628 { 629 if v == PUBLIC_KEY { 630 Ok(Type) 631 } else { 632 Err(E::invalid_value(Unexpected::Str(v), &PUBLIC_KEY)) 633 } 634 } 635 } 636 deserializer.deserialize_str(TypeVisitor) 637 } 638 } 639 let mut opt_id = None; 640 let mut typ = None; 641 let mut raw = None; 642 let mut resp = None; 643 let mut attach = None; 644 let mut ext = None; 645 while let Some(key) = map.next_key::<Field<REL>>()? { 646 match key { 647 Field::Id => { 648 if opt_id.is_some() { 649 return Err(Error::duplicate_field(ID)); 650 } 651 opt_id = map.next_value::<Option<_>>().map(Some)?; 652 } 653 Field::Type => { 654 if typ.is_some() { 655 return Err(Error::duplicate_field(TYPE)); 656 } 657 typ = map.next_value::<Option<Type>>().map(Some)?; 658 } 659 Field::RawId => { 660 if raw.is_some() { 661 return Err(Error::duplicate_field(RAW_ID)); 662 } 663 raw = map.next_value::<Option<CredentialId<_>>>().map(Some)?; 664 } 665 Field::Response => { 666 if resp.is_some() { 667 return Err(Error::duplicate_field(RESPONSE)); 668 } 669 resp = map.next_value::<R>().map(Some)?; 670 } 671 Field::AuthenticatorAttachment => { 672 if attach.is_some() { 673 return Err(Error::duplicate_field(AUTHENTICATOR_ATTACHMENT)); 674 } 675 attach = map.next_value().map(Some)?; 676 } 677 Field::ClientExtensionResults => { 678 if ext.is_some() { 679 return Err(Error::duplicate_field(CLIENT_EXTENSION_RESULTS)); 680 } 681 ext = map.next_value::<Option<E>>().map(Some)?; 682 } 683 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 684 } 685 } 686 resp.ok_or_else(|| Error::missing_field(RESPONSE)) 687 .and_then(|response| { 688 opt_id.ok_or(false).and_then(|id| id.ok_or(true)).map_or_else( 689 |flag| { 690 if REL && REGI { 691 Ok(None) 692 } else if flag { 693 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{ID} to be a base64url-encoded CredentialId").as_str())) 694 } else { 695 Err(Error::missing_field(ID)) 696 } 697 }, 698 |id| Ok(Some(id)), 699 ) 700 .and_then(|id| { 701 raw.ok_or(false).and_then(|opt_raw_id| opt_raw_id.ok_or(true)).map_or_else( 702 |flag| { 703 if REL { 704 Ok(()) 705 } else if flag { 706 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{RAW_ID} to be a base64url-encoded CredentialId").as_str())) 707 } else { 708 Err(Error::missing_field(RAW_ID)) 709 } 710 }, 711 |raw_id| { 712 id.as_ref().map_or_else( 713 || Ok(()), 714 |i| { 715 if raw_id == i { 716 Ok(()) 717 } else { 718 Err(Error::invalid_value( 719 Unexpected::Bytes(raw_id.as_ref()), 720 &format!("{ID} and {RAW_ID} to match: {i:?}").as_str(), 721 )) 722 } 723 }, 724 ) 725 } 726 ) 727 .and_then(|()| { 728 ext.ok_or(false).and_then(|opt_ext| opt_ext.ok_or(true)).map_or_else( 729 |flag| { 730 if REL { 731 Ok(E::empty()) 732 } else if flag { 733 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{CLIENT_EXTENSION_RESULTS} to be a map of allowed client extensions").as_str())) 734 } else { 735 Err(Error::missing_field(CLIENT_EXTENSION_RESULTS)) 736 } 737 }, 738 Ok 739 ) 740 .and_then(|client_extension_results| { 741 typ.ok_or(false).and_then(|opt_typ| opt_typ.ok_or(true)).map_or_else( 742 |flag| { 743 if REL { 744 Ok(()) 745 } else if flag { 746 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{TYPE} to be '{PUBLIC_KEY}'").as_str())) 747 } else { 748 Err(Error::missing_field(TYPE)) 749 } 750 }, 751 |_| Ok(()), 752 ).map(|()| PublicKeyCredential { 753 id, 754 response, 755 authenticator_attachment: attach.flatten().unwrap_or(AuthenticatorAttachment::None), 756 client_extension_results, 757 }) 758 }) 759 }) 760 }) 761 }) 762 } 763 } 764 /// `"id"`. 765 const ID: &str = "id"; 766 /// `"type"`. 767 const TYPE: &str = "type"; 768 /// `"rawId"`. 769 const RAW_ID: &str = "rawId"; 770 /// `"response"`. 771 const RESPONSE: &str = "response"; 772 /// `"authenticatorAttachment"`. 773 const AUTHENTICATOR_ATTACHMENT: &str = "authenticatorAttachment"; 774 /// `"clientExtensionResults"`. 775 const CLIENT_EXTENSION_RESULTS: &str = "clientExtensionResults"; 776 /// `"public-key"`. 777 const PUBLIC_KEY: &str = "public-key"; 778 /// Fields for `PublicKeyCredentialJSON`. 779 const REG_FIELDS: &[&str; 6] = &[ 780 ID, 781 TYPE, 782 RAW_ID, 783 RESPONSE, 784 AUTHENTICATOR_ATTACHMENT, 785 CLIENT_EXTENSION_RESULTS, 786 ]; 787 impl<'de, const REL: bool, const REGI: bool, R, E> Deserialize<'de> 788 for PublicKeyCredential<REL, REGI, R, E> 789 where 790 R: for<'a> Deserialize<'a>, 791 E: for<'a> Deserialize<'a> + ClientExtensions, 792 { 793 /// Deserializes a `struct` based on 794 /// [`PublicKeyCredentialJSON`](https://www.w3.org/TR/webauthn-3/#typedefdef-publickeycredentialjson). 795 /// 796 /// `REL` iff unknown fields should be ignored and not cause an error. 797 /// `REGI` iff `Self` is from a registration ceremony. 798 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 799 where 800 D: Deserializer<'de>, 801 { 802 deserializer.deserialize_struct( 803 "PublicKeyCredential", 804 REG_FIELDS, 805 PublicKeyCredentialVisitor::<REL, REGI, _, _>(PhantomData), 806 ) 807 } 808 } 809 impl Serialize for AllAcceptedCredentialsOptions<'_, '_> { 810 /// Serializes `self` to conform with 811 /// [`AllAcceptedCredentialsOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-allacceptedcredentialsoptions). 812 /// 813 /// # Examples 814 /// 815 /// ``` 816 /// # use core::str::FromStr; 817 /// # #[cfg(feature = "bin")] 818 /// # use webauthn_rp::bin::Decode; 819 /// # use webauthn_rp::{ 820 /// # request::{register::UserHandle, AsciiDomain, RpId}, 821 /// # response::{error::CredentialIdErr, AllAcceptedCredentialsOptions, CredentialId}, 822 /// # }; 823 /// /// Retrieves the `CredentialId`s associated with `user_id` from the database. 824 /// # #[cfg(all(feature = "bin", feature = "custom"))] 825 /// fn get_credential_ids(user_id: UserHandle<&[u8]>) -> Result<Vec<CredentialId<Vec<u8>>>, CredentialIdErr> { 826 /// // ⋮ 827 /// # CredentialId::decode(vec![0; 16]).map(|cred_id| vec![cred_id]) 828 /// } 829 /// /// Retrieves the `UserHandle` from a session cookie. 830 /// # #[cfg(feature = "custom")] 831 /// fn get_user_handle() -> UserHandle<Vec<u8>> { 832 /// // ⋮ 833 /// # UserHandle::try_from(vec![0]).unwrap() 834 /// } 835 /// # #[cfg(feature = "custom")] 836 /// let user_id = get_user_handle(); 837 /// # #[cfg(all(feature = "bin", feature = "custom"))] 838 /// assert_eq!( 839 /// serde_json::to_string(&AllAcceptedCredentialsOptions { 840 /// rp_id: &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?), 841 /// user_id: (&user_id).into(), 842 /// all_accepted_credential_ids: get_credential_ids((&user_id).into())?, 843 /// }) 844 /// .unwrap(), 845 /// r#"{"rpId":"example.com","userId":"AA","allAcceptedCredentialIds":["AAAAAAAAAAAAAAAAAAAAAA"]}"# 846 /// ); 847 /// # Ok::<_, webauthn_rp::AggErr>(()) 848 /// ``` 849 #[inline] 850 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 851 where 852 S: Serializer, 853 { 854 serializer 855 .serialize_struct("AllAcceptedCredentialsOptions", 3) 856 .and_then(|mut ser| { 857 ser.serialize_field("rpId", self.rp_id).and_then(|()| { 858 ser.serialize_field("userId", &self.user_id).and_then(|()| { 859 ser.serialize_field( 860 "allAcceptedCredentialIds", 861 &self.all_accepted_credential_ids, 862 ) 863 .and_then(|()| ser.end()) 864 }) 865 }) 866 }) 867 } 868 } 869 impl Serialize for CurrentUserDetailsOptions<'_, '_, '_, '_> { 870 /// Serializes `self` to conform with 871 /// [`CurrentUserDetailsOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-currentuserdetailsoptions). 872 /// 873 /// # Examples 874 /// 875 /// ``` 876 /// # use core::str::FromStr; 877 /// # #[cfg(feature = "bin")] 878 /// # use webauthn_rp::bin::Decode; 879 /// # use webauthn_rp::{ 880 /// # request::{register::{Nickname, PublicKeyCredentialUserEntity, UserHandle, Username}, AsciiDomain, RpId}, 881 /// # response::CurrentUserDetailsOptions, 882 /// # AggErr, 883 /// # }; 884 /// /// Retrieves the `PublicKeyCredentialUserEntity` info associated with `user_id` from the database. 885 /// # #[cfg(feature = "bin")] 886 /// fn get_user_info(user_id: UserHandle<&[u8]>) -> Result<(Username, Option<Nickname>), AggErr> { 887 /// // ⋮ 888 /// # Ok((Username::decode("foo".to_owned()).unwrap(), Some(Nickname::decode("foo".to_owned()).unwrap()))) 889 /// } 890 /// /// Retrieves the `UserHandle` from a session cookie. 891 /// # #[cfg(feature = "custom")] 892 /// fn get_user_handle() -> UserHandle<Vec<u8>> { 893 /// // ⋮ 894 /// # UserHandle::try_from(vec![0]).unwrap() 895 /// } 896 /// # #[cfg(feature = "custom")] 897 /// let user_handle = get_user_handle(); 898 /// # #[cfg(all(feature = "bin", feature = "custom"))] 899 /// let id = (&user_handle).into(); 900 /// # #[cfg(all(feature = "bin", feature = "custom"))] 901 /// let (name, display_name) = get_user_info(id)?; 902 /// # #[cfg(all(feature = "bin", feature = "custom"))] 903 /// assert_eq!( 904 /// serde_json::to_string(&CurrentUserDetailsOptions { 905 /// rp_id: &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?), 906 /// user: PublicKeyCredentialUserEntity { name, id, display_name, }, 907 /// }) 908 /// .unwrap(), 909 /// r#"{"rpId":"example.com","userId":"AA","name":"foo","displayName":"foo"}"# 910 /// ); 911 /// # Ok::<_, AggErr>(()) 912 /// ``` 913 #[inline] 914 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 915 where 916 S: Serializer, 917 { 918 serializer 919 .serialize_struct("CurrentUserDetailsOptions", 4) 920 .and_then(|mut ser| { 921 ser.serialize_field("rpId", self.rp_id).and_then(|()| { 922 ser.serialize_field("userId", &self.user.id).and_then(|()| { 923 ser.serialize_field("name", &self.user.name).and_then(|()| { 924 ser.serialize_field("displayName", &self.user.display_name) 925 .and_then(|()| ser.end()) 926 }) 927 }) 928 }) 929 }) 930 } 931 } 932 /// JSON `null`. 933 struct Null; 934 impl<'de> Deserialize<'de> for Null { 935 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 936 where 937 D: Deserializer<'de>, 938 { 939 /// `Visitor` for `Null`. 940 struct NullVisitor; 941 impl Visitor<'_> for NullVisitor { 942 type Value = Null; 943 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 944 formatter.write_str("null") 945 } 946 fn visit_none<E>(self) -> Result<Self::Value, E> 947 where 948 E: Error, 949 { 950 Ok(Null) 951 } 952 } 953 deserializer.deserialize_option(NullVisitor) 954 } 955 } 956 /// [`AuthenticationExtensionsPRFValues`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfvalues). 957 pub(super) struct AuthenticationExtensionsPrfValues; 958 /// `Visitor` for `AuthenticationExtensionsPrfValues`. 959 /// 960 /// Unknown fields are ignored iff `RELAXED`.`first` must always exist if `second` does. 961 /// `first` and `second` must be `null` if they exist. `first` must exist iff `!RELAXED`. 962 pub(super) struct AuthenticationExtensionsPrfValuesVisitor<const RELAXED: bool>; 963 impl<'d, const R: bool> Visitor<'d> for AuthenticationExtensionsPrfValuesVisitor<R> { 964 type Value = AuthenticationExtensionsPrfValues; 965 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 966 formatter.write_str("AuthenticationExtensionsPrfValues") 967 } 968 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 969 where 970 A: MapAccess<'d>, 971 { 972 /// Fields. 973 enum Field<const IGNORE_UNKNOWN: bool> { 974 /// `first` field. 975 First, 976 /// `second` field. 977 Second, 978 /// Unknown field. 979 Other, 980 } 981 impl<'e, const I: bool> Deserialize<'e> for Field<I> { 982 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 983 where 984 D: Deserializer<'e>, 985 { 986 /// `Visitor` for `Field`. 987 /// 988 /// Unknown fields are ignored iff `IGNORE_UNKNOWN`. 989 struct FieldVisitor<const IGNORE_UNKNOWN: bool>; 990 impl<const IG: bool> Visitor<'_> for FieldVisitor<IG> { 991 type Value = Field<IG>; 992 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 993 write!(formatter, "'{FIRST}' or '{SECOND}'") 994 } 995 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 996 where 997 E: Error, 998 { 999 match v { 1000 FIRST => Ok(Field::First), 1001 SECOND => Ok(Field::Second), 1002 _ => { 1003 if IG { 1004 Ok(Field::Other) 1005 } else { 1006 Err(E::unknown_field(v, PRF_VALUES_FIELDS)) 1007 } 1008 } 1009 } 1010 } 1011 } 1012 deserializer.deserialize_identifier(FieldVisitor) 1013 } 1014 } 1015 let mut first = None; 1016 let mut second = None; 1017 while let Some(key) = map.next_key::<Field<R>>()? { 1018 match key { 1019 Field::First => { 1020 if first.is_some() { 1021 return Err(Error::duplicate_field(FIRST)); 1022 } 1023 first = map.next_value::<Null>().map(Some)?; 1024 } 1025 Field::Second => { 1026 if second.is_some() { 1027 return Err(Error::duplicate_field(SECOND)); 1028 } 1029 second = map.next_value::<Null>().map(Some)?; 1030 } 1031 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 1032 } 1033 } 1034 if first.is_some() || (R && second.is_none()) { 1035 Ok(AuthenticationExtensionsPrfValues) 1036 } else { 1037 Err(Error::missing_field(FIRST)) 1038 } 1039 } 1040 } 1041 /// `"first"` 1042 const FIRST: &str = "first"; 1043 /// `"second"` 1044 const SECOND: &str = "second"; 1045 /// `AuthenticationExtensionsPrfValues` fields. 1046 pub(super) const PRF_VALUES_FIELDS: &[&str; 2] = &[FIRST, SECOND]; 1047 impl<'de> Deserialize<'de> for AuthenticationExtensionsPrfValues { 1048 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1049 where 1050 D: Deserializer<'de>, 1051 { 1052 deserializer.deserialize_struct( 1053 "AuthenticationExtensionsPrfValues", 1054 PRF_VALUES_FIELDS, 1055 AuthenticationExtensionsPrfValuesVisitor::<false>, 1056 ) 1057 } 1058 } 1059 /// [`AuthenticationExtensionsPRFOutputs`](https://www.w3.org/TR/webauthn-3/#dictdef-authenticationextensionsprfoutputs). 1060 /// 1061 /// `RELAXED` iff unknown fields are ignored. 1062 /// 1063 /// `REGISTRATION` iff 1064 /// [`enabled`](https://www.w3.org/TR/webauthn-3/#dom-authenticationextensionsprfoutputs-enabled) 1065 /// is required (and must not be `null`); otherwise it's forbidden. 1066 /// 1067 /// The contained `Option` is `Some` iff `REGISTRATION`. 1068 pub(super) struct AuthenticationExtensionsPrfOutputsHelper< 1069 const RELAXED: bool, 1070 const REGISTRATION: bool, 1071 Prf, 1072 >(pub Option<bool>, pub PhantomData<fn() -> Prf>); 1073 /// `Visitor` for `AuthenticationExtensionsPrfOutputs`. 1074 /// 1075 /// Unknown fields are ignored iff `RELAXED`.`enabled` must exist and not be `null` iff `REGISTRATION`. 1076 struct AuthenticationExtensionsPrfOutputsVisitor<const RELAXED: bool, const REGISTRATION: bool, Prf>( 1077 PhantomData<fn() -> Prf>, 1078 ); 1079 impl<'d, const REL: bool, const REG: bool, Prf> Visitor<'d> 1080 for AuthenticationExtensionsPrfOutputsVisitor<REL, REG, Prf> 1081 where 1082 Prf: for<'a> Deserialize<'a>, 1083 { 1084 type Value = AuthenticationExtensionsPrfOutputsHelper<REL, REG, Prf>; 1085 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1086 formatter.write_str("AuthenticationExtensionsPrfOutputs") 1087 } 1088 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 1089 where 1090 A: MapAccess<'d>, 1091 { 1092 /// Fields. 1093 enum Field<const IGNORE_UNKNOWN: bool, const REGI: bool> { 1094 /// `enabled` field. 1095 Enabled, 1096 /// `results` field. 1097 Results, 1098 /// Unknown field. 1099 Other, 1100 } 1101 impl<'e, const I: bool, const R: bool> Deserialize<'e> for Field<I, R> { 1102 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1103 where 1104 D: Deserializer<'e>, 1105 { 1106 /// `Visitor` for `Field`. 1107 /// 1108 /// Unknown fields are ignored iff `IGNORE_UNKNOWN`. 1109 /// `enabled` is allowed to exist iff `REGI`. 1110 struct FieldVisitor<const IGNORE_UNKNOWN: bool, const REGI: bool>; 1111 impl<const IG: bool, const RE: bool> Visitor<'_> for FieldVisitor<IG, RE> { 1112 type Value = Field<IG, RE>; 1113 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 1114 if RE { 1115 write!(formatter, "'{ENABLED}' or '{RESULTS}'") 1116 } else { 1117 write!(formatter, "'{RESULTS}'") 1118 } 1119 } 1120 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 1121 where 1122 E: Error, 1123 { 1124 match v { 1125 ENABLED => { 1126 if RE { 1127 Ok(Field::Enabled) 1128 } else { 1129 Err(E::unknown_field(v, PRF_AUTH_OUTPUTS_FIELDS)) 1130 } 1131 } 1132 RESULTS => Ok(Field::Results), 1133 _ => { 1134 if IG { 1135 Ok(Field::Other) 1136 } else if RE { 1137 Err(E::unknown_field(v, PRF_REG_OUTPUTS_FIELDS)) 1138 } else { 1139 Err(E::unknown_field(v, PRF_AUTH_OUTPUTS_FIELDS)) 1140 } 1141 } 1142 } 1143 } 1144 } 1145 deserializer.deserialize_identifier(FieldVisitor) 1146 } 1147 } 1148 let mut enabled = None; 1149 let mut results = None; 1150 while let Some(key) = map.next_key::<Field<REL, REG>>()? { 1151 match key { 1152 Field::Enabled => { 1153 if enabled.is_some() { 1154 return Err(Error::duplicate_field(ENABLED)); 1155 } 1156 enabled = map.next_value().map(Some)?; 1157 } 1158 Field::Results => { 1159 if results.is_some() { 1160 return Err(Error::duplicate_field(RESULTS)); 1161 } 1162 results = map.next_value::<Option<Prf>>().map(Some)?; 1163 } 1164 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 1165 } 1166 } 1167 if REG { 1168 enabled.ok_or_else(|| Error::missing_field(ENABLED)).and_then(|e| { 1169 if e || results.is_none() { 1170 Ok(()) 1171 } else { 1172 Err(Error::custom("prf must not have 'results', including a null 'results', if 'enabled' is false")) 1173 } 1174 }) 1175 } else { 1176 Ok(()) 1177 }.map(|()| AuthenticationExtensionsPrfOutputsHelper(enabled, PhantomData)) 1178 } 1179 } 1180 /// `"enabled"` 1181 const ENABLED: &str = "enabled"; 1182 /// `"results"` 1183 const RESULTS: &str = "results"; 1184 /// `AuthenticationExtensionsPrfOutputs` field during registration. 1185 const PRF_REG_OUTPUTS_FIELDS: &[&str; 2] = &[ENABLED, RESULTS]; 1186 /// `AuthenticationExtensionsPrfOutputs` field during authentication. 1187 const PRF_AUTH_OUTPUTS_FIELDS: &[&str; 1] = &[RESULTS]; 1188 impl<'de, const REL: bool, const REG: bool, Prf> Deserialize<'de> 1189 for AuthenticationExtensionsPrfOutputsHelper<REL, REG, Prf> 1190 where 1191 for<'a> Prf: Deserialize<'a>, 1192 { 1193 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 1194 where 1195 D: Deserializer<'de>, 1196 { 1197 deserializer.deserialize_struct( 1198 "AuthenticationExtensionsPrfOutputs", 1199 if REG { 1200 PRF_REG_OUTPUTS_FIELDS 1201 } else { 1202 PRF_AUTH_OUTPUTS_FIELDS 1203 }, 1204 AuthenticationExtensionsPrfOutputsVisitor::<REL, REG, Prf>(PhantomData), 1205 ) 1206 } 1207 }