diff --git a/apps/desktop/desktop_native/win_webauthn/src/plugin/mod.rs b/apps/desktop/desktop_native/win_webauthn/src/plugin/mod.rs index 921a0279b32..cc4c53c38ff 100644 --- a/apps/desktop/desktop_native/win_webauthn/src/plugin/mod.rs +++ b/apps/desktop/desktop_native/win_webauthn/src/plugin/mod.rs @@ -1,7 +1,7 @@ pub(crate) mod com; pub(crate) mod types; -use std::{error::Error, mem::MaybeUninit, ptr::NonNull}; +use std::{error::Error, ptr::NonNull}; use types::*; use windows::{ core::GUID, diff --git a/apps/desktop/desktop_native/win_webauthn/src/plugin/types.rs b/apps/desktop/desktop_native/win_webauthn/src/plugin/types.rs index 7cd0b4d6472..a69ea62f01f 100644 --- a/apps/desktop/desktop_native/win_webauthn/src/plugin/types.rs +++ b/apps/desktop/desktop_native/win_webauthn/src/plugin/types.rs @@ -34,7 +34,7 @@ use super::Clsid; /// Header File Name: _WEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS #[repr(C)] #[derive(Debug, Copy, Clone)] -pub struct WebAuthnCtapCborAuthenticatorOptions { +pub struct WEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS { dwVersion: u32, // LONG lUp: +1=TRUE, 0=Not defined, -1=FALSE lUp: i32, @@ -69,7 +69,8 @@ impl WebAuthnCtapCborAuthenticatorOptions { } } } -type WEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS = WebAuthnCtapCborAuthenticatorOptions; + +pub type WebAuthnCtapCborAuthenticatorOptions = WEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS; /// Used when adding a Windows plugin authenticator (stable API). /// Header File Name: _WEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_OPTIONS @@ -369,7 +370,7 @@ pub enum WebAuthnPluginRequestType { #[repr(C)] #[derive(Debug, Copy, Clone)] -struct WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST { +pub(super) struct WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST { pub dwVersion: u32, pub cbRpId: u32, pub pbRpId: *const u8, @@ -755,7 +756,7 @@ webauthn_call!("WebAuthNEncodeMakeCredentialResponse" as fn webauthn_encode_make #[repr(C)] #[derive(Debug, Copy, Clone)] -struct WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST { +pub(super) struct WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST { pub dwVersion: u32, pub pwszRpId: *const u16, // PCWSTR pub cbRpId: u32, diff --git a/apps/desktop/desktop_native/win_webauthn/src/types/mod.rs b/apps/desktop/desktop_native/win_webauthn/src/types/mod.rs index 78afcbb4dd4..103f657b7e8 100644 --- a/apps/desktop/desktop_native/win_webauthn/src/types/mod.rs +++ b/apps/desktop/desktop_native/win_webauthn/src/types/mod.rs @@ -8,12 +8,7 @@ use std::{collections::HashSet, fmt::Display, ptr::NonNull}; use ciborium::Value; use windows_core::PCWSTR; -use crate::{ - // com::ComBuffer, - util::ArrayPointerIterator, - ErrorKind, - WinWebAuthnError, -}; +use crate::{util::ArrayPointerIterator, ErrorKind, WinWebAuthnError}; /// List of its supported protocol versions and extensions, its AAGUID, and /// other aspects of its overall capabilities. @@ -629,36 +624,6 @@ pub struct CredentialEx { inner: NonNull, } -impl CredentialEx { - /* - fn new_for_com( - version: u32, - id: &CredentialId, - credential_type: &str, - transports: &[CtapTransport], - ) -> Self { - let (pwszCredentialType, _) = credential_type.to_com_utf16(); - let (pbId, cbId) = ComBuffer::from_buffer(&id); - let ptr = unsafe { - let mut uninit: MaybeUninit = MaybeUninit::uninit(); - let ptr = uninit.as_mut_ptr(); - std::ptr::write( - ptr, - WEBAUTHN_CREDENTIAL_EX { - dwVersion: version, - cbId, - pbId, - pwszCredentialType, - dwTransports: transports.iter().map(|t| t.clone() as u32).sum(), - }, - ); - NonNull::new_unchecked(ptr) - }; - Self { inner: ptr } - } - */ -} - impl AsRef for CredentialEx { fn as_ref(&self) -> &WEBAUTHN_CREDENTIAL_EX { // SAFETY: We initialize memory manually in constructors. diff --git a/apps/desktop/desktop_native/win_webauthn/src/util.rs b/apps/desktop/desktop_native/win_webauthn/src/util.rs index a48c75cbfd1..eee0a2edc7f 100644 --- a/apps/desktop/desktop_native/win_webauthn/src/util.rs +++ b/apps/desktop/desktop_native/win_webauthn/src/util.rs @@ -6,11 +6,7 @@ use windows::{ }, }; -use crate::{ - // com::ComBuffer, - ErrorKind, - WinWebAuthnError, -}; +use crate::{ErrorKind, WinWebAuthnError}; macro_rules! webauthn_call { ($symbol:literal as fn $fn_name:ident($($arg:ident: $arg_type:ty),+) -> $result_type:ty) => ( diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/src/com_buffer.rs b/apps/desktop/desktop_native/windows_plugin_authenticator/src/com_buffer.rs deleted file mode 100644 index af34107a050..00000000000 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/com_buffer.rs +++ /dev/null @@ -1,84 +0,0 @@ -use std::alloc; -use std::mem::{align_of, MaybeUninit}; -use std::ptr::NonNull; -use windows::Win32::System::Com::CoTaskMemAlloc; - -#[repr(transparent)] -pub struct ComBuffer(NonNull>); - -impl ComBuffer { - /// Returns an COM-allocated buffer of `size`. - fn alloc(size: usize, for_slice: bool) -> Self { - #[expect(clippy::as_conversions)] - { - assert!(size <= isize::MAX as usize, "requested bad object size"); - } - - // SAFETY: Any size is valid to pass to Windows, even `0`. - let ptr = NonNull::new(unsafe { CoTaskMemAlloc(size) }).unwrap_or_else(|| { - // XXX: This doesn't have to be correct, just close enough for an OK OOM error. - let layout = alloc::Layout::from_size_align(size, align_of::()).unwrap(); - alloc::handle_alloc_error(layout) - }); - - if for_slice { - // Ininitialize the buffer so it can later be treated as `&mut [u8]`. - // SAFETY: The pointer is valid and we are using a valid value for a byte-wise allocation. - unsafe { ptr.write_bytes(0, size) }; - } - - Self(ptr.cast()) - } - - fn into_ptr(self) -> *mut T { - self.0.cast().as_ptr() - } - - /// Creates a new COM-allocated structure. - /// - /// Note that `T` must be [Copy] to avoid any possible memory leaks. - pub fn with_object(object: T) -> *mut T { - // NB: Vendored from Rust's alloc code since we can't yet allocate `Box` with a custom allocator. - const MIN_ALIGN: usize = if cfg!(target_pointer_width = "64") { - 16 - } else if cfg!(target_pointer_width = "32") { - 8 - } else { - panic!("unsupported arch") - }; - - // SAFETY: Validate that our alignment works for a normal size-based allocation for soundness. - let layout = const { - let layout = alloc::Layout::new::(); - assert!(layout.align() <= MIN_ALIGN); - layout - }; - - let buffer = Self::alloc(layout.size(), false); - // SAFETY: `ptr` is valid for writes of `T` because we correctly allocated the right sized buffer that - // accounts for any alignment requirements. - // - // Additionally, we ensure the value is treated as moved by forgetting the source. - unsafe { buffer.0.cast::().write(object) }; - buffer.into_ptr() - } - - pub fn from_buffer>(buffer: T) -> (*mut u8, u32) { - let buffer = buffer.as_ref(); - let len = buffer.len(); - let com_buffer = Self::alloc(len, true); - - // SAFETY: `ptr` points to a valid allocation that `len` matches, and we made sure - // the bytes were initialized. Additionally, bytes have no alignment requirements. - unsafe { - NonNull::slice_from_raw_parts(com_buffer.0.cast::(), len) - .as_mut() - .copy_from_slice(buffer) - } - - // Safety: The Windows API structures these buffers are placed into use `u32` (`DWORD`) to - // represent length. - #[expect(clippy::as_conversions)] - (com_buffer.into_ptr(), len as u32) - } -} diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/src/lib.rs b/apps/desktop/desktop_native/windows_plugin_authenticator/src/lib.rs index ca3fbbd7263..be92d1df1ca 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/lib.rs +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/src/lib.rs @@ -4,13 +4,10 @@ // New modular structure mod assert; -mod com_buffer; -// mod com_provider; mod ipc2; mod make_credential; mod types; mod util; -mod webauthn; use std::{collections::HashSet, sync::Arc, time::Duration}; @@ -25,10 +22,7 @@ use win_webauthn::{ AuthenticatorInfo, CtapVersion, PublicKeyCredentialParameters, }; -use crate::{ - ipc2::{ConnectionStatus, TimedCallback, WindowsProviderClient}, - make_credential::make_credential, -}; +use crate::ipc2::{TimedCallback, WindowsProviderClient}; const AUTHENTICATOR_NAME: &str = "Bitwarden Desktop"; const RPID: &str = "bitwarden.com"; diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/src/util.rs b/apps/desktop/desktop_native/windows_plugin_authenticator/src/util.rs index 5675c5ac23a..396533f3998 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/util.rs +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/src/util.rs @@ -1,15 +1,5 @@ -use std::fs::{create_dir_all, OpenOptions}; -use std::io::Write; -use std::path::Path; -use std::time::{SystemTime, UNIX_EPOCH}; - use windows::Win32::UI::HiDpi::GetDpiForWindow; -use windows::{ - core::PCSTR, - Win32::{Foundation::*, System::LibraryLoader::*, UI::WindowsAndMessaging::GetWindowRect}, -}; - -use crate::com_buffer::ComBuffer; +use windows::Win32::{Foundation::*, UI::WindowsAndMessaging::GetWindowRect}; const BASE_DPI: u32 = 96; @@ -44,90 +34,3 @@ impl HwndExt for HWND { } } } - -pub unsafe fn delay_load(library: PCSTR, function: PCSTR) -> Option { - let library = LoadLibraryExA(library, None, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS); - - let Ok(library) = library else { - return None; - }; - - let address = GetProcAddress(library, function); - - if address.is_some() { - return Some(std::mem::transmute_copy(&address)); - } - - _ = FreeLibrary(library); - - None -} - -/// Trait for converting strings to Windows-compatible wide strings using COM allocation -pub trait WindowsString { - /// Converts to null-terminated UTF-16 using COM allocation - fn to_com_utf16(&self) -> (*mut u16, u32); - /// Converts to Vec for temporary use (caller must keep Vec alive) - fn to_utf16(&self) -> Vec; -} - -impl WindowsString for str { - fn to_com_utf16(&self) -> (*mut u16, u32) { - let mut wide_vec: Vec = self.encode_utf16().collect(); - wide_vec.push(0); // null terminator - let wide_bytes: Vec = wide_vec.iter().flat_map(|&x| x.to_le_bytes()).collect(); - let (ptr, byte_count) = ComBuffer::from_buffer(&wide_bytes); - (ptr as *mut u16, byte_count) - } - - fn to_utf16(&self) -> Vec { - let mut wide_vec: Vec = self.encode_utf16().collect(); - wide_vec.push(0); // null terminator - wide_vec - } -} - -pub fn file_log(msg: &str) { - let log_path = "C:\\temp\\bitwarden_com_debug.log"; - - // Create the temp directory if it doesn't exist - if let Some(parent) = Path::new(log_path).parent() { - let _ = create_dir_all(parent); - } - - if let Ok(mut file) = OpenOptions::new().create(true).append(true).open(log_path) { - let now = SystemTime::now(); - let timestamp = match now.duration_since(UNIX_EPOCH) { - Ok(duration) => { - let total_secs = duration.as_secs(); - let millis = duration.subsec_millis(); - let secs = total_secs % 60; - let mins = (total_secs / 60) % 60; - let hours = (total_secs / 3600) % 24; - format!("{:02}:{:02}:{:02}.{:03}", hours, mins, secs, millis) - } - Err(_) => "??:??:??.???".to_string(), - }; - - let _ = writeln!(file, "[{}] {}", timestamp, msg); - } -} - -// Helper function to convert Windows wide string (UTF-16) to Rust String -pub unsafe fn wstr_to_string( - wstr_ptr: *const u16, -) -> std::result::Result { - if wstr_ptr.is_null() { - return Ok(String::new()); - } - - // Find the length of the null-terminated wide string - let mut len = 0; - while *wstr_ptr.add(len) != 0 { - len += 1; - } - - // Convert to Rust string - let wide_slice = std::slice::from_raw_parts(wstr_ptr, len); - String::from_utf16(wide_slice) -} diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/src/webauthn.rs b/apps/desktop/desktop_native/windows_plugin_authenticator/src/webauthn.rs deleted file mode 100644 index ca51b53dff0..00000000000 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/webauthn.rs +++ /dev/null @@ -1,420 +0,0 @@ -/* - This file exposes safe functions and types for interacting with the stable - Windows WebAuthn Plugin API defined here: - - https://github.com/microsoft/webauthn/blob/master/webauthnplugin.h -*/ - -use windows::core::*; - -use crate::com_buffer::ComBuffer; -use crate::util::{delay_load, WindowsString}; - -/// Windows WebAuthn Authenticator Options structure -/// Header File Name: _WEBAUTHN_CTAPCBOR_AUTHENTICATOR_OPTIONS -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct WebAuthnCtapCborAuthenticatorOptions { - pub version: u32, // DWORD dwVersion - pub user_presence: i32, // LONG lUp: +1=TRUE, 0=Not defined, -1=FALSE - pub user_verification: i32, // LONG lUv: +1=TRUE, 0=Not defined, -1=FALSE - pub require_resident_key: i32, // LONG lRequireResidentKey: +1=TRUE, 0=Not defined, -1=FALSE -} - -/// Used when adding a Windows plugin authenticator (stable API). -/// Header File Name: _WEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_OPTIONS -/// Header File Usage: WebAuthNPluginAddAuthenticator() -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct WebAuthnPluginAddAuthenticatorOptions { - pub authenticator_name: *const u16, // LPCWSTR - pub rclsid: *const GUID, // REFCLSID (changed from string) - pub rpid: *const u16, // LPCWSTR (optional) - pub light_theme_logo_svg: *const u16, // LPCWSTR (optional, base64 SVG) - pub dark_theme_logo_svg: *const u16, // LPCWSTR (optional, base64 SVG) - pub cbor_authenticator_info_byte_count: u32, - pub cbor_authenticator_info: *const u8, // const BYTE* - pub supported_rp_ids_count: u32, // NEW in stable - pub supported_rp_ids: *const *const u16, // NEW in stable: array of LPCWSTR -} - -/// Used as a response type when adding a Windows plugin authenticator (stable API). -/// Header File Name: _WEBAUTHN_PLUGIN_ADD_AUTHENTICATOR_RESPONSE -/// Header File Usage: WebAuthNPluginAddAuthenticator() -/// WebAuthNPluginFreeAddAuthenticatorResponse() -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct WebAuthnPluginAddAuthenticatorResponse { - pub plugin_operation_signing_key_byte_count: u32, - pub plugin_operation_signing_key: *mut u8, -} - -/// Represents a credential. -/// Header File Name: _WEBAUTHN_PLUGIN_CREDENTIAL_DETAILS -/// Header File Usage: WebAuthNPluginAuthenticatorAddCredentials, etc. -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct WebAuthnPluginCredentialDetails { - pub credential_id_byte_count: u32, - pub credential_id_pointer: *const u8, // Changed to const in stable - pub rpid: *const u16, // Changed to const (LPCWSTR) - pub rp_friendly_name: *const u16, // Changed to const (LPCWSTR) - pub user_id_byte_count: u32, - pub user_id_pointer: *const u8, // Changed to const - pub user_name: *const u16, // Changed to const (LPCWSTR) - pub user_display_name: *const u16, // Changed to const (LPCWSTR) -} - -impl WebAuthnPluginCredentialDetails { - pub fn create_from_bytes( - credential_id: Vec, - rpid: String, - rp_friendly_name: String, - user_id: Vec, - user_name: String, - user_display_name: String, - ) -> Self { - // Allocate credential_id bytes with COM - let (credential_id_pointer, credential_id_byte_count) = - ComBuffer::from_buffer(&credential_id); - - // Allocate user_id bytes with COM - let (user_id_pointer, user_id_byte_count) = ComBuffer::from_buffer(&user_id); - - // Convert strings to null-terminated wide strings using trait methods - let (rpid_ptr, _) = rpid.to_com_utf16(); - let (rp_friendly_name_ptr, _) = rp_friendly_name.to_com_utf16(); - let (user_name_ptr, _) = user_name.to_com_utf16(); - let (user_display_name_ptr, _) = user_display_name.to_com_utf16(); - - Self { - credential_id_byte_count, - credential_id_pointer: credential_id_pointer as *const u8, - rpid: rpid_ptr as *const u16, - rp_friendly_name: rp_friendly_name_ptr as *const u16, - user_id_byte_count, - user_id_pointer: user_id_pointer as *const u8, - user_name: user_name_ptr as *const u16, - user_display_name: user_display_name_ptr as *const u16, - } - } -} - -// Stable API function signatures - now use REFCLSID and flat arrays -pub type WebAuthNPluginAuthenticatorAddCredentialsFnDeclaration = - unsafe extern "cdecl" fn( - rclsid: *const GUID, // Changed from string to GUID reference - cCredentialDetails: u32, - pCredentialDetails: *const WebAuthnPluginCredentialDetails, // Flat array, not list - ) -> HRESULT; - -pub type WebAuthNPluginAuthenticatorRemoveCredentialsFnDeclaration = - unsafe extern "cdecl" fn( - rclsid: *const GUID, - cCredentialDetails: u32, - pCredentialDetails: *const WebAuthnPluginCredentialDetails, - ) -> HRESULT; - -pub type WebAuthNPluginAuthenticatorGetAllCredentialsFnDeclaration = - unsafe extern "cdecl" fn( - rclsid: *const GUID, - pcCredentialDetails: *mut u32, // Out param for count - ppCredentialDetailsArray: *mut *mut WebAuthnPluginCredentialDetails, // Out param for array - ) -> HRESULT; - -pub type WebAuthNPluginAuthenticatorFreeCredentialDetailsArrayFnDeclaration = - unsafe extern "cdecl" fn( - cCredentialDetails: u32, - pCredentialDetailsArray: *mut WebAuthnPluginCredentialDetails, - ); - -pub type WebAuthNPluginAuthenticatorRemoveAllCredentialsFnDeclaration = - unsafe extern "cdecl" fn(rclsid: *const GUID) -> HRESULT; - -pub fn add_credentials( - clsid_guid: GUID, - credentials: Vec, -) -> std::result::Result<(), String> { - tracing::debug!("Loading WebAuthNPluginAuthenticatorAddCredentials function..."); - - let result = unsafe { - delay_load::( - s!("webauthn.dll"), - s!("WebAuthNPluginAuthenticatorAddCredentials"), - ) - }; - - match result { - Some(api) => { - tracing::debug!("Function loaded successfully, calling API..."); - tracing::debug!("Adding {} credentials", credentials.len()); - - let credential_count = credentials.len() as u32; - let credentials_ptr = if credentials.is_empty() { - std::ptr::null() - } else { - credentials.as_ptr() - }; - - let result = unsafe { api(&clsid_guid, credential_count, credentials_ptr) }; - - if result.is_err() { - let error_code = result.0; - tracing::debug!("API call failed with HRESULT: 0x{:x}", error_code); - return Err(format!( - "Error: Error response from WebAuthNPluginAuthenticatorAddCredentials()\nHRESULT: 0x{:x}\n{}", - error_code, result.message() - )); - } - - tracing::debug!("API call succeeded"); - Ok(()) - } - None => { - tracing::debug!("Failed to load WebAuthNPluginAuthenticatorAddCredentials function from webauthn.dll"); - Err(String::from("Error: Can't complete add_credentials(), as the function WebAuthNPluginAuthenticatorAddCredentials can't be loaded.")) - } - } -} - -pub fn remove_credentials( - clsid_guid: GUID, - credentials: Vec, -) -> std::result::Result<(), String> { - tracing::debug!("Loading WebAuthNPluginAuthenticatorRemoveCredentials function..."); - - let result = unsafe { - delay_load::( - s!("webauthn.dll"), - s!("WebAuthNPluginAuthenticatorRemoveCredentials"), - ) - }; - - match result { - Some(api) => { - tracing::debug!("Removing {} credentials", credentials.len()); - - let credential_count = credentials.len() as u32; - let credentials_ptr = if credentials.is_empty() { - std::ptr::null() - } else { - credentials.as_ptr() - }; - - let result = unsafe { api(&clsid_guid, credential_count, credentials_ptr) }; - - if result.is_err() { - return Err(format!( - "Error: Error response from WebAuthNPluginAuthenticatorRemoveCredentials()\n{}", - result.message() - )); - } - - Ok(()) - }, - None => { - Err(String::from("Error: Can't complete remove_credentials(), as the function WebAuthNPluginAuthenticatorRemoveCredentials can't be loaded.")) - } - } -} - -// Helper struct to hold owned credential data -#[derive(Debug, Clone)] -pub struct OwnedCredentialDetails { - pub credential_id: Vec, - pub rpid: String, - pub rp_friendly_name: String, - pub user_id: Vec, - pub user_name: String, - pub user_display_name: String, -} - -pub fn get_all_credentials( - clsid_guid: GUID, -) -> std::result::Result, String> { - tracing::debug!("Loading WebAuthNPluginAuthenticatorGetAllCredentials function..."); - - let result = unsafe { - delay_load::( - s!("webauthn.dll"), - s!("WebAuthNPluginAuthenticatorGetAllCredentials"), - ) - }; - - match result { - Some(api) => { - let mut credential_count: u32 = 0; - let mut credentials_array_ptr: *mut WebAuthnPluginCredentialDetails = std::ptr::null_mut(); - - let result = unsafe { api(&clsid_guid, &mut credential_count, &mut credentials_array_ptr) }; - - if result.is_err() { - return Err(format!( - "Error: Error response from WebAuthNPluginAuthenticatorGetAllCredentials()\n{}", - result.message() - )); - } - - if credentials_array_ptr.is_null() || credential_count == 0 { - tracing::debug!("No credentials returned"); - return Ok(Vec::new()); - } - - // Deep copy the credential data before Windows frees it - let credentials_slice = unsafe { - std::slice::from_raw_parts(credentials_array_ptr, credential_count as usize) - }; - - let mut owned_credentials = Vec::new(); - for cred in credentials_slice { - unsafe { - // Copy credential ID bytes - let credential_id = if !cred.credential_id_pointer.is_null() && cred.credential_id_byte_count > 0 { - std::slice::from_raw_parts(cred.credential_id_pointer, cred.credential_id_byte_count as usize).to_vec() - } else { - Vec::new() - }; - - // Copy user ID bytes - let user_id = if !cred.user_id_pointer.is_null() && cred.user_id_byte_count > 0 { - std::slice::from_raw_parts(cred.user_id_pointer, cred.user_id_byte_count as usize).to_vec() - } else { - Vec::new() - }; - - // Copy string fields - let rpid = if !cred.rpid.is_null() { - String::from_utf16_lossy(std::slice::from_raw_parts( - cred.rpid, - (0..).position(|i| *cred.rpid.offset(i) == 0).unwrap_or(0) - )) - } else { - String::new() - }; - - let rp_friendly_name = if !cred.rp_friendly_name.is_null() { - String::from_utf16_lossy(std::slice::from_raw_parts( - cred.rp_friendly_name, - (0..).position(|i| *cred.rp_friendly_name.offset(i) == 0).unwrap_or(0) - )) - } else { - String::new() - }; - - let user_name = if !cred.user_name.is_null() { - String::from_utf16_lossy(std::slice::from_raw_parts( - cred.user_name, - (0..).position(|i| *cred.user_name.offset(i) == 0).unwrap_or(0) - )) - } else { - String::new() - }; - - let user_display_name = if !cred.user_display_name.is_null() { - String::from_utf16_lossy(std::slice::from_raw_parts( - cred.user_display_name, - (0..).position(|i| *cred.user_display_name.offset(i) == 0).unwrap_or(0) - )) - } else { - String::new() - }; - - owned_credentials.push(OwnedCredentialDetails { - credential_id, - rpid, - rp_friendly_name, - user_id, - user_name, - user_display_name, - }); - } - } - - // Free the array using the Windows API - this frees everything including strings - free_credential_details_array(credential_count, credentials_array_ptr); - - tracing::debug!("Retrieved {} credentials", owned_credentials.len()); - Ok(owned_credentials) - }, - None => { - Err(String::from("Error: Can't complete get_all_credentials(), as the function WebAuthNPluginAuthenticatorGetAllCredentials can't be loaded.")) - } - } -} - -fn free_credential_details_array( - credential_count: u32, - credentials_array: *mut WebAuthnPluginCredentialDetails, -) { - if credentials_array.is_null() { - return; - } - - let result = unsafe { - delay_load::( - s!("webauthn.dll"), - s!("WebAuthNPluginAuthenticatorFreeCredentialDetailsArray"), - ) - }; - - if let Some(api) = result { - unsafe { api(credential_count, credentials_array) }; - } else { - tracing::debug!( - "Warning: Could not load WebAuthNPluginAuthenticatorFreeCredentialDetailsArray" - ); - } -} - -pub fn remove_all_credentials(clsid_guid: GUID) -> std::result::Result<(), String> { - tracing::debug!("Loading WebAuthNPluginAuthenticatorRemoveAllCredentials function..."); - - let result = unsafe { - delay_load::( - s!("webauthn.dll"), - s!("WebAuthNPluginAuthenticatorRemoveAllCredentials"), - ) - }; - - match result { - Some(api) => { - tracing::debug!("Function loaded successfully, calling API..."); - - let result = unsafe { api(&clsid_guid) }; - - if result.is_err() { - let error_code = result.0; - tracing::debug!("API call failed with HRESULT: 0x{:x}", error_code); - - return Err(format!( - "Error: Error response from WebAuthNPluginAuthenticatorRemoveAllCredentials()\nHRESULT: 0x{:x}\n{}", - error_code, result.message() - )); - } - - tracing::debug!("API call succeeded"); - Ok(()) - } - None => { - tracing::debug!("Failed to load WebAuthNPluginAuthenticatorRemoveAllCredentials function from webauthn.dll"); - Err(String::from("Error: Can't complete remove_all_credentials(), as the function WebAuthNPluginAuthenticatorRemoveAllCredentials can't be loaded.")) - } - } -} - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct WEBAUTHN_CREDENTIAL_EX { - pub dwVersion: u32, - pub cbId: u32, - pub pbId: *const u8, - pub pwszCredentialType: *const u16, // LPCWSTR - pub dwTransports: u32, -} - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct WEBAUTHN_CREDENTIAL_LIST { - pub cCredentials: u32, - pub ppCredentials: *const *const WEBAUTHN_CREDENTIAL_EX, -}