1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-14 15:33:55 +00:00
This commit is contained in:
Isaiah Inuwa
2025-11-24 13:40:16 -06:00
parent 61745ece41
commit 1c81932d01
8 changed files with 10 additions and 655 deletions

View File

@@ -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,

View File

@@ -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,

View File

@@ -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<WEBAUTHN_CREDENTIAL_EX>,
}
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<WEBAUTHN_CREDENTIAL_EX> = 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<WEBAUTHN_CREDENTIAL_EX> for CredentialEx {
fn as_ref(&self) -> &WEBAUTHN_CREDENTIAL_EX {
// SAFETY: We initialize memory manually in constructors.

View File

@@ -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) => (

View File

@@ -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<MaybeUninit<u8>>);
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::<u8>()).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<T>(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<T: Copy>(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::<T>();
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::<T>().write(object) };
buffer.into_ptr()
}
pub fn from_buffer<T: AsRef<[u8]>>(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::<u8>(), 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)
}
}

View File

@@ -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";

View File

@@ -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<T>(library: PCSTR, function: PCSTR) -> Option<T> {
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<u16> for temporary use (caller must keep Vec alive)
fn to_utf16(&self) -> Vec<u16>;
}
impl WindowsString for str {
fn to_com_utf16(&self) -> (*mut u16, u32) {
let mut wide_vec: Vec<u16> = self.encode_utf16().collect();
wide_vec.push(0); // null terminator
let wide_bytes: Vec<u8> = 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<u16> {
let mut wide_vec: Vec<u16> = 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<String, std::string::FromUtf16Error> {
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)
}

View File

@@ -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<u8>,
rpid: String,
rp_friendly_name: String,
user_id: Vec<u8>,
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<WebAuthnPluginCredentialDetails>,
) -> std::result::Result<(), String> {
tracing::debug!("Loading WebAuthNPluginAuthenticatorAddCredentials function...");
let result = unsafe {
delay_load::<WebAuthNPluginAuthenticatorAddCredentialsFnDeclaration>(
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<WebAuthnPluginCredentialDetails>,
) -> std::result::Result<(), String> {
tracing::debug!("Loading WebAuthNPluginAuthenticatorRemoveCredentials function...");
let result = unsafe {
delay_load::<WebAuthNPluginAuthenticatorRemoveCredentialsFnDeclaration>(
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<u8>,
pub rpid: String,
pub rp_friendly_name: String,
pub user_id: Vec<u8>,
pub user_name: String,
pub user_display_name: String,
}
pub fn get_all_credentials(
clsid_guid: GUID,
) -> std::result::Result<Vec<OwnedCredentialDetails>, String> {
tracing::debug!("Loading WebAuthNPluginAuthenticatorGetAllCredentials function...");
let result = unsafe {
delay_load::<WebAuthNPluginAuthenticatorGetAllCredentialsFnDeclaration>(
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::<WebAuthNPluginAuthenticatorFreeCredentialDetailsArrayFnDeclaration>(
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::<WebAuthNPluginAuthenticatorRemoveAllCredentialsFnDeclaration>(
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,
}