webauthn_rp

WebAuthn Level 3 RP library.
git clone https://git.philomathiclife.com/repos/webauthn_rp
Log | Files | Refs | README

ser.rs (13017B)


      1 use super::{
      2     super::BASE64URL_NOPAD_ENC, Challenge, CredentialId, Hint, PublicKeyCredentialDescriptor, RpId,
      3     UserVerificationRequirement,
      4 };
      5 use core::str;
      6 use serde::ser::{Serialize, SerializeSeq as _, SerializeStruct as _, Serializer};
      7 impl Serialize for Challenge {
      8     /// Serializes `self` to conform with
      9     /// [`challenge`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptionsjson-challenge).
     10     ///
     11     /// Specifically `self` is interpreted as a little-endian array of 16 bytes that is then transformed into a
     12     /// base64url-encoded string.
     13     ///
     14     /// # Examples
     15     ///
     16     /// ```
     17     /// # use webauthn_rp::request::Challenge;
     18     /// # // `Challenge::BASE64_LEN` is 22, but we add two for the quotes.
     19     /// assert_eq!(serde_json::to_string(&Challenge::new())?.len(), 24);
     20     /// # Ok::<_, serde_json::Error>(())
     21     /// ```
     22     #[expect(clippy::unreachable, reason = "when there is a bug, we want to crash")]
     23     #[expect(
     24         clippy::little_endian_bytes,
     25         reason = "SentChallenge::deserialize and Challenge::serialize need to be consistent across architectures"
     26     )]
     27     #[inline]
     28     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     29     where
     30         S: Serializer,
     31     {
     32         let mut data = [0; Self::BASE64_LEN];
     33         BASE64URL_NOPAD_ENC.encode_mut(self.0.to_le_bytes().as_slice(), data.as_mut_slice());
     34         serializer.serialize_str(
     35             str::from_utf8(data.as_slice())
     36                 // There is a bug, so crash and burn.
     37                 .unwrap_or_else(|_| unreachable!("there is a bug in Challenge::serialize")),
     38         )
     39     }
     40 }
     41 impl Serialize for RpId {
     42     /// Serializes `self` as a [`prim@str`].
     43     ///
     44     /// # Examples
     45     ///
     46     /// ```
     47     /// # use webauthn_rp::request::{AsciiDomain, RpId};
     48     /// assert_eq!(
     49     ///     serde_json::to_string(&RpId::Domain(AsciiDomain::try_from("www.example.com".to_owned()).unwrap())).unwrap(),
     50     ///     r#""www.example.com""#
     51     /// );
     52     /// assert_eq!(
     53     ///     serde_json::to_string(&RpId::Url("ssh:foo".parse().unwrap())).unwrap(),
     54     ///     r#""ssh:foo""#
     55     /// );
     56     /// # Ok::<_, serde_json::Error>(())
     57     /// ```
     58     #[inline]
     59     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     60     where
     61         S: Serializer,
     62     {
     63         serializer.serialize_str(self.as_ref())
     64     }
     65 }
     66 impl<T> Serialize for PublicKeyCredentialDescriptor<T>
     67 where
     68     CredentialId<T>: Serialize,
     69 {
     70     /// Serializes `self` to conform with
     71     /// [`PublicKeyCredentialDescriptorJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialdescriptorjson).
     72     ///
     73     /// # Examples
     74     ///
     75     /// ```
     76     /// # #[cfg(all(feature = "bin", feature = "custom"))]
     77     /// # use webauthn_rp::{bin::Decode, response::bin::DecodeAuthTransportsErr};
     78     /// # use webauthn_rp::{
     79     /// #     request::PublicKeyCredentialDescriptor,
     80     /// #     response::{AuthTransports, CredentialId},
     81     /// # };
     82     /// /// Retrieves the `AuthTransports` associated with the unique `cred_id`
     83     /// /// from the database.
     84     /// # #[cfg(all(feature = "bin", feature = "custom"))]
     85     /// fn get_transports(cred_id: CredentialId<&[u8]>) -> Result<AuthTransports, DecodeAuthTransportsErr> {
     86     ///     // ⋮
     87     /// #     AuthTransports::decode(32)
     88     /// }
     89     /// // `CredentialId::try_from` only exists when `custom` is enabled; and even then, it is
     90     /// // likely never needed since the `CredentialId` was originally sent from the client and is likely
     91     /// // stored in a database which would be fetched by `UserHandle` or `Authentication::raw_id`.
     92     /// # #[cfg(all(feature = "bin", feature = "custom"))]
     93     /// let id = CredentialId::try_from(vec![0; 16])?;
     94     /// # #[cfg(all(feature = "bin", feature = "custom"))]
     95     /// let transports = get_transports((&id).into())?;
     96     /// # #[cfg(all(feature = "bin", feature = "custom"))]
     97     /// assert_eq!(
     98     ///     serde_json::to_string(&PublicKeyCredentialDescriptor { id, transports }).unwrap(),
     99     ///     r#"{"type":"public-key","id":"AAAAAAAAAAAAAAAAAAAAAA","transports":["usb"]}"#
    100     /// );
    101     /// # Ok::<_, webauthn_rp::AggErr>(())
    102     /// ```
    103     #[inline]
    104     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    105     where
    106         S: Serializer,
    107     {
    108         serializer
    109             .serialize_struct("PublicKeyCredentialDescriptor", 3)
    110             .and_then(|mut ser| {
    111                 ser.serialize_field("type", "public-key").and_then(|()| {
    112                     ser.serialize_field("id", &self.id).and_then(|()| {
    113                         ser.serialize_field("transports", &self.transports)
    114                             .and_then(|()| ser.end())
    115                     })
    116                 })
    117             })
    118     }
    119 }
    120 impl Serialize for UserVerificationRequirement {
    121     /// Serializes `self` to conform with
    122     /// [`UserVerificationRequirement`](https://www.w3.org/TR/webauthn-3/#enumdef-userverificationrequirement).
    123     ///
    124     /// # Examples
    125     ///
    126     /// ```
    127     /// # use webauthn_rp::request::UserVerificationRequirement;
    128     /// assert_eq!(
    129     ///     serde_json::to_string(&UserVerificationRequirement::Required)?,
    130     ///     r#""required""#
    131     /// );
    132     /// assert_eq!(
    133     ///     serde_json::to_string(&UserVerificationRequirement::Discouraged)?,
    134     ///     r#""discouraged""#
    135     /// );
    136     /// assert_eq!(
    137     ///     serde_json::to_string(&UserVerificationRequirement::Preferred)?,
    138     ///     r#""preferred""#
    139     /// );
    140     /// # Ok::<_, serde_json::Error>(())
    141     /// ```
    142     #[inline]
    143     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    144     where
    145         S: Serializer,
    146     {
    147         serializer.serialize_str(match *self {
    148             Self::Required => "required",
    149             Self::Discouraged => "discouraged",
    150             Self::Preferred => "preferred",
    151         })
    152     }
    153 }
    154 impl Serialize for Hint {
    155     /// Serializes `self` to conform with
    156     /// [`hints`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialcreationoptionsjson-hints).
    157     ///
    158     /// # Examples
    159     ///
    160     /// ```
    161     /// # use webauthn_rp::request::Hint;
    162     /// assert_eq!(
    163     ///     serde_json::to_string(&Hint::None)?,
    164     ///     r#"[]"#
    165     /// );
    166     /// assert_eq!(
    167     ///     serde_json::to_string(&Hint::SecurityKey)?,
    168     ///     r#"["security-key"]"#
    169     /// );
    170     /// assert_eq!(
    171     ///     serde_json::to_string(&Hint::ClientDevice)?,
    172     ///     r#"["client-device"]"#
    173     /// );
    174     /// assert_eq!(
    175     ///     serde_json::to_string(&Hint::Hybrid)?,
    176     ///     r#"["hybrid"]"#
    177     /// );
    178     /// assert_eq!(
    179     ///     serde_json::to_string(&Hint::SecurityKeyClientDevice)?,
    180     ///     r#"["security-key","client-device"]"#
    181     /// );
    182     /// assert_eq!(
    183     ///     serde_json::to_string(&Hint::ClientDeviceSecurityKey)?,
    184     ///     r#"["client-device","security-key"]"#
    185     /// );
    186     /// assert_eq!(
    187     ///     serde_json::to_string(&Hint::SecurityKeyHybrid)?,
    188     ///     r#"["security-key","hybrid"]"#
    189     /// );
    190     /// assert_eq!(
    191     ///     serde_json::to_string(&Hint::HybridSecurityKey)?,
    192     ///     r#"["hybrid","security-key"]"#
    193     /// );
    194     /// assert_eq!(
    195     ///     serde_json::to_string(&Hint::ClientDeviceHybrid)?,
    196     ///     r#"["client-device","hybrid"]"#
    197     /// );
    198     /// assert_eq!(
    199     ///     serde_json::to_string(&Hint::HybridClientDevice)?,
    200     ///     r#"["hybrid","client-device"]"#
    201     /// );
    202     /// assert_eq!(
    203     ///     serde_json::to_string(&Hint::SecurityKeyClientDeviceHybrid)?,
    204     ///     r#"["security-key","client-device","hybrid"]"#
    205     /// );
    206     /// assert_eq!(
    207     ///     serde_json::to_string(&Hint::SecurityKeyHybridClientDevice)?,
    208     ///     r#"["security-key","hybrid","client-device"]"#
    209     /// );
    210     /// assert_eq!(
    211     ///     serde_json::to_string(&Hint::ClientDeviceSecurityKeyHybrid)?,
    212     ///     r#"["client-device","security-key","hybrid"]"#
    213     /// );
    214     /// assert_eq!(
    215     ///     serde_json::to_string(&Hint::ClientDeviceHybridSecurityKey)?,
    216     ///     r#"["client-device","hybrid","security-key"]"#
    217     /// );
    218     /// assert_eq!(
    219     ///     serde_json::to_string(&Hint::HybridSecurityKeyClientDevice)?,
    220     ///     r#"["hybrid","security-key","client-device"]"#
    221     /// );
    222     /// assert_eq!(
    223     ///     serde_json::to_string(&Hint::HybridClientDeviceSecurityKey)?,
    224     ///     r#"["hybrid","client-device","security-key"]"#
    225     /// );
    226     /// # Ok::<_, serde_json::Error>(())
    227     /// ```
    228     #[inline]
    229     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    230     where
    231         S: Serializer,
    232     {
    233         /// [`security-key`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialhints-security-key).
    234         const SECURITY_KEY: &str = "security-key";
    235         /// [`client-device`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialhints-client-device).
    236         const CLIENT_DEVICE: &str = "client-device";
    237         /// [`hybrid`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialhints-hybrid).
    238         const HYBRID: &str = "hybrid";
    239         let count = match *self {
    240             Self::None => 0,
    241             Self::SecurityKey | Self::ClientDevice | Self::Hybrid => 1,
    242             Self::SecurityKeyClientDevice
    243             | Self::ClientDeviceSecurityKey
    244             | Self::SecurityKeyHybrid
    245             | Self::HybridSecurityKey
    246             | Self::ClientDeviceHybrid
    247             | Self::HybridClientDevice => 2,
    248             Self::SecurityKeyClientDeviceHybrid
    249             | Self::SecurityKeyHybridClientDevice
    250             | Self::ClientDeviceSecurityKeyHybrid
    251             | Self::ClientDeviceHybridSecurityKey
    252             | Self::HybridSecurityKeyClientDevice
    253             | Self::HybridClientDeviceSecurityKey => 3,
    254         };
    255         serializer.serialize_seq(Some(count)).and_then(|mut ser| {
    256             match *self {
    257                 Self::None => Ok(()),
    258                 Self::SecurityKey => ser.serialize_element(SECURITY_KEY),
    259                 Self::ClientDevice => ser.serialize_element(CLIENT_DEVICE),
    260                 Self::Hybrid => ser.serialize_element(HYBRID),
    261                 Self::SecurityKeyClientDevice => ser
    262                     .serialize_element(SECURITY_KEY)
    263                     .and_then(|()| ser.serialize_element(CLIENT_DEVICE)),
    264                 Self::ClientDeviceSecurityKey => ser
    265                     .serialize_element(CLIENT_DEVICE)
    266                     .and_then(|()| ser.serialize_element(SECURITY_KEY)),
    267                 Self::SecurityKeyHybrid => ser
    268                     .serialize_element(SECURITY_KEY)
    269                     .and_then(|()| ser.serialize_element(HYBRID)),
    270                 Self::HybridSecurityKey => ser
    271                     .serialize_element(HYBRID)
    272                     .and_then(|()| ser.serialize_element(SECURITY_KEY)),
    273                 Self::ClientDeviceHybrid => ser
    274                     .serialize_element(CLIENT_DEVICE)
    275                     .and_then(|()| ser.serialize_element(HYBRID)),
    276                 Self::HybridClientDevice => ser
    277                     .serialize_element(HYBRID)
    278                     .and_then(|()| ser.serialize_element(CLIENT_DEVICE)),
    279                 Self::SecurityKeyClientDeviceHybrid => {
    280                     ser.serialize_element(SECURITY_KEY).and_then(|()| {
    281                         ser.serialize_element(CLIENT_DEVICE)
    282                             .and_then(|()| ser.serialize_element(HYBRID))
    283                     })
    284                 }
    285                 Self::SecurityKeyHybridClientDevice => {
    286                     ser.serialize_element(SECURITY_KEY).and_then(|()| {
    287                         ser.serialize_element(HYBRID)
    288                             .and_then(|()| ser.serialize_element(CLIENT_DEVICE))
    289                     })
    290                 }
    291                 Self::ClientDeviceSecurityKeyHybrid => {
    292                     ser.serialize_element(CLIENT_DEVICE).and_then(|()| {
    293                         ser.serialize_element(SECURITY_KEY)
    294                             .and_then(|()| ser.serialize_element(HYBRID))
    295                     })
    296                 }
    297                 Self::ClientDeviceHybridSecurityKey => {
    298                     ser.serialize_element(CLIENT_DEVICE).and_then(|()| {
    299                         ser.serialize_element(HYBRID)
    300                             .and_then(|()| ser.serialize_element(SECURITY_KEY))
    301                     })
    302                 }
    303                 Self::HybridSecurityKeyClientDevice => {
    304                     ser.serialize_element(HYBRID).and_then(|()| {
    305                         ser.serialize_element(SECURITY_KEY)
    306                             .and_then(|()| ser.serialize_element(CLIENT_DEVICE))
    307                     })
    308                 }
    309                 Self::HybridClientDeviceSecurityKey => {
    310                     ser.serialize_element(HYBRID).and_then(|()| {
    311                         ser.serialize_element(CLIENT_DEVICE)
    312                             .and_then(|()| ser.serialize_element(SECURITY_KEY))
    313                     })
    314                 }
    315             }
    316             .and_then(|()| ser.end())
    317         })
    318     }
    319 }