ser.rs (49055B)
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, 9 }; 10 use alloc::borrow::Cow; 11 use core::{ 12 fmt::{self, Formatter}, 13 marker::PhantomData, 14 }; 15 use data_encoding::BASE64URL_NOPAD; 16 use serde::{ 17 de::{Deserialize, Deserializer, Error, IgnoredAny, MapAccess, SeqAccess, Unexpected, Visitor}, 18 ser::{Serialize, SerializeSeq as _, SerializeStruct as _, Serializer}, 19 }; 20 /// [`"ble"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-ble). 21 const BLE: &str = "ble"; 22 /// [`"hybrid"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-hybrid). 23 const HYBRID: &str = "hybrid"; 24 /// [`"internal"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-internal). 25 const INTERNAL: &str = "internal"; 26 /// [`"nfc"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-nfc). 27 const NFC: &str = "nfc"; 28 /// [`"smart-card"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-smart-card). 29 const SMART_CARD: &str = "smart-card"; 30 /// [`"usb"`](https://www.w3.org/TR/webauthn-3/#dom-authenticatortransport-usb). 31 const USB: &str = "usb"; 32 impl Serialize for AuthenticatorTransport { 33 /// Serializes `self` as 34 /// [`AuthenticatorTransport`](https://www.w3.org/TR/webauthn-3/#enumdef-authenticatortransport). 35 /// 36 /// # Examples 37 /// 38 /// ``` 39 /// # use webauthn_rp::response::AuthenticatorTransport; 40 /// assert_eq!( 41 /// serde_json::to_string(&AuthenticatorTransport::Usb)?, 42 /// r#""usb""# 43 /// ); 44 /// # Ok::<_, serde_json::Error>(()) 45 /// ``` 46 #[inline] 47 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 48 where 49 S: Serializer, 50 { 51 serializer.serialize_str(match *self { 52 Self::Ble => BLE, 53 Self::Hybrid => HYBRID, 54 Self::Internal => INTERNAL, 55 Self::Nfc => NFC, 56 Self::SmartCard => SMART_CARD, 57 Self::Usb => USB, 58 }) 59 } 60 } 61 impl<'de> Deserialize<'de> for AuthenticatorTransport { 62 /// Deserializes [`prim@str`] based on 63 /// [`AuthenticatorTransport`](https://www.w3.org/TR/webauthn-3/#enumdef-authenticatortransport). 64 /// 65 /// # Examples 66 /// 67 /// ``` 68 /// # use webauthn_rp::response::AuthenticatorTransport; 69 /// assert!(matches!( 70 /// serde_json::from_str::<AuthenticatorTransport>(r#""usb""#)?, 71 /// AuthenticatorTransport::Usb 72 /// )); 73 /// // Case matters. 74 /// assert!(serde_json::from_str::<AuthenticatorTransport>(r#""Usb""#).is_err()); 75 /// # Ok::<_, serde_json::Error>(()) 76 /// ``` 77 #[inline] 78 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 79 where 80 D: Deserializer<'de>, 81 { 82 /// `Visitor` for `AuthenticatorTransport`. 83 struct AuthenticatorTransportVisitor; 84 impl Visitor<'_> for AuthenticatorTransportVisitor { 85 type Value = AuthenticatorTransport; 86 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 87 formatter.write_str("AuthenticatorTransport") 88 } 89 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 90 where 91 E: Error, 92 { 93 match v { 94 BLE => Ok(AuthenticatorTransport::Ble), 95 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!("'{BLE}', '{HYBRID}', '{INTERNAL}', '{NFC}', '{SMART_CARD}', or '{USB}'").as_str(), 103 )), 104 } 105 } 106 } 107 deserializer.deserialize_str(AuthenticatorTransportVisitor) 108 } 109 } 110 impl Serialize for AuthTransports { 111 /// Serializes `self` based on 112 /// [`transports`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-transports). 113 /// 114 /// # Examples 115 /// 116 /// ``` 117 /// # use webauthn_rp::response::{AuthTransports, AuthenticatorTransport}; 118 /// # #[cfg(feature = "custom")] 119 /// assert_eq!( 120 /// serde_json::to_string(&AuthTransports::ALL)?, 121 /// r#"["ble","hybrid","internal","nfc","smart-card","usb"]"# 122 /// ); 123 /// # Ok::<_, serde_json::Error>(()) 124 /// ``` 125 #[expect(clippy::unreachable, reason = "there is a bug, so we want to crash")] 126 #[inline] 127 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 128 where 129 S: Serializer, 130 { 131 let count = usize::try_from(self.count()) 132 .unwrap_or_else(|_e| unreachable!("there is a bug in AuthenticatorTransports::count")); 133 serializer.serialize_seq(Some(count)).and_then(|mut ser| { 134 if self.contains(AuthenticatorTransport::Ble) { 135 ser.serialize_element(&AuthenticatorTransport::Ble) 136 } else { 137 Ok(()) 138 } 139 .and_then(|()| { 140 if self.contains(AuthenticatorTransport::Hybrid) { 141 ser.serialize_element(&AuthenticatorTransport::Hybrid) 142 } else { 143 Ok(()) 144 } 145 .and_then(|()| { 146 if self.contains(AuthenticatorTransport::Internal) { 147 ser.serialize_element(&AuthenticatorTransport::Internal) 148 } else { 149 Ok(()) 150 } 151 .and_then(|()| { 152 if self.contains(AuthenticatorTransport::Nfc) { 153 ser.serialize_element(&AuthenticatorTransport::Nfc) 154 } else { 155 Ok(()) 156 } 157 .and_then(|()| { 158 if self.contains(AuthenticatorTransport::SmartCard) { 159 ser.serialize_element(&AuthenticatorTransport::SmartCard) 160 } else { 161 Ok(()) 162 } 163 .and_then(|()| { 164 if self.contains(AuthenticatorTransport::Usb) { 165 ser.serialize_element(&AuthenticatorTransport::Usb) 166 } else { 167 Ok(()) 168 } 169 .and_then(|()| ser.end()) 170 }) 171 }) 172 }) 173 }) 174 }) 175 }) 176 } 177 } 178 impl<'de> Deserialize<'de> for AuthTransports { 179 /// Deserializes a sequence based on 180 /// [`transports`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptor-transports). 181 /// 182 /// # Examples 183 /// 184 /// ``` 185 /// # use webauthn_rp::response::{AuthTransports, AuthenticatorTransport}; 186 /// # #[cfg(feature = "custom")] 187 /// assert_eq!( 188 /// serde_json::from_str::<AuthTransports>( 189 /// r#"["ble","hybrid","internal","nfc","smart-card","usb"]"# 190 /// ) 191 /// ?.count(), 192 /// 6 193 /// ); 194 /// // Errors since `"foo"` is not valid. 195 /// assert!(serde_json::from_str::<AuthTransports>(r#"["foo"]"#).is_err()); 196 /// # Ok::<_, serde_json::Error>(()) 197 /// ``` 198 #[inline] 199 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 200 where 201 D: Deserializer<'de>, 202 { 203 /// `Visitor` for `AuthTransports`. 204 struct AuthTransportsVisitor; 205 impl<'d> Visitor<'d> for AuthTransportsVisitor { 206 type Value = AuthTransports; 207 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 208 formatter.write_str("AuthTransports") 209 } 210 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> 211 where 212 A: SeqAccess<'d>, 213 { 214 let mut transports = AuthTransports::new(); 215 while let Some(val) = seq.next_element::<AuthenticatorTransport>()? { 216 transports = transports.add_transport(val); 217 } 218 Ok(transports) 219 } 220 } 221 deserializer.deserialize_seq(AuthTransportsVisitor) 222 } 223 } 224 impl<T: AsRef<[u8]>> Serialize for CredentialId<T> { 225 /// Serializes `self` into a [`prim@str`] based on 226 /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptorjson-id). 227 /// 228 /// # Examples 229 /// 230 /// ``` 231 /// # use webauthn_rp::response::CredentialId; 232 /// // `CredentialId::try_from` only exists when `custom` is enabled; and even then, it is 233 /// // likely never needed since the `CredentialId` was originally sent from the client and is likely 234 /// // stored in a database which would be fetched by `UserHandle` or `Authentication::raw_id`. 235 /// # #[cfg(feature = "custom")] 236 /// assert_eq!( 237 /// serde_json::to_string(&CredentialId::try_from(vec![0; 16])?).unwrap(), 238 /// r#""AAAAAAAAAAAAAAAAAAAAAA""# 239 /// ); 240 /// # Ok::<_, webauthn_rp::AggErr>(()) 241 ///``` 242 #[inline] 243 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 244 where 245 S: Serializer, 246 { 247 serializer.serialize_str(BASE64URL_NOPAD.encode(self.0.as_ref()).as_str()) 248 } 249 } 250 impl<'de> Deserialize<'de> for CredentialId<Vec<u8>> { 251 /// Deserializes [`prim@str`] based on 252 /// [`id`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialdescriptorjson-id). 253 /// 254 /// # Examples 255 /// 256 /// ``` 257 /// # use webauthn_rp::response::CredentialId; 258 /// # #[cfg(feature = "custom")] 259 /// assert_eq!( 260 /// serde_json::from_str::<CredentialId<_>>(r#""AAAAAAAAAAAAAAAAAAAAAA""#).unwrap(), 261 /// CredentialId::try_from(vec![0; 16])? 262 /// ); 263 /// # Ok::<_, webauthn_rp::AggErr>(()) 264 ///``` 265 #[inline] 266 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 267 where 268 D: Deserializer<'de>, 269 { 270 /// `Visitor` for `CredentialId`. 271 struct CredentialIdVisitor; 272 impl Visitor<'_> for CredentialIdVisitor { 273 type Value = CredentialId<Vec<u8>>; 274 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 275 formatter.write_str("CredentialId") 276 } 277 #[expect(clippy::unreachable, reason = "we want to crash when there is a bug")] 278 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 279 where 280 E: Error, 281 { 282 // Any value between `super::CRED_ID_MIN_LEN` and `super::CRED_ID_MIN_LEN` can be base64url encoded 283 // without fear since that range is just 16 to 1023, and 284 // 4/3 of 1023 is less than `usize::MAX`. 285 if (crate::base64url_nopad_len(super::CRED_ID_MIN_LEN).unwrap_or_else(|| { 286 unreachable!("there is a bug in webauthn_rp::base64url_nopad_len") 287 }) 288 ..=crate::base64url_nopad_len(super::CRED_ID_MAX_LEN).unwrap_or_else(|| { 289 unreachable!("there is a bug in webauthn_rp::base64url_nopad_len") 290 })) 291 .contains(&v.len()) 292 { 293 BASE64URL_NOPAD 294 .decode(v.as_bytes()) 295 .map_err(E::custom) 296 .map(CredentialId) 297 } else { 298 Err(E::invalid_value( 299 Unexpected::Str(v), 300 &"16 to 1023 bytes encoded in base64url without padding", 301 )) 302 } 303 } 304 } 305 deserializer.deserialize_str(CredentialIdVisitor) 306 } 307 } 308 impl<'de> Deserialize<'de> for AuthenticatorAttachment { 309 /// Deserializes [`prim@str`] based on 310 /// [`AuthenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#enumdef-authenticatorattachment). 311 /// 312 /// # Examples 313 /// 314 /// ``` 315 /// # use webauthn_rp::response::AuthenticatorAttachment; 316 /// assert!(matches!( 317 /// serde_json::from_str::<AuthenticatorAttachment>(r#""cross-platform""#)?, 318 /// AuthenticatorAttachment::CrossPlatform) 319 /// ); 320 /// assert!(matches!( 321 /// serde_json::from_str::<AuthenticatorAttachment>(r#""platform""#)?, 322 /// AuthenticatorAttachment::Platform) 323 /// ); 324 /// // Case matters. 325 /// assert!(serde_json::from_str::<AuthenticatorAttachment>(r#""Platform""#).is_err()); 326 /// // `AuthenticatorAttachment::None` is not deserializable. 327 /// assert!(serde_json::from_str::<AuthenticatorAttachment>(r#""""#).is_err()); 328 /// assert!(serde_json::from_str::<AuthenticatorAttachment>("null").is_err()); 329 /// assert!(serde_json::from_str::<AuthenticatorAttachment>(r#""none""#).is_err()); 330 /// assert!(serde_json::from_str::<AuthenticatorAttachment>(r#""None""#).is_err()); 331 /// # Ok::<_, serde_json::Error>(()) 332 ///``` 333 #[inline] 334 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 335 where 336 D: Deserializer<'de>, 337 { 338 /// `Visitor` for `AuthenticatorAttachment`. 339 struct AuthenticatorAttachmentVisitor; 340 impl Visitor<'_> for AuthenticatorAttachmentVisitor { 341 type Value = AuthenticatorAttachment; 342 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 343 formatter.write_str("AuthenticatorAttachment") 344 } 345 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 346 where 347 E: Error, 348 { 349 /// `"platform"` 350 const PLATFORM: &str = "platform"; 351 /// `"cross-platform"` 352 const CROSS_PLATFORM: &str = "cross-platform"; 353 match v { 354 PLATFORM => Ok(AuthenticatorAttachment::Platform), 355 CROSS_PLATFORM => Ok(AuthenticatorAttachment::CrossPlatform), 356 _ => Err(E::invalid_value( 357 Unexpected::Str(v), 358 &format!("'{PLATFORM}' or '{CROSS_PLATFORM}'").as_str(), 359 )), 360 } 361 } 362 } 363 deserializer.deserialize_str(AuthenticatorAttachmentVisitor) 364 } 365 } 366 /// Container of data that was encoded in base64url. 367 pub(super) struct Base64DecodedVal(pub Vec<u8>); 368 impl<'de> Deserialize<'de> for Base64DecodedVal { 369 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 370 where 371 D: Deserializer<'de>, 372 { 373 /// `Visitor` for `Base64DecodedVal`. 374 struct Base64DecodedValVisitor; 375 impl Visitor<'_> for Base64DecodedValVisitor { 376 type Value = Base64DecodedVal; 377 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 378 formatter.write_str("base64url-encoded data") 379 } 380 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 381 where 382 E: Error, 383 { 384 BASE64URL_NOPAD 385 .decode(v.as_bytes()) 386 .map_err(E::custom) 387 .map(Base64DecodedVal) 388 } 389 } 390 deserializer.deserialize_str(Base64DecodedValVisitor) 391 } 392 } 393 impl<'de> Deserialize<'de> for SentChallenge { 394 /// Deserializes `[u8]` or [`prim@str`] based on 395 /// [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-collectedclientdata-challenge). 396 /// 397 /// Specifically a `[u8]` or `str` is base64url-decoded and interpreted as a little-endian 398 /// `u128`. 399 /// 400 /// # Examples 401 /// 402 /// ``` 403 /// # use webauthn_rp::response::SentChallenge; 404 /// assert_eq!( 405 /// serde_json::from_slice::<SentChallenge>(br#""AAAAAAAAAAAAAAAAAAAAAA""#)?, 406 /// SentChallenge(0) 407 /// ); 408 /// # Ok::<_, serde_json::Error>(()) 409 ///``` 410 #[inline] 411 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 412 where 413 D: Deserializer<'de>, 414 { 415 /// `Visitor` for `SentChallenge`. 416 struct ChallengeVisitor; 417 impl Visitor<'_> for ChallengeVisitor { 418 type Value = SentChallenge; 419 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 420 formatter.write_str( 421 "base64 encoding of the 16-byte challenge in a URL safe way without padding", 422 ) 423 } 424 #[expect( 425 clippy::panic_in_result_fn, 426 reason = "we want to crash when there is a bug" 427 )] 428 #[expect( 429 clippy::little_endian_bytes, 430 reason = "SentChallenge::deserialize and Challenge::serialize need to be consistent across architectures" 431 )] 432 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> 433 where 434 E: Error, 435 { 436 if v.len() == Challenge::BASE64_LEN { 437 let mut data = [0; 16]; 438 BASE64URL_NOPAD 439 .decode_mut(v, data.as_mut_slice()) 440 .map_err(|err| E::custom(err.error)) 441 .map(|len| { 442 assert_eq!(len, 16, "there is a bug in BASE64URL_NOPAD::decode_mut"); 443 SentChallenge(u128::from_le_bytes(data)) 444 }) 445 } else { 446 Err(E::invalid_value( 447 Unexpected::Bytes(v), 448 &"22 bytes encoded in base64url without padding", 449 )) 450 } 451 } 452 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 453 where 454 E: Error, 455 { 456 self.visit_bytes(v.as_bytes()) 457 } 458 } 459 deserializer.deserialize_bytes(ChallengeVisitor) 460 } 461 } 462 impl<'de: 'a, 'a> Deserialize<'de> for Origin<'a> { 463 /// Deserializes [`prim@str`] by borrowing the data when possible. 464 /// 465 /// # Examples 466 /// 467 /// ``` 468 /// # extern crate alloc; 469 /// # use alloc::borrow::Cow; 470 /// # use webauthn_rp::response::Origin; 471 /// let origin_borrowed = "https://example.com"; 472 /// let origin_owned = "\\\\https://example.com"; 473 /// assert!( 474 /// matches!(serde_json::from_str::<Origin<'_>>(format!("\"{origin_borrowed}\"").as_str())?.0, Cow::Borrowed(val) if val == origin_borrowed) 475 /// ); 476 /// assert!( 477 /// matches!(serde_json::from_str::<Origin<'_>>(format!("\"{origin_owned}\"").as_str())?.0, Cow::Owned(val) if *val.as_bytes() == origin_owned.as_bytes()[1..]) 478 /// ); 479 /// # Ok::<_, serde_json::Error>(()) 480 ///``` 481 #[inline] 482 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 483 where 484 D: Deserializer<'de>, 485 { 486 /// `Visitor` for `Origin`. 487 struct OriginVisitor<'b>(PhantomData<fn() -> &'b ()>); 488 impl<'d: 'b, 'b> Visitor<'d> for OriginVisitor<'b> { 489 type Value = Origin<'b>; 490 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 491 formatter.write_str("Origin") 492 } 493 fn visit_borrowed_str<E>(self, v: &'d str) -> Result<Self::Value, E> 494 where 495 E: Error, 496 { 497 Ok(Origin(Cow::Borrowed(v))) 498 } 499 fn visit_string<E>(self, v: String) -> Result<Self::Value, E> 500 where 501 E: Error, 502 { 503 Ok(Origin(Cow::Owned(v))) 504 } 505 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 506 where 507 E: Error, 508 { 509 self.visit_string(v.to_owned()) 510 } 511 } 512 deserializer.deserialize_str(OriginVisitor(PhantomData)) 513 } 514 } 515 /// `trait` that returns an empty instance of `Self`. 516 pub(super) trait ClientExtensions: Sized { 517 /// Returns an empty instance of `Self`. 518 fn empty() -> Self; 519 } 520 /// Response for both registration and authentication ceremonies. 521 /// 522 /// [`Self::raw_id`] is always `Some` when `!RELAXED` or `!REG`. 523 /// 524 /// `RELAXED` and `REG` are used purely for deserialization purposes. 525 pub(super) struct PublicKeyCredential<const RELAXED: bool, const REG: bool, AuthResp, Ext> { 526 /// [`rawId`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-rawid). 527 pub id: Option<CredentialId<Vec<u8>>>, 528 /// [`response`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-response). 529 pub response: AuthResp, 530 /// [`authenticatorAttachment`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-authenticatorattachment). 531 pub authenticator_attachment: AuthenticatorAttachment, 532 /// [`getClientExtensionResults`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredential-getclientextensionresults). 533 pub client_extension_results: Ext, 534 } 535 /// `Visitor` for `PublicKeyCredential`. 536 /// 537 /// When `!RELAXED`, `REG` is ignored and all fields must exist and unknown fields are not allowed. 538 /// When `RELAXED`, unknown fields are ignored. 539 /// When `RELAXED` and `REG`, only `response` is required. 540 /// When `RELAXED` and `!REG`, only `id` and `response` are required. 541 struct PublicKeyCredentialVisitor<const RELAXED: bool, const REG: bool, R, E>( 542 pub PhantomData<fn() -> (R, E)>, 543 ); 544 impl<'d, const REL: bool, const REGI: bool, R, E> Visitor<'d> 545 for PublicKeyCredentialVisitor<REL, REGI, R, E> 546 where 547 R: for<'a> Deserialize<'a>, 548 E: for<'a> Deserialize<'a> + ClientExtensions, 549 { 550 type Value = PublicKeyCredential<REL, REGI, R, E>; 551 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 552 formatter.write_str("PublicKeyCredential") 553 } 554 #[expect( 555 clippy::too_many_lines, 556 reason = "rather hide all the internal logic instead instead of moving into an outer scope" 557 )] 558 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> 559 where 560 A: MapAccess<'d>, 561 { 562 /// `PublicKeyCredentialJSON` fields. 563 enum Field<const IGNORE_UNKNOWN: bool> { 564 /// `id`. 565 Id, 566 /// `type`. 567 Type, 568 /// `rawId`. 569 RawId, 570 /// `response`. 571 Response, 572 /// `authenticatorAttachment`. 573 AuthenticatorAttachment, 574 /// `clientExtensionResults`. 575 ClientExtensionResults, 576 /// Unknown field. 577 Other, 578 } 579 impl<'e, const IGNORE: bool> Deserialize<'e> for Field<IGNORE> { 580 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 581 where 582 D: Deserializer<'e>, 583 { 584 /// `Visitor` for `Field`. 585 struct FieldVisitor<const IGNORE_UNKNOWN: bool>; 586 impl<const IGN: bool> Visitor<'_> for FieldVisitor<IGN> { 587 type Value = Field<IGN>; 588 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 589 write!(formatter, "'{ID}', '{TYPE}', '{RAW_ID}', '{RESPONSE}', '{AUTHENTICATOR_ATTACHMENT}', or '{CLIENT_EXTENSION_RESULTS}'") 590 } 591 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 592 where 593 E: Error, 594 { 595 match v { 596 ID => Ok(Field::Id), 597 TYPE => Ok(Field::Type), 598 RAW_ID => Ok(Field::RawId), 599 RESPONSE => Ok(Field::Response), 600 AUTHENTICATOR_ATTACHMENT => Ok(Field::AuthenticatorAttachment), 601 CLIENT_EXTENSION_RESULTS => Ok(Field::ClientExtensionResults), 602 _ => { 603 if IGN { 604 Ok(Field::Other) 605 } else { 606 Err(E::unknown_field(v, REG_FIELDS)) 607 } 608 } 609 } 610 } 611 } 612 deserializer.deserialize_identifier(FieldVisitor::<IGNORE>) 613 } 614 } 615 /// Deserializes the value for type. 616 struct Type; 617 impl<'e> Deserialize<'e> for Type { 618 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 619 where 620 D: Deserializer<'e>, 621 { 622 /// `Visitor` for `Type`. 623 struct TypeVisitor; 624 impl Visitor<'_> for TypeVisitor { 625 type Value = Type; 626 fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result { 627 formatter.write_str(PUBLIC_KEY) 628 } 629 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> 630 where 631 E: Error, 632 { 633 if v == PUBLIC_KEY { 634 Ok(Type) 635 } else { 636 Err(E::invalid_value(Unexpected::Str(v), &PUBLIC_KEY)) 637 } 638 } 639 } 640 deserializer.deserialize_str(TypeVisitor) 641 } 642 } 643 let mut opt_id = None; 644 let mut typ = None; 645 let mut raw = None; 646 let mut resp = None; 647 let mut attach = None; 648 let mut ext = None; 649 while let Some(key) = map.next_key::<Field<REL>>()? { 650 match key { 651 Field::Id => { 652 if opt_id.is_some() { 653 return Err(Error::duplicate_field(ID)); 654 } 655 opt_id = map.next_value::<Option<_>>().map(Some)?; 656 } 657 Field::Type => { 658 if typ.is_some() { 659 return Err(Error::duplicate_field(TYPE)); 660 } 661 typ = map.next_value::<Option<Type>>().map(Some)?; 662 } 663 Field::RawId => { 664 if raw.is_some() { 665 return Err(Error::duplicate_field(RAW_ID)); 666 } 667 raw = map.next_value::<Option<CredentialId<_>>>().map(Some)?; 668 } 669 Field::Response => { 670 if resp.is_some() { 671 return Err(Error::duplicate_field(RESPONSE)); 672 } 673 resp = map.next_value::<R>().map(Some)?; 674 } 675 Field::AuthenticatorAttachment => { 676 if attach.is_some() { 677 return Err(Error::duplicate_field(AUTHENTICATOR_ATTACHMENT)); 678 } 679 attach = map.next_value().map(Some)?; 680 } 681 Field::ClientExtensionResults => { 682 if ext.is_some() { 683 return Err(Error::duplicate_field(CLIENT_EXTENSION_RESULTS)); 684 } 685 ext = map.next_value::<Option<E>>().map(Some)?; 686 } 687 Field::Other => map.next_value::<IgnoredAny>().map(|_| ())?, 688 } 689 } 690 resp.ok_or_else(|| Error::missing_field(RESPONSE)) 691 .and_then(|response| { 692 opt_id.ok_or(false).and_then(|id| id.ok_or(true)).map_or_else( 693 |flag| { 694 if REL && REGI { 695 Ok(None) 696 } else if flag { 697 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{ID} to be a base64url-encoded CredentialId").as_str())) 698 } else { 699 Err(Error::missing_field(ID)) 700 } 701 }, 702 |id| Ok(Some(id)), 703 ) 704 .and_then(|id| { 705 raw.ok_or(false).and_then(|opt_raw_id| opt_raw_id.ok_or(true)).map_or_else( 706 |flag| { 707 if REL { 708 Ok(()) 709 } else if flag { 710 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{RAW_ID} to be a base64url-encoded CredentialId").as_str())) 711 } else { 712 Err(Error::missing_field(RAW_ID)) 713 } 714 }, 715 |raw_id| { 716 id.as_ref().map_or_else( 717 || Ok(()), 718 |i| { 719 if raw_id == i { 720 Ok(()) 721 } else { 722 Err(Error::invalid_value( 723 Unexpected::Bytes(raw_id.as_ref()), 724 &format!("{ID} and {RAW_ID} to match: {i:?}").as_str(), 725 )) 726 } 727 }, 728 ) 729 } 730 ) 731 .and_then(|()| { 732 ext.ok_or(false).and_then(|opt_ext| opt_ext.ok_or(true)).map_or_else( 733 |flag| { 734 if REL { 735 Ok(E::empty()) 736 } else if flag { 737 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{CLIENT_EXTENSION_RESULTS} to be a map of allowed client extensions").as_str())) 738 } else { 739 Err(Error::missing_field(CLIENT_EXTENSION_RESULTS)) 740 } 741 }, 742 Ok 743 ) 744 .and_then(|client_extension_results| { 745 typ.ok_or(false).and_then(|opt_typ| opt_typ.ok_or(true)).map_or_else( 746 |flag| { 747 if REL { 748 Ok(()) 749 } else if flag { 750 Err(Error::invalid_type(Unexpected::Other("null"), &format!("{TYPE} to be '{PUBLIC_KEY}'").as_str())) 751 } else { 752 Err(Error::missing_field(TYPE)) 753 } 754 }, 755 |_| Ok(()), 756 ).map(|()| PublicKeyCredential { 757 id, 758 response, 759 authenticator_attachment: attach.flatten().unwrap_or(AuthenticatorAttachment::None), 760 client_extension_results, 761 }) 762 }) 763 }) 764 }) 765 }) 766 } 767 } 768 /// `"id"`. 769 const ID: &str = "id"; 770 /// `"type"`. 771 const TYPE: &str = "type"; 772 /// `"rawId"`. 773 const RAW_ID: &str = "rawId"; 774 /// `"response"`. 775 const RESPONSE: &str = "response"; 776 /// `"authenticatorAttachment"`. 777 const AUTHENTICATOR_ATTACHMENT: &str = "authenticatorAttachment"; 778 /// `"clientExtensionResults"`. 779 const CLIENT_EXTENSION_RESULTS: &str = "clientExtensionResults"; 780 /// `"public-key"`. 781 const PUBLIC_KEY: &str = "public-key"; 782 /// Fields for `PublicKeyCredentialJSON`. 783 const REG_FIELDS: &[&str; 6] = &[ 784 ID, 785 TYPE, 786 RAW_ID, 787 RESPONSE, 788 AUTHENTICATOR_ATTACHMENT, 789 CLIENT_EXTENSION_RESULTS, 790 ]; 791 impl<'de, const REL: bool, const REGI: bool, R, E> Deserialize<'de> 792 for PublicKeyCredential<REL, REGI, R, E> 793 where 794 R: for<'a> Deserialize<'a>, 795 E: for<'a> Deserialize<'a> + ClientExtensions, 796 { 797 /// Deserializes a `struct` based on 798 /// [`PublicKeyCredentialJSON`](https://www.w3.org/TR/webauthn-3/#typedefdef-publickeycredentialjson). 799 /// 800 /// `REL` iff unknown fields should be ignored and not cause an error. 801 /// `REGI` iff `Self` is from a registration ceremony. 802 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 803 where 804 D: Deserializer<'de>, 805 { 806 deserializer.deserialize_struct( 807 "PublicKeyCredential", 808 REG_FIELDS, 809 PublicKeyCredentialVisitor::<REL, REGI, _, _>(PhantomData), 810 ) 811 } 812 } 813 impl Serialize for AllAcceptedCredentialsOptions<'_, '_> { 814 /// Serializes `self` to conform with 815 /// [`AllAcceptedCredentialsOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-allacceptedcredentialsoptions). 816 /// 817 /// # Examples 818 /// 819 /// ``` 820 /// # use core::str::FromStr; 821 /// # #[cfg(feature = "bin")] 822 /// # use webauthn_rp::bin::Decode; 823 /// # use webauthn_rp::{ 824 /// # request::{register::UserHandle, AsciiDomain, RpId}, 825 /// # response::{error::CredentialIdErr, AllAcceptedCredentialsOptions, CredentialId}, 826 /// # }; 827 /// /// Retrieves the `CredentialId`s associated with `user_id` from the database. 828 /// # #[cfg(all(feature = "bin", feature = "custom"))] 829 /// fn get_credential_ids(user_id: UserHandle<&[u8]>) -> Result<Vec<CredentialId<Vec<u8>>>, CredentialIdErr> { 830 /// // ⋮ 831 /// # CredentialId::decode(vec![0; 16]).map(|cred_id| vec![cred_id]) 832 /// } 833 /// /// Retrieves the `UserHandle` from a session cookie. 834 /// # #[cfg(feature = "custom")] 835 /// fn get_user_handle() -> UserHandle<Vec<u8>> { 836 /// // ⋮ 837 /// # UserHandle::try_from(vec![0]).unwrap() 838 /// } 839 /// # #[cfg(feature = "custom")] 840 /// let user_id = get_user_handle(); 841 /// # #[cfg(all(feature = "bin", feature = "custom"))] 842 /// assert_eq!( 843 /// serde_json::to_string(&AllAcceptedCredentialsOptions { 844 /// rp_id: &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?), 845 /// user_id: (&user_id).into(), 846 /// all_accepted_credential_ids: get_credential_ids((&user_id).into())?, 847 /// }) 848 /// .unwrap(), 849 /// r#"{"rpId":"example.com","userId":"AA","allAcceptedCredentialIds":["AAAAAAAAAAAAAAAAAAAAAA"]}"# 850 /// ); 851 /// # Ok::<_, webauthn_rp::AggErr>(()) 852 /// ``` 853 #[inline] 854 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 855 where 856 S: Serializer, 857 { 858 serializer 859 .serialize_struct("AllAcceptedCredentialsOptions", 3) 860 .and_then(|mut ser| { 861 ser.serialize_field("rpId", self.rp_id).and_then(|()| { 862 ser.serialize_field("userId", &self.user_id).and_then(|()| { 863 ser.serialize_field( 864 "allAcceptedCredentialIds", 865 &self.all_accepted_credential_ids, 866 ) 867 .and_then(|()| ser.end()) 868 }) 869 }) 870 }) 871 } 872 } 873 impl Serialize for CurrentUserDetailsOptions<'_, '_, '_, '_> { 874 /// Serializes `self` to conform with 875 /// [`CurrentUserDetailsOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-currentuserdetailsoptions). 876 /// 877 /// # Examples 878 /// 879 /// ``` 880 /// # use core::str::FromStr; 881 /// # #[cfg(feature = "bin")] 882 /// # use webauthn_rp::bin::Decode; 883 /// # use webauthn_rp::{ 884 /// # request::{register::{Nickname, PublicKeyCredentialUserEntity, UserHandle, Username}, AsciiDomain, RpId}, 885 /// # response::CurrentUserDetailsOptions, 886 /// # AggErr, 887 /// # }; 888 /// /// Retrieves the `PublicKeyCredentialUserEntity` info associated with `user_id` from the database. 889 /// # #[cfg(feature = "bin")] 890 /// fn get_user_info(user_id: UserHandle<&[u8]>) -> Result<(Username, Option<Nickname>), AggErr> { 891 /// // ⋮ 892 /// # Ok((Username::decode("foo".to_owned()).unwrap(), Some(Nickname::decode("foo".to_owned()).unwrap()))) 893 /// } 894 /// /// Retrieves the `UserHandle` from a session cookie. 895 /// # #[cfg(feature = "custom")] 896 /// fn get_user_handle() -> UserHandle<Vec<u8>> { 897 /// // ⋮ 898 /// # UserHandle::try_from(vec![0]).unwrap() 899 /// } 900 /// # #[cfg(feature = "custom")] 901 /// let user_handle = get_user_handle(); 902 /// # #[cfg(all(feature = "bin", feature = "custom"))] 903 /// let id = (&user_handle).into(); 904 /// # #[cfg(all(feature = "bin", feature = "custom"))] 905 /// let (name, display_name) = get_user_info(id)?; 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, 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 }