webauthn_authenticator_rs/win10/
mod.rs1#[cfg(feature = "win10")]
11mod clientdata;
12#[cfg(feature = "win10")]
13mod cose;
14#[cfg(feature = "win10")]
15mod credential;
16#[cfg(feature = "win10")]
17mod extensions;
18#[cfg(feature = "win10")]
19mod gui;
20#[cfg(feature = "win10")]
21mod native;
22#[cfg(feature = "win10")]
23mod rp;
24#[cfg(feature = "win10")]
25mod user;
26
27#[cfg(feature = "win10")]
28use crate::win10::{
29 clientdata::WinClientData,
30 cose::WinCoseCredentialParameters,
31 credential::{native_to_transports, WinCredentialList},
32 extensions::{
33 native_to_assertion_extensions, native_to_registration_extensions,
34 WinExtensionMakeCredentialRequest, WinExtensionsRequest,
35 },
36 gui::Window,
37 native::{WinPtr, WinWrapper},
38 rp::WinRpEntityInformation,
39 user::WinUserEntityInformation,
40};
41use crate::{
42 error::WebauthnCError,
43 util::{creation_to_clientdata, get_to_clientdata},
44 AuthenticatorBackend, Url, BASE64_ENGINE,
45};
46
47use base64::Engine;
48use base64urlsafedata::Base64UrlSafeData;
49use webauthn_rs_proto::{
50 AuthenticatorAssertionResponseRaw, AuthenticatorAttachment,
51 AuthenticatorAttestationResponseRaw, PublicKeyCredential, PublicKeyCredentialCreationOptions,
52 PublicKeyCredentialRequestOptions, RegisterPublicKeyCredential, UserVerificationPolicy,
53};
54
55#[cfg(feature = "win10")]
56use windows::{
57 core::{HSTRING, PCWSTR},
58 Win32::{Foundation::BOOL, Networking::WindowsWebServices::*},
59};
60
61use std::slice::from_raw_parts;
62
63pub struct Win10 {}
65
66impl Default for Win10 {
67 fn default() -> Self {
68 unsafe {
69 trace!(
70 "WebAuthNGetApiVersionNumber(): {}",
71 WebAuthNGetApiVersionNumber()
72 );
73 match WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable() {
74 Ok(v) => trace!(
75 "WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable() = {:?}",
76 <_ as Into<bool>>::into(v)
77 ),
78 Err(e) => trace!("error requesting platform authenticator: {:?}", e),
79 }
80 }
81
82 Self {}
83 }
84}
85
86impl AuthenticatorBackend for Win10 {
87 fn perform_register(
93 &mut self,
94 origin: Url,
95 options: PublicKeyCredentialCreationOptions,
96 timeout_ms: u32,
97 ) -> Result<RegisterPublicKeyCredential, WebauthnCError> {
98 let hwnd = Window::new()?;
99 let rp = WinRpEntityInformation::new(options.rp)?;
101 let userinfo = WinUserEntityInformation::new(options.user)?;
102 let pubkeycredparams = WinCoseCredentialParameters::new(options.pub_key_cred_params)?;
103 let clientdata =
104 WinClientData::new(creation_to_clientdata(origin, options.challenge.clone()))?;
105
106 let mut exclude_credentials = if let Some(e) = options.exclude_credentials {
107 Some(WinCredentialList::new(e)?)
108 } else {
109 None
110 };
111 let extensions = match options.extensions {
112 Some(e) => WinExtensionsRequest::new(e)?,
113 None => Box::pin(WinExtensionsRequest::<WinExtensionMakeCredentialRequest>::default()),
114 };
115 let makecredopts = WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS {
118 dwVersion: WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_CURRENT_VERSION,
119 dwTimeoutMilliseconds: timeout_ms,
120 CredentialList: WEBAUTHN_CREDENTIALS {
122 cCredentials: 0,
123 pCredentials: [].as_mut_ptr(),
124 },
125 Extensions: *extensions.native_ptr(),
126 dwAuthenticatorAttachment: attachment_to_native(
127 options
128 .authenticator_selection
129 .as_ref()
130 .map(|s| s.authenticator_attachment)
131 .unwrap_or(None),
132 ),
133 bRequireResidentKey: options
134 .authenticator_selection
135 .as_ref()
136 .map(|s| s.require_resident_key)
137 .unwrap_or(false)
138 .into(),
139 dwUserVerificationRequirement: user_verification_to_native(
140 options
141 .authenticator_selection
142 .as_ref()
143 .map(|s| &s.user_verification),
144 ),
145 dwAttestationConveyancePreference: 0,
146 dwFlags: 0,
147 pCancellationId: std::ptr::null_mut(),
148 pExcludeCredentialList: match &mut exclude_credentials {
149 None => std::ptr::null(),
150 Some(l) => &l.native,
151 } as *mut _,
152 dwEnterpriseAttestation: 0,
153 dwLargeBlobSupport: 0,
154 bPreferResidentKey: false.into(),
155 };
156
157 let a = unsafe {
161 let r = WebAuthNAuthenticatorMakeCredential(
162 &hwnd,
163 rp.native_ptr(),
164 userinfo.native_ptr(),
165 pubkeycredparams.native_ptr(),
166 clientdata.native_ptr(),
167 Some(&makecredopts),
168 )
169 .map_err(|e| {
170 error!("Error: {:?}", e);
172 WebauthnCError::Internal
173 })?;
174
175 WinPtr::new(r, |a| WebAuthNFreeCredentialAttestation(Some(a)))
176 .ok_or(WebauthnCError::Internal)?
177 };
178 drop(extensions);
180 drop(hwnd);
181
182 unsafe {
186 let cred_id = from_raw_parts(a.pbCredentialId, a.cbCredentialId as usize).to_vec();
187 let attesation_object =
188 from_raw_parts(a.pbAttestationObject, a.cbAttestationObject as usize).to_vec();
189 let type_: String = a
190 .pwszFormatType
191 .to_string()
192 .map_err(|_| WebauthnCError::Internal)?;
193
194 Ok(RegisterPublicKeyCredential {
195 id: BASE64_ENGINE.encode(&cred_id),
196 raw_id: cred_id.into(),
197 type_,
198 extensions: native_to_registration_extensions(&a.Extensions)?,
199 response: AuthenticatorAttestationResponseRaw {
200 attestation_object: attesation_object.into(),
201 client_data_json: Base64UrlSafeData::from(
202 clientdata.client_data_json().as_bytes().to_vec(),
203 ),
204 transports: Some(native_to_transports(a.dwUsedTransport)),
205 },
206 })
207 }
208 }
209
210 fn perform_auth(
216 &mut self,
217 origin: Url,
218 options: PublicKeyCredentialRequestOptions,
219 timeout_ms: u32,
220 ) -> Result<PublicKeyCredential, WebauthnCError> {
221 trace!(?options);
222 let hwnd = Window::new()?;
223 let rp_id: HSTRING = options.rp_id.clone().into();
224 let clientdata = WinClientData::new(get_to_clientdata(origin, options.challenge.clone()))?;
225
226 let mut allow_credentials = WinCredentialList::new(options.allow_credentials)?;
227
228 let app_id: Option<HSTRING> = options
229 .extensions
230 .as_ref()
231 .and_then(|e| e.appid.as_ref())
232 .map(|a| a.clone().into());
233 let mut app_id_used: BOOL = false.into();
255 let getassertopts = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS {
261 dwVersion: WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_CURRENT_VERSION,
262 dwTimeoutMilliseconds: timeout_ms,
263 CredentialList: WEBAUTHN_CREDENTIALS {
265 cCredentials: 0,
266 pCredentials: [].as_mut_ptr(),
267 },
268 Extensions: Default::default(),
270 dwAuthenticatorAttachment: 0, dwUserVerificationRequirement: user_verification_to_native(Some(
272 &options.user_verification,
273 )),
274 dwFlags: 0,
275 pwszU2fAppId: match &app_id {
276 None => PCWSTR::null(),
277 Some(l) => l.into(),
278 },
279 pbU2fAppId: std::ptr::addr_of_mut!(app_id_used),
280 pCancellationId: std::ptr::null_mut(),
281 pAllowCredentialList: &mut allow_credentials.native,
282 dwCredLargeBlobOperation: 0,
283 cbCredLargeBlob: 0,
284 pbCredLargeBlob: std::ptr::null_mut(),
285 };
286
287 let a = unsafe {
289 let r = WebAuthNAuthenticatorGetAssertion(
290 &hwnd,
291 &rp_id,
292 clientdata.native_ptr(),
293 Some(&getassertopts),
294 )
295 .map_err(|e| {
296 error!("Error: {:?}", e);
298 WebauthnCError::Internal
299 })?;
300
301 WinPtr::new(r, WebAuthNFreeAssertion).ok_or(WebauthnCError::Internal)?
302 };
303 drop(hwnd);
305 unsafe {
308 let user_id = from_raw_parts(a.pbUserId, a.cbUserId as usize).to_vec();
309 let authenticator_data =
310 from_raw_parts(a.pbAuthenticatorData, a.cbAuthenticatorData as usize).to_vec();
311 let signature = from_raw_parts(a.pbSignature, a.cbSignature as usize).to_vec();
312
313 let credential_id =
314 from_raw_parts(a.Credential.pbId, a.Credential.cbId as usize).to_vec();
315 let type_ = a
316 .Credential
317 .pwszCredentialType
318 .to_string()
319 .map_err(|_| WebauthnCError::Internal)?;
320
321 let mut extensions = if a.dwVersion >= 2 {
322 native_to_assertion_extensions(&a.Extensions)?
323 } else {
324 Default::default()
325 };
326 extensions.appid = Some(app_id_used.into());
327
328 Ok(PublicKeyCredential {
329 id: BASE64_ENGINE.encode(&credential_id),
330 raw_id: credential_id.into(),
331 response: AuthenticatorAssertionResponseRaw {
332 authenticator_data: authenticator_data.into(),
333 client_data_json: Base64UrlSafeData::from(
334 clientdata.client_data_json().as_bytes().to_vec(),
335 ),
336 signature: signature.into(),
337 user_handle: Some(user_id.into()),
338 },
339 type_,
340 extensions,
341 })
342 }
343 }
344}
345
346#[cfg(feature = "win10")]
347fn attachment_to_native(attachment: Option<AuthenticatorAttachment>) -> u32 {
350 use AuthenticatorAttachment::*;
351 match attachment {
352 None => WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY,
353 Some(CrossPlatform) => WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM,
354 Some(Platform) => WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM,
355 }
356}
357
358#[cfg(feature = "win10")]
359fn user_verification_to_native(policy: Option<&UserVerificationPolicy>) -> u32 {
362 use UserVerificationPolicy::*;
363 match policy {
364 None => WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY,
365 Some(p) => match p {
366 Required => WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED,
367 Preferred => WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED,
368 Discouraged_DO_NOT_USE => WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED,
369 },
370 }
371}