ser.rs (47984B)
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 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> 432 where 433 E: Error, 434 { 435 if v.len() == Challenge::BASE64_LEN { 436 let mut data = [0; 16]; 437 BASE64URL_NOPAD 438 .decode_mut(v, data.as_mut_slice()) 439 .map_err(|err| E::custom(err.error)) 440 .map(|len| { 441 assert_eq!(len, 16, "there is a bug in BASE64URL_NOPAD::decode_mut"); 442 SentChallenge::from_array(data) 443 }) 444 } else { 445 Err(E::invalid_value( 446 Unexpected::Bytes(v), 447 &"22 bytes encoded in base64url without padding", 448 )) 449 } 450 } 451 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 452 where 453 E: Error, 454 { 455 self.visit_bytes(v.as_bytes()) 456 } 457 } 458 deserializer.deserialize_bytes(ChallengeVisitor) 459 } 460 } 461 impl<'de: 'a, 'a> Deserialize<'de> for Origin<'a> { 462 /// Deserializes [`prim@str`] by borrowing the data when possible. 463 /// 464 /// # Examples 465 /// 466 /// ``` 467 /// # extern crate alloc; 468 /// # use alloc::borrow::Cow; 469 /// # use webauthn_rp::response::Origin; 470 /// let origin_borrowed = "https://example.com"; 471 /// let origin_owned = "\\\\https://example.com"; 472 /// assert!( 473 /// matches!(serde_json::from_str::<Origin<'_>>(format!("\"{origin_borrowed}\"").as_str())?.0, Cow::Borrowed(val) if val == origin_borrowed) 474 /// ); 475 /// assert!( 476 /// matches!(serde_json::from_str::<Origin<'_>>(format!("\"{origin_owned}\"").as_str())?.0, Cow::Owned(val) if *val.as_bytes() == origin_owned.as_bytes()[1..]) 477 /// ); 478 /// # Ok::<_, serde_json::Error>(()) 479 ///``` 480 #[inline] 481 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 482 where 483 D: Deserializer<'de>, 484 { 485 /// `Visitor` for `Origin`. 486 struct OriginVisitor<'b>(PhantomData<fn() -> &'b ()>); 487 impl<'d: 'b, 'b> Visitor<'d> for OriginVisitor<'b> { 488 type Value = Origin<'b>; 489 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 490 formatter.write_str("Origin") 491 } 492 fn visit_borrowed_str<E>(self, v: &'d str) -> Result<Self::Value, E> 493 where 494 E: Error, 495 { 496 Ok(Origin(Cow::Borrowed(v))) 497 } 498 fn visit_string<E>(self, v: String) -> Result<Self::Value, E> 499 where 500 E: Error, 501 { 502 Ok(Origin(Cow::Owned(v))) 503 } 504 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 505 where 506 E: Error, 507 { 508 self.visit_string(v.to_owned()) 509 } 510 } 511 deserializer.deserialize_str(OriginVisitor(PhantomData)) 512 } 513 } 514 /// `trait` that returns an empty instance of `Self`. 515 pub(super) trait ClientExtensions: Sized { 516 /// Returns an empty instance of `Self`. 517 fn empty() -> Self; 518 } 519 /// Response for both registration and authentication ceremonies. 520 /// 521 /// [`Self::raw_id`] is always `Some` when `!RELAXED` or `!REG`. 522 /// 523 /// `RELAXED` and `REG` are used purely for deserialization purposes. 524 pub(super) struct PublicKeyCredential<const RELAXED: bool, const REG: bool, AuthResp, Ext> { 525 /// [`rawId`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-rawid). 526 pub id: Option<CredentialId<Vec<u8>>>, 527 /// [`response`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-response). 528 pub response: AuthResp, 529 /// [`authenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-authenticatorattachment). 530 pub authenticator_attachment: AuthenticatorAttachment, 531 /// [`getClientExtensionResults`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-getclientextensionresults). 532 pub client_extension_results: Ext, 533 } 534 /// Deserializes the value for type. 535 pub(super) struct Type; 536 impl<'e> Deserialize<'e> for Type { 537 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 538 where 539 D: Deserializer<'e>, 540 { 541 /// `Visitor` for `Type`. 542 struct TypeVisitor; 543 impl Visitor<'_> for TypeVisitor { 544 type Value = Type; 545 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 546 formatter.write_str(PUBLIC_KEY) 547 } 548 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 549 where 550 E: Error, 551 { 552 if v == PUBLIC_KEY { 553 Ok(Type) 554 } else { 555 Err(E::invalid_value(Unexpected::Str(v), &PUBLIC_KEY)) 556 } 557 } 558 } 559 deserializer.deserialize_str(TypeVisitor) 560 } 561 } 562 /// `Visitor` for `PublicKeyCredential`. 563 /// 564 /// When `!RELAXED`, `REG` is ignored and all fields must exist and unknown fields are not allowed. 565 /// When `RELAXED`, unknown fields are ignored. 566 /// When `RELAXED` and `REG`, only `response` is required. 567 /// When `RELAXED` and `!REG`, only `id` and `response` are required. 568 struct PublicKeyCredentialVisitor<const RELAXED: bool, const REG: bool, R, E>( 569 pub PhantomData<fn() -> (R, E)>, 570 ); 571 impl<'d, const REL: bool, const REGI: bool, R, E> Visitor<'d> 572 for PublicKeyCredentialVisitor<REL, REGI, R, E> 573 where 574 R: Deserialize<'d>, 575 E: for<'a> Deserialize<'a> + ClientExtensions, 576 { 577 type Value = PublicKeyCredential<REL, REGI, R, E>; 578 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 579 formatter.write_str("PublicKeyCredential") 580 } 581 #[expect( 582 clippy::too_many_lines, 583 reason = "rather hide all the internal logic instead instead of moving into an outer scope" 584 )] 585 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 586 where 587 A: MapAccess<'d>, 588 { 589 /// `PublicKeyCredentialJSON` fields. 590 enum Field<const IGNORE_UNKNOWN: bool> { 591 /// `id`. 592 Id, 593 /// `type`. 594 Type, 595 /// `rawId`. 596 RawId, 597 /// `response`. 598 Response, 599 /// `authenticatorAttachment`. 600 AuthenticatorAttachment, 601 /// `clientExtensionResults`. 602 ClientExtensionResults, 603 /// Unknown field. 604 Other, 605 } 606 impl<'e, const IGNORE: bool> Deserialize<'e> for Field<IGNORE> { 607 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 608 where 609 D: Deserializer<'e>, 610 { 611 /// `Visitor` for `Field`. 612 struct FieldVisitor<const IGNORE_UNKNOWN: bool>; 613 impl<const IGN: bool> Visitor<'_> for FieldVisitor<IGN> { 614 type Value = Field<IGN>; 615 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 616 write!( 617 formatter, 618 "'{ID}', '{TYPE}', '{RAW_ID}', '{RESPONSE}', '{AUTHENTICATOR_ATTACHMENT}', or '{CLIENT_EXTENSION_RESULTS}'" 619 ) 620 } 621 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 622 where 623 E: Error, 624 { 625 match v { 626 ID => Ok(Field::Id), 627 TYPE => Ok(Field::Type), 628 RAW_ID => Ok(Field::RawId), 629 RESPONSE => Ok(Field::Response), 630 AUTHENTICATOR_ATTACHMENT => Ok(Field::AuthenticatorAttachment), 631 CLIENT_EXTENSION_RESULTS => Ok(Field::ClientExtensionResults), 632 _ => { 633 if IGN { 634 Ok(Field::Other) 635 } else { 636 Err(E::unknown_field(v, REG_FIELDS)) 637 } 638 } 639 } 640 } 641 } 642 deserializer.deserialize_identifier(FieldVisitor::<IGNORE>) 643 } 644 } 645 let mut opt_id = None; 646 let mut typ = false; 647 let mut raw = None; 648 let mut resp = None; 649 let mut attach = None; 650 let mut ext = None; 651 while let Some(key) = map.next_key::<Field<REL>>()? { 652 match key { 653 Field::Id => { 654 if opt_id.is_some() { 655 return Err(Error::duplicate_field(ID)); 656 } 657 opt_id = map.next_value::<CredentialId<_>>().map(Some)?; 658 } 659 Field::Type => { 660 if typ { 661 return Err(Error::duplicate_field(TYPE)); 662 } 663 typ = map.next_value::<Type>().map(|_| true)?; 664 } 665 Field::RawId => { 666 if raw.is_some() { 667 return Err(Error::duplicate_field(RAW_ID)); 668 } 669 raw = map.next_value::<CredentialId<_>>().map(Some)?; 670 } 671 Field::Response => { 672 if resp.is_some() { 673 return Err(Error::duplicate_field(RESPONSE)); 674 } 675 resp = map.next_value::<R>().map(Some)?; 676 } 677 Field::AuthenticatorAttachment => { 678 if attach.is_some() { 679 return Err(Error::duplicate_field(AUTHENTICATOR_ATTACHMENT)); 680 } 681 attach = map.next_value().map(Some)?; 682 } 683 Field::ClientExtensionResults => { 684 if ext.is_some() { 685 return Err(Error::duplicate_field(CLIENT_EXTENSION_RESULTS)); 686 } 687 ext = map.next_value::<Option<E>>().map(Some)?; 688 } 689 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 690 } 691 } 692 resp.ok_or_else(|| Error::missing_field(RESPONSE)) 693 .and_then(|response| { 694 opt_id.map_or_else( 695 || { 696 if REL && REGI { 697 Ok(None) 698 } else { 699 Err(Error::missing_field(ID)) 700 } 701 }, 702 |id| Ok(Some(id)), 703 ) 704 .and_then(|id| { 705 raw.map_or_else( 706 || { 707 if REL { 708 Ok(()) 709 } else { 710 Err(Error::missing_field(RAW_ID)) 711 } 712 }, 713 |raw_id| { 714 id.as_ref().map_or_else( 715 || Ok(()), 716 |i| { 717 if raw_id == i { 718 Ok(()) 719 } else { 720 Err(Error::invalid_value( 721 Unexpected::Bytes(raw_id.as_ref()), 722 &format!("{ID} and {RAW_ID} to match: {i:?}").as_str(), 723 )) 724 } 725 }, 726 ) 727 } 728 ) 729 .and_then(|()| { 730 ext.ok_or(false).and_then(|opt_ext| opt_ext.ok_or(true)).map_or_else( 731 |flag| { 732 if REL { 733 Ok(E::empty()) 734 } else if flag { 735 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{CLIENT_EXTENSION_RESULTS} to be a map of allowed client extensions").as_str())) 736 } else { 737 Err(Error::missing_field(CLIENT_EXTENSION_RESULTS)) 738 } 739 }, 740 Ok 741 ) 742 .and_then(|client_extension_results| { 743 if typ || REL { 744 Ok(PublicKeyCredential { 745 id, 746 response, 747 authenticator_attachment: attach.flatten().unwrap_or(AuthenticatorAttachment::None), 748 client_extension_results, 749 }) 750 } else { 751 Err(Error::missing_field(TYPE)) 752 } 753 }) 754 }) 755 }) 756 }) 757 } 758 } 759 /// `"id"`. 760 const ID: &str = "id"; 761 /// `"type"`. 762 const TYPE: &str = "type"; 763 /// `"rawId"`. 764 const RAW_ID: &str = "rawId"; 765 /// `"response"`. 766 const RESPONSE: &str = "response"; 767 /// `"authenticatorAttachment"`. 768 const AUTHENTICATOR_ATTACHMENT: &str = "authenticatorAttachment"; 769 /// `"clientExtensionResults"`. 770 const CLIENT_EXTENSION_RESULTS: &str = "clientExtensionResults"; 771 /// `"public-key"`. 772 const PUBLIC_KEY: &str = "public-key"; 773 /// Fields for `PublicKeyCredentialJSON`. 774 const REG_FIELDS: &[&str; 6] = &[ 775 ID, 776 TYPE, 777 RAW_ID, 778 RESPONSE, 779 AUTHENTICATOR_ATTACHMENT, 780 CLIENT_EXTENSION_RESULTS, 781 ]; 782 impl<'de, const REL: bool, const REGI: bool, R, E> Deserialize<'de> 783 for PublicKeyCredential<REL, REGI, R, E> 784 where 785 R: Deserialize<'de>, 786 E: for<'a> Deserialize<'a> + ClientExtensions, 787 { 788 /// Deserializes a `struct` based on 789 /// [`PublicKeyCredentialJSON`](https://www.w3.org/TR/webauthn-3/#typedefdef-publickeycredentialjson). 790 /// 791 /// `REL` iff unknown fields should be ignored and not cause an error. 792 /// `REGI` iff `Self` is from a registration ceremony. 793 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 794 where 795 D: Deserializer<'de>, 796 { 797 deserializer.deserialize_struct( 798 "PublicKeyCredential", 799 REG_FIELDS, 800 PublicKeyCredentialVisitor::<REL, REGI, _, _>(PhantomData), 801 ) 802 } 803 } 804 use super::UserHandle; 805 impl<const USER_LEN: usize> Serialize for AllAcceptedCredentialsOptions<'_, '_, USER_LEN> 806 where 807 UserHandle<USER_LEN>: Serialize, 808 { 809 /// Serializes `self` to conform with 810 /// [`AllAcceptedCredentialsOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-allacceptedcredentialsoptions). 811 /// 812 /// # Examples 813 /// 814 /// ``` 815 /// # use core::str::FromStr; 816 /// # #[cfg(feature = "bin")] 817 /// # use webauthn_rp::bin::Decode; 818 /// # use webauthn_rp::{ 819 /// # request::{register::{UserHandle, USER_HANDLE_MIN_LEN}, AsciiDomain, RpId}, 820 /// # response::{error::CredentialIdErr, AllAcceptedCredentialsOptions, CredentialId}, 821 /// # }; 822 /// /// Retrieves the `CredentialId`s associated with `user_id` from the database. 823 /// # #[cfg(all(feature = "bin", feature = "custom"))] 824 /// fn get_credential_ids(user_id: UserHandle<USER_HANDLE_MIN_LEN>) -> Result<Vec<CredentialId<Vec<u8>>>, CredentialIdErr> { 825 /// // ⋮ 826 /// # CredentialId::decode(vec![0; 16]).map(|cred_id| vec![cred_id]) 827 /// } 828 /// /// Retrieves the `UserHandle` from a session cookie. 829 /// # #[cfg(feature = "custom")] 830 /// fn get_user_handle() -> UserHandle<USER_HANDLE_MIN_LEN> { 831 /// // ⋮ 832 /// # [0].into() 833 /// } 834 /// # #[cfg(feature = "custom")] 835 /// let user_id = get_user_handle(); 836 /// # #[cfg(all(feature = "bin", feature = "custom"))] 837 /// assert_eq!( 838 /// serde_json::to_string(&AllAcceptedCredentialsOptions { 839 /// rp_id: &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?), 840 /// user_id: &user_id, 841 /// all_accepted_credential_ids: get_credential_ids(user_id)?, 842 /// }) 843 /// .unwrap(), 844 /// r#"{"rpId":"example.com","userId":"AA","allAcceptedCredentialIds":["AAAAAAAAAAAAAAAAAAAAAA"]}"# 845 /// ); 846 /// # Ok::<_, webauthn_rp::AggErr>(()) 847 /// ``` 848 #[inline] 849 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 850 where 851 S: Serializer, 852 { 853 serializer 854 .serialize_struct("AllAcceptedCredentialsOptions", 3) 855 .and_then(|mut ser| { 856 ser.serialize_field("rpId", self.rp_id).and_then(|()| { 857 ser.serialize_field("userId", &self.user_id).and_then(|()| { 858 ser.serialize_field( 859 "allAcceptedCredentialIds", 860 &self.all_accepted_credential_ids, 861 ) 862 .and_then(|()| ser.end()) 863 }) 864 }) 865 }) 866 } 867 } 868 impl<const LEN: usize> Serialize for CurrentUserDetailsOptions<'_, '_, '_, '_, LEN> 869 where 870 UserHandle<LEN>: Serialize, 871 { 872 /// Serializes `self` to conform with 873 /// [`CurrentUserDetailsOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-currentuserdetailsoptions). 874 /// 875 /// # Examples 876 /// 877 /// ``` 878 /// # use core::str::FromStr; 879 /// # #[cfg(feature = "bin")] 880 /// # use webauthn_rp::bin::Decode; 881 /// # use webauthn_rp::{ 882 /// # request::{register::{Nickname, PublicKeyCredentialUserEntity, UserHandle, USER_HANDLE_MIN_LEN, Username}, AsciiDomain, RpId}, 883 /// # response::CurrentUserDetailsOptions, 884 /// # AggErr, 885 /// # }; 886 /// /// Retrieves the `PublicKeyCredentialUserEntity` info associated with `user_id` from the database. 887 /// # #[cfg(feature = "bin")] 888 /// fn get_user_info(user_id: UserHandle<USER_HANDLE_MIN_LEN>) -> Result<(Username<'static>, Option<Nickname<'static>>), AggErr> { 889 /// // ⋮ 890 /// # Ok((Username::decode("foo").unwrap(), Some(Nickname::decode("foo").unwrap()))) 891 /// } 892 /// /// Retrieves the `UserHandle` from a session cookie. 893 /// # #[cfg(feature = "custom")] 894 /// fn get_user_handle() -> UserHandle<USER_HANDLE_MIN_LEN> { 895 /// // ⋮ 896 /// # [0].into() 897 /// } 898 /// # #[cfg(feature = "custom")] 899 /// let user_handle = get_user_handle(); 900 /// # #[cfg(all(feature = "bin", feature = "custom"))] 901 /// let (name, display_name) = get_user_info(user_handle)?; 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: &user_handle, 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 }