webauthn_authenticator_rs/ctap2/
ctap21.rs1use crate::{
2 ctap2::{
3 commands::GetInfoResponse, ctap21_bio::BiometricAuthenticatorInfo,
4 ctap21_cred::CredentialManagementAuthenticatorInfo, internal::CtapAuthenticatorVersion,
5 Ctap20Authenticator,
6 },
7 transport::Token,
8 ui::UiCallback,
9};
10#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
11use crate::{
12 ctap2::{
13 commands::{
14 BioEnrollmentRequest, ConfigRequest, ConfigSubCommand, CredSubCommand,
15 CredentialManagementRequest, Permissions, PublicKeyCredentialDescriptorCM,
16 SetMinPinLengthParams, UserCM,
17 },
18 ctap21_cred::CredentialManagementAuthenticatorSupport,
19 CredentialManagementAuthenticator,
20 },
21 error::WebauthnCError,
22};
23use std::ops::{Deref, DerefMut};
24#[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
25use webauthn_rs_proto::UserVerificationPolicy;
26
27#[derive(Debug)]
32pub struct Ctap21Authenticator<'a, T: Token, U: UiCallback> {
33 authenticator: Ctap20Authenticator<'a, T, U>,
34}
35
36impl<'a, T: Token, U: UiCallback> Deref for Ctap21Authenticator<'a, T, U> {
39 type Target = Ctap20Authenticator<'a, T, U>;
40
41 fn deref(&self) -> &Self::Target {
42 &self.authenticator
43 }
44}
45
46impl<T: Token, U: UiCallback> DerefMut for Ctap21Authenticator<'_, T, U> {
47 fn deref_mut(&mut self) -> &mut Self::Target {
48 &mut self.authenticator
49 }
50}
51
52impl<'a, T: Token, U: UiCallback> CtapAuthenticatorVersion<'a, T, U>
53 for Ctap21Authenticator<'a, T, U>
54{
55 const VERSION: &'static str = "FIDO_2_1";
56 fn new_with_info(info: GetInfoResponse, token: T, ui_callback: &'a U) -> Self {
57 Self {
58 authenticator: Ctap20Authenticator::new_with_info(info, token, ui_callback),
59 }
60 }
61}
62
63impl<'a, T: Token, U: UiCallback> Ctap21Authenticator<'a, T, U> {
64 #[inline]
72 pub fn supports_config(&self) -> bool {
73 self.info.supports_config()
74 }
75
76 #[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
77 async fn config(
78 &mut self,
79 sub_command: ConfigSubCommand,
80 toggle_always_uv: bool,
81 ) -> Result<(), WebauthnCError> {
82 if !self.supports_config() {
83 return Err(WebauthnCError::NotSupported);
84 }
85
86 let ui_callback = self.ui_callback;
87
88 let (pin_uv_auth_proto, pin_uv_auth_param) = self
89 .get_pin_uv_auth_token(
90 sub_command.prf().as_slice(),
91 Permissions::AUTHENTICATOR_CONFIGURATION,
92 None,
93 if toggle_always_uv {
94 UserVerificationPolicy::Discouraged_DO_NOT_USE
95 } else {
96 UserVerificationPolicy::Required
97 },
98 )
99 .await?
100 .into_pin_uv_params();
101
102 self.token
104 .transmit(
105 ConfigRequest::new(sub_command, pin_uv_auth_proto, pin_uv_auth_param),
106 ui_callback,
107 )
108 .await?;
109
110 self.refresh_info().await
111 }
112
113 #[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
114 pub async fn toggle_always_uv(&mut self) -> Result<(), WebauthnCError> {
121 self.config(ConfigSubCommand::ToggleAlwaysUv, true).await
122 }
123
124 #[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
125 pub async fn set_min_pin_length(
132 &mut self,
133 new_min_pin_length: Option<u32>,
134 min_pin_length_rpids: Vec<String>,
135 force_change_pin: Option<bool>,
136 ) -> Result<(), WebauthnCError> {
137 self.config(
138 ConfigSubCommand::SetMinPinLength(SetMinPinLengthParams {
139 new_min_pin_length,
140 min_pin_length_rpids,
141 force_change_pin,
142 }),
143 false,
144 )
145 .await
146 }
147
148 #[inline]
157 pub fn supports_enterprise_attestation(&self) -> bool {
158 self.info.supports_enterprise_attestation()
159 }
160
161 #[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
162 pub async fn enable_enterprise_attestation(&mut self) -> Result<(), WebauthnCError> {
170 if !self.supports_enterprise_attestation() || !self.supports_config() {
171 return Err(WebauthnCError::NotSupported);
172 }
173 self.config(ConfigSubCommand::EnableEnterpriseAttestation, false)
174 .await
175 }
176
177 #[inline]
187 pub fn supports_ctap21_credential_management(&self) -> bool {
188 self.info.ctap21_credential_management()
189 }
190
191 #[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
207 pub async fn update_credential_user(
208 &mut self,
209 credential_id: PublicKeyCredentialDescriptorCM,
210 user: UserCM,
211 ) -> Result<(), WebauthnCError> {
212 self.check_credential_management_support()?;
213
214 self.cred_mgmt(CredSubCommand::UpdateUserInformation(credential_id, user))
215 .await
216 .map(|_| ())
217 }
218}
219
220impl<T: Token, U: UiCallback> BiometricAuthenticatorInfo<U> for Ctap21Authenticator<'_, T, U> {
221 #[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
222 type RequestType = BioEnrollmentRequest;
223
224 #[inline]
225 fn biometrics(&self) -> Option<bool> {
226 self.info.ctap21_biometrics()
227 }
228}
229
230impl<T: Token, U: UiCallback> CredentialManagementAuthenticatorInfo<U>
231 for Ctap21Authenticator<'_, T, U>
232{
233 #[cfg(any(all(doc, not(doctest)), feature = "ctap2-management"))]
234 type RequestType = CredentialManagementRequest;
235 #[inline]
238 fn supports_credential_management(&self) -> bool {
239 self.supports_ctap21_credential_management()
240 }
241}