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 }