webauthn_authenticator_rs/ctap2/
ctap21_cred.rs1#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
3use std::ops::{Deref, DerefMut};
4
5#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
6use async_trait::async_trait;
7#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
8use webauthn_rs_proto::UserVerificationPolicy;
9
10use crate::ui::UiCallback;
11
12#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
13use crate::{
14 crypto::SHA256Hash,
15 ctap2::{
16 commands::{
17 CredSubCommand, CredentialManagementRequestTrait, CredentialManagementResponse,
18 CredentialStorageMetadata, DiscoverableCredential, Permissions,
19 PublicKeyCredentialDescriptorCM, RelyingPartyCM,
20 },
21 ctap20::AuthSession,
22 Ctap20Authenticator,
23 },
24 error::{CtapError, WebauthnCError},
25 transport::Token,
26};
27
28pub trait CredentialManagementAuthenticatorInfo<U: UiCallback>: Sync + Send {
30 #[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
31 type RequestType: CredentialManagementRequestTrait;
33
34 fn supports_credential_management(&self) -> bool;
36}
37
38#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
39#[async_trait]
41pub(super) trait CredentialManagementAuthenticatorSupport<T, U, R>
42where
43 T: CredentialManagementAuthenticatorInfo<U, RequestType = R>,
44 U: UiCallback,
45 R: CredentialManagementRequestTrait,
46{
47 async fn cred_mgmt(
48 &mut self,
49 sub_command: CredSubCommand,
50 ) -> Result<CredentialManagementResponse, WebauthnCError>;
51
52 #[allow(dead_code)]
54 async fn cred_mgmt_with_session(
55 &mut self,
56 sub_command: CredSubCommand,
57 auth_session: &AuthSession,
58 ) -> Result<CredentialManagementResponse, WebauthnCError>;
59}
60
61#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
62#[async_trait]
63impl<'a, K, T, U, R> CredentialManagementAuthenticatorSupport<T, U, R> for T
64where
65 K: Token,
66 T: CredentialManagementAuthenticatorInfo<U, RequestType = R>
67 + Deref<Target = Ctap20Authenticator<'a, K, U>>
68 + DerefMut<Target = Ctap20Authenticator<'a, K, U>>,
69 U: UiCallback + 'a,
70 R: CredentialManagementRequestTrait,
71{
72 async fn cred_mgmt(
73 &mut self,
74 sub_command: CredSubCommand,
75 ) -> Result<CredentialManagementResponse, WebauthnCError> {
76 let (pin_uv_auth_proto, pin_uv_auth_param) = self
77 .get_pin_uv_auth_token(
78 sub_command.prf().as_slice(),
79 Permissions::CREDENTIAL_MANAGEMENT,
80 None,
81 UserVerificationPolicy::Required,
82 )
83 .await?
84 .into_pin_uv_params();
85
86 let ui = self.ui_callback;
87 let r = self
88 .token
89 .transmit(
90 T::RequestType::new(sub_command, pin_uv_auth_proto, pin_uv_auth_param),
91 ui,
92 )
93 .await?;
94
95 Ok(r)
96 }
97
98 async fn cred_mgmt_with_session(
99 &mut self,
100 sub_command: CredSubCommand,
101 auth_session: &AuthSession,
102 ) -> Result<CredentialManagementResponse, WebauthnCError> {
103 let (pin_uv_protocol, pin_uv_auth_param) = match auth_session {
104 AuthSession::InterfaceToken(iface, pin_token) => {
105 let mut pin_uv_auth_param =
106 iface.authenticate(pin_token, sub_command.prf().as_slice())?;
107 pin_uv_auth_param.truncate(16);
108
109 (Some(iface.get_pin_uv_protocol()), Some(pin_uv_auth_param))
110 }
111
112 _ => (None, None),
113 };
114
115 let ui = self.ui_callback;
116 self.token
117 .transmit(
118 T::RequestType::new(sub_command, pin_uv_protocol, pin_uv_auth_param),
119 ui,
120 )
121 .await
122 }
123}
124
125#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
126#[async_trait]
139pub trait CredentialManagementAuthenticator {
140 fn check_credential_management_support(&self) -> Result<(), WebauthnCError>;
145
146 async fn get_credentials_metadata(
150 &mut self,
151 ) -> Result<CredentialStorageMetadata, WebauthnCError>;
152
153 async fn enumerate_rps(&mut self) -> Result<Vec<RelyingPartyCM>, WebauthnCError>;
166
167 async fn enumerate_credentials_by_hash(
176 &mut self,
177 rp_id_hash: SHA256Hash,
178 ) -> Result<Vec<DiscoverableCredential>, WebauthnCError>;
179
180 async fn enumerate_credentials_by_rpid(
189 &mut self,
190 rpid: &str,
191 ) -> Result<Vec<DiscoverableCredential>, WebauthnCError>;
192
193 async fn delete_credential(
205 &mut self,
206 credential_id: PublicKeyCredentialDescriptorCM,
207 ) -> Result<(), WebauthnCError>;
208}
209
210#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
211#[async_trait]
217impl<'a, K, T, U, R> CredentialManagementAuthenticator for T
218where
219 K: Token,
220 T: CredentialManagementAuthenticatorInfo<U, RequestType = R>
221 + Deref<Target = Ctap20Authenticator<'a, K, U>>
222 + DerefMut<Target = Ctap20Authenticator<'a, K, U>>,
223 U: UiCallback + 'a,
224 R: CredentialManagementRequestTrait,
225{
226 fn check_credential_management_support(&self) -> Result<(), WebauthnCError> {
227 if !self.supports_credential_management() {
228 return Err(WebauthnCError::NotSupported);
229 }
230
231 Ok(())
232 }
233
234 async fn get_credentials_metadata(
235 &mut self,
236 ) -> Result<CredentialStorageMetadata, WebauthnCError> {
237 self.check_credential_management_support()?;
238
239 let r = self.cred_mgmt(CredSubCommand::GetCredsMetadata).await?;
240
241 r.storage_metadata
242 .ok_or(WebauthnCError::MissingRequiredField)
243 }
244
245 async fn enumerate_rps(&mut self) -> Result<Vec<RelyingPartyCM>, WebauthnCError> {
246 self.check_credential_management_support()?;
247 let r = self.cred_mgmt(CredSubCommand::EnumerateRPsBegin).await;
248
249 if matches!(r, Err(WebauthnCError::Ctap(CtapError::Ctap2NoCredentials))) {
252 return Ok(Vec::new());
253 }
254 let r = r?;
255
256 let total_rps = r.total_rps.unwrap_or_default();
263 let mut o = Vec::with_capacity(total_rps as usize);
264
265 if total_rps == 0 {
266 return Ok(o);
267 }
268
269 if let Some(rp) = r.rp {
270 o.push(rp);
271 } else {
272 return Err(WebauthnCError::MissingRequiredField);
273 };
274
275 let ui = self.ui_callback;
276 for _ in 1..total_rps {
277 let r = self.token.transmit(R::ENUMERATE_RPS_GET_NEXT, ui).await?;
278 if let Some(rp) = r.rp {
279 o.push(rp);
280 } else {
281 break;
282 }
283 }
284
285 Ok(o)
286 }
287
288 async fn enumerate_credentials_by_hash(
289 &mut self,
290 rp_id_hash: SHA256Hash,
291 ) -> Result<Vec<DiscoverableCredential>, WebauthnCError> {
292 self.check_credential_management_support()?;
293 enumerate_credentials_impl(self, CredSubCommand::EnumerateCredentialsBegin(rp_id_hash))
294 .await
295 }
296
297 async fn enumerate_credentials_by_rpid(
298 &mut self,
299 rp_id: &str,
300 ) -> Result<Vec<DiscoverableCredential>, WebauthnCError> {
301 self.check_credential_management_support()?;
302 enumerate_credentials_impl(self, CredSubCommand::enumerate_credentials_by_rpid(rp_id)).await
303 }
304
305 async fn delete_credential(
306 &mut self,
307 credential_id: PublicKeyCredentialDescriptorCM,
308 ) -> Result<(), WebauthnCError> {
309 self.check_credential_management_support()?;
310
311 self.cred_mgmt(CredSubCommand::DeleteCredential(credential_id))
312 .await
313 .map(|_| ())
314 }
315}
316
317#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
318#[doc(hidden)]
319async fn enumerate_credentials_impl<'a, K, T, U, R>(
320 self_: &mut T,
321 sub_command: CredSubCommand,
322) -> Result<Vec<DiscoverableCredential>, WebauthnCError>
323where
324 K: Token,
325 T: CredentialManagementAuthenticatorInfo<U, RequestType = R>
326 + Deref<Target = Ctap20Authenticator<'a, K, U>>
327 + DerefMut<Target = Ctap20Authenticator<'a, K, U>>,
328 U: UiCallback + 'a,
329 R: CredentialManagementRequestTrait,
330{
331 let r = self_.cred_mgmt(sub_command).await;
332
333 if matches!(r, Err(WebauthnCError::Ctap(CtapError::Ctap2NoCredentials))) {
336 return Ok(Vec::new());
337 }
338 let r = r?;
339
340 let total_creds = r.total_credentials.unwrap_or_default();
341 let mut o = Vec::with_capacity(total_creds as usize);
342
343 if total_creds == 0 {
344 return Ok(o);
345 }
346
347 o.push(r.discoverable_credential);
348
349 let ui = self_.ui_callback;
350 for _ in 1..total_creds {
351 let r = self_
352 .token
353 .transmit(R::ENUMERATE_CREDENTIALS_GET_NEXT, ui)
354 .await?;
355 o.push(r.discoverable_credential);
356 }
357
358 Ok(o)
359}