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