1use base64urlsafedata::Base64UrlSafeData;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Serialize, Clone, Copy, Deserialize, PartialEq, Eq)]
8#[serde(rename_all = "camelCase")]
9#[repr(u8)]
10pub enum CredentialProtectionPolicy {
11 UserVerificationOptional = 0x1,
16 UserVerificationOptionalWithCredentialIDList = 0x2,
20 UserVerificationRequired = 0x3,
23}
24
25impl TryFrom<u8> for CredentialProtectionPolicy {
26 type Error = &'static str;
27
28 fn try_from(v: u8) -> Result<Self, Self::Error> {
29 use CredentialProtectionPolicy::*;
30 match v {
31 0x1 => Ok(UserVerificationOptional),
32 0x2 => Ok(UserVerificationOptionalWithCredentialIDList),
33 0x3 => Ok(UserVerificationRequired),
34 _ => Err("Invalid policy number"),
35 }
36 }
37}
38
39#[derive(Debug, Serialize, Clone, Deserialize, PartialEq, Eq)]
43#[serde(rename_all = "camelCase")]
44pub struct CredProtect {
45 pub credential_protection_policy: CredentialProtectionPolicy,
47 #[serde(skip_serializing_if = "Option::is_none")]
51 pub enforce_credential_protection_policy: Option<bool>,
52}
53
54#[derive(Debug, Serialize, Clone, Deserialize)]
58#[serde(rename_all = "camelCase")]
59pub struct RequestRegistrationExtensions {
60 #[serde(flatten, skip_serializing_if = "Option::is_none")]
62 pub cred_protect: Option<CredProtect>,
63
64 #[serde(skip_serializing_if = "Option::is_none")]
67 pub uvm: Option<bool>,
68
69 #[serde(skip_serializing_if = "Option::is_none")]
73 pub cred_props: Option<bool>,
74
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub min_pin_length: Option<bool>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
82 pub hmac_create_secret: Option<bool>,
83}
84
85impl Default for RequestRegistrationExtensions {
86 fn default() -> Self {
87 RequestRegistrationExtensions {
88 cred_protect: None,
89 uvm: Some(true),
90 cred_props: Some(true),
91 min_pin_length: None,
92 hmac_create_secret: None,
93 }
94 }
95}
96
97#[allow(clippy::from_over_into)]
99#[cfg(feature = "wasm")]
100impl Into<js_sys::Object> for &RequestRegistrationExtensions {
101 fn into(self) -> js_sys::Object {
102 use js_sys::Object;
103 use wasm_bindgen::JsValue;
104
105 let RequestRegistrationExtensions {
106 cred_protect,
107 uvm,
108 cred_props,
109 min_pin_length,
110 hmac_create_secret,
111 } = self;
112
113 let obj = Object::new();
114
115 if let Some(cred_protect) = cred_protect {
116 let jsv = serde_wasm_bindgen::to_value(&cred_protect).unwrap();
117 js_sys::Reflect::set(&obj, &"credProtect".into(), &jsv).unwrap();
118 }
119
120 if let Some(uvm) = uvm {
121 js_sys::Reflect::set(&obj, &"uvm".into(), &JsValue::from_bool(*uvm)).unwrap();
122 }
123
124 if let Some(cred_props) = cred_props {
125 js_sys::Reflect::set(&obj, &"credProps".into(), &JsValue::from_bool(*cred_props))
126 .unwrap();
127 }
128
129 if let Some(min_pin_length) = min_pin_length {
130 js_sys::Reflect::set(
131 &obj,
132 &"minPinLength".into(),
133 &JsValue::from_bool(*min_pin_length),
134 )
135 .unwrap();
136 }
137
138 if let Some(hmac_create_secret) = hmac_create_secret {
139 js_sys::Reflect::set(
140 &obj,
141 &"hmacCreateSecret".into(),
142 &JsValue::from_bool(*hmac_create_secret),
143 )
144 .unwrap();
145 }
146
147 obj
148 }
149}
150
151#[derive(Debug, Serialize, Clone, Deserialize, PartialEq, Eq)]
157#[serde(rename_all = "camelCase")]
158pub struct HmacGetSecretInput {
159 pub output1: Base64UrlSafeData,
161 pub output2: Option<Base64UrlSafeData>,
163}
164
165#[derive(Debug, Serialize, Clone, Deserialize)]
169#[serde(rename_all = "camelCase")]
170pub struct RequestAuthenticationExtensions {
171 #[serde(skip_serializing_if = "Option::is_none")]
173 pub appid: Option<String>,
174
175 #[serde(skip_serializing_if = "Option::is_none")]
178 pub uvm: Option<bool>,
179
180 #[serde(skip_serializing_if = "Option::is_none")]
184 pub hmac_get_secret: Option<HmacGetSecretInput>,
185}
186
187#[allow(clippy::from_over_into)]
189#[cfg(feature = "wasm")]
190impl Into<js_sys::Object> for &RequestAuthenticationExtensions {
191 fn into(self) -> js_sys::Object {
192 use js_sys::{Object, Uint8Array};
193 use wasm_bindgen::JsValue;
194
195 let RequestAuthenticationExtensions {
196 appid: _,
198 uvm,
199 hmac_get_secret,
200 } = self;
201
202 let obj = Object::new();
203
204 if let Some(uvm) = uvm {
205 js_sys::Reflect::set(&obj, &"uvm".into(), &JsValue::from_bool(*uvm)).unwrap();
206 }
207
208 if let Some(HmacGetSecretInput { output1, output2 }) = hmac_get_secret {
209 let hmac = Object::new();
210
211 let o1 = Uint8Array::from(output1.as_slice());
212 js_sys::Reflect::set(&hmac, &"output1".into(), &o1).unwrap();
213
214 if let Some(output2) = output2 {
215 let o2 = Uint8Array::from(output2.as_slice());
216 js_sys::Reflect::set(&hmac, &"output2".into(), &o2).unwrap();
217 }
218
219 js_sys::Reflect::set(&obj, &"hmacGetSecret".into(), &hmac).unwrap();
220 }
221
222 obj
223 }
224}
225
226#[derive(Debug, Serialize, Clone, Deserialize, PartialEq, Eq)]
228#[serde(rename_all = "camelCase")]
229pub struct HmacGetSecretOutput {
230 pub output1: Base64UrlSafeData,
232 pub output2: Option<Base64UrlSafeData>,
234}
235
236#[derive(Debug, Deserialize, Serialize, Clone, Default)]
239pub struct AuthenticationExtensionsClientOutputs {
240 #[serde(default)]
242 pub appid: Option<bool>,
243 #[serde(default)]
245 pub hmac_get_secret: Option<HmacGetSecretOutput>,
246}
247
248#[cfg(feature = "wasm")]
249impl From<web_sys::AuthenticationExtensionsClientOutputs>
250 for AuthenticationExtensionsClientOutputs
251{
252 fn from(
253 ext: web_sys::AuthenticationExtensionsClientOutputs,
254 ) -> AuthenticationExtensionsClientOutputs {
255 use js_sys::Uint8Array;
256
257 let appid = js_sys::Reflect::get(&ext, &"appid".into())
258 .ok()
259 .and_then(|jv| jv.as_bool());
260
261 let hmac_get_secret = js_sys::Reflect::get(&ext, &"hmacGetSecret".into())
262 .ok()
263 .and_then(|jv| {
264 let output2 = js_sys::Reflect::get(&jv, &"output2".into())
265 .map(|v| Uint8Array::new(&v).to_vec())
266 .map(Base64UrlSafeData::from)
267 .ok();
268
269 let output1 = js_sys::Reflect::get(&jv, &"output1".into())
270 .map(|v| Uint8Array::new(&v).to_vec())
271 .map(Base64UrlSafeData::from)
272 .ok();
273
274 output1.map(|output1| HmacGetSecretOutput { output1, output2 })
275 });
276
277 AuthenticationExtensionsClientOutputs {
278 appid,
279 hmac_get_secret,
280 }
281 }
282}
283
284#[derive(Debug, Deserialize, Serialize, Clone)]
286pub struct CredProps {
287 pub rk: bool,
293}
294
295#[derive(Debug, Deserialize, Serialize, Clone, Default)]
298#[serde(rename_all = "camelCase")]
299pub struct RegistrationExtensionsClientOutputs {
300 #[serde(default, skip_serializing_if = "Option::is_none")]
302 pub appid: Option<bool>,
303
304 #[serde(default, skip_serializing_if = "Option::is_none")]
307 pub cred_props: Option<CredProps>,
308
309 #[serde(default, skip_serializing_if = "Option::is_none")]
311 pub hmac_secret: Option<bool>,
312
313 #[serde(default, skip_serializing_if = "Option::is_none")]
315 pub cred_protect: Option<CredentialProtectionPolicy>,
316
317 #[serde(default, skip_serializing_if = "Option::is_none")]
319 pub min_pin_length: Option<u32>,
320}
321
322#[cfg(feature = "wasm")]
323impl From<web_sys::AuthenticationExtensionsClientOutputs> for RegistrationExtensionsClientOutputs {
324 fn from(
325 ext: web_sys::AuthenticationExtensionsClientOutputs,
326 ) -> RegistrationExtensionsClientOutputs {
327 let appid = js_sys::Reflect::get(&ext, &"appid".into())
328 .ok()
329 .and_then(|jv| jv.as_bool());
330
331 let cred_props = js_sys::Reflect::get(&ext, &"credProps".into())
333 .ok()
334 .and_then(|cred_props_struct| {
335 js_sys::Reflect::get(&cred_props_struct, &"rk".into())
336 .ok()
337 .and_then(|jv| jv.as_bool())
338 .map(|rk| CredProps { rk })
339 });
340
341 let hmac_secret = js_sys::Reflect::get(&ext, &"hmac-secret".into())
342 .ok()
343 .and_then(|jv| jv.as_bool());
344
345 let cred_protect = js_sys::Reflect::get(&ext, &"credProtect".into())
346 .ok()
347 .and_then(|jv| jv.as_f64())
348 .and_then(|f| CredentialProtectionPolicy::try_from(f as u8).ok());
349
350 let min_pin_length = js_sys::Reflect::get(&ext, &"minPinLength".into())
351 .ok()
352 .and_then(|jv| jv.as_f64())
353 .map(|f| f as u32);
354
355 RegistrationExtensionsClientOutputs {
356 appid,
357 cred_props,
358 hmac_secret,
359 cred_protect,
360 min_pin_length,
361 }
362 }
363}
364
365#[derive(Clone, Debug, Default, Serialize, Deserialize)]
367pub enum ExtnState<T>
368where
369 T: Clone + std::fmt::Debug,
370{
371 #[default]
373 NotRequested,
374 Ignored,
376 Set(T),
378 Unsolicited(T),
380 Unsigned(T),
383}
384
385#[derive(Clone, Debug, Default, Serialize, Deserialize)]
387pub struct RegisteredExtensions {
388 #[serde(default)]
392 pub cred_protect: ExtnState<CredentialProtectionPolicy>,
393 #[serde(default)]
395 pub hmac_create_secret: ExtnState<bool>,
396 #[serde(default)]
398 pub appid: ExtnState<bool>,
399 #[serde(default)]
401 pub cred_props: ExtnState<CredProps>,
402}
403
404impl RegisteredExtensions {
405 pub fn none() -> Self {
407 RegisteredExtensions {
408 cred_protect: ExtnState::NotRequested,
409 hmac_create_secret: ExtnState::NotRequested,
410 appid: ExtnState::NotRequested,
411 cred_props: ExtnState::NotRequested,
412 }
413 }
414}
415
416#[derive(Clone, Debug, Serialize, Deserialize)]
418pub struct AuthenticationExtensions {}