From 2196a8339c5ea8510d0ce5207d678b7e304b2ab3 Mon Sep 17 00:00:00 2001 From: Isaiah Inuwa Date: Sat, 8 Nov 2025 21:14:00 -0600 Subject: [PATCH] Wire up Windows IPC for GetAssertion --- .../src/assert.rs | 275 ++++++++---------- .../src/com_provider.rs | 28 +- .../src/ipc2/assertion.rs | 26 +- .../src/ipc2/mod.rs | 63 +++- .../src/make_credential.rs | 121 ++------ 5 files changed, 237 insertions(+), 276 deletions(-) diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/src/assert.rs b/apps/desktop/desktop_native/windows_plugin_authenticator/src/assert.rs index 810e54b6a17..610a319380d 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/assert.rs +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/src/assert.rs @@ -1,12 +1,20 @@ use serde_json; -use std::alloc::{alloc, Layout}; -use std::ptr; +use std::{ + alloc::{alloc, Layout}, + ptr, + sync::Arc, + time::Duration, +}; use windows_core::{s, HRESULT}; use crate::com_provider::{ parse_credential_list, WebAuthnPluginOperationRequest, WebAuthnPluginOperationResponse, }; -use crate::types::*; +use crate::ipc2::{ + PasskeyAssertionRequest, PasskeyAssertionResponse, Position, TimedCallback, UserVerification, + WindowsProviderClient, +}; +use crate::types::UserVerificationRequirement; use crate::util::{debug_log, delay_load, wstr_to_string}; use crate::webauthn::WEBAUTHN_CREDENTIAL_LIST; @@ -62,7 +70,7 @@ impl Drop for DecodedGetAssertionRequest { fn drop(&mut self) { if !self.ptr.is_null() { if let Some(free_fn) = self.free_fn { - debug_log("Freeing decoded get assertion request"); + tracing::debug!("Freeing decoded get assertion request"); unsafe { free_fn(self.ptr); } @@ -75,7 +83,7 @@ impl Drop for DecodedGetAssertionRequest { unsafe fn decode_get_assertion_request( encoded_request: &[u8], ) -> Result { - debug_log("Attempting to decode get assertion request using Windows API"); + tracing::debug!("Attempting to decode get assertion request using Windows API"); // Load the Windows WebAuthn API function let decode_fn: Option = @@ -122,38 +130,40 @@ pub struct WindowsAssertionRequest { /// Helper for assertion requests fn send_assertion_request( + ipc_client: &WindowsProviderClient, transaction_id: &str, request: &WindowsAssertionRequest, -) -> Option { +) -> Result { + let user_verification = match request.user_verification { + UserVerificationRequirement::Discouraged => UserVerification::Discouraged, + UserVerificationRequirement::Preferred => UserVerification::Preferred, + UserVerificationRequirement::Required => UserVerification::Required, + }; let passkey_request = PasskeyAssertionRequest { rp_id: request.rpid.clone(), - transaction_id: transaction_id.to_string(), + // transaction_id: transaction_id.to_string(), client_data_hash: request.client_data_hash.clone(), allowed_credentials: request.allowed_credentials.clone(), - user_verification: request.user_verification.clone(), + user_verification, window_xy: Position { x: 400, y: 400 }, }; - debug_log(&format!( + tracing::debug!( "Assertion request data - RP ID: {}, Client data hash: {} bytes, Allowed credentials: {:?}", passkey_request.rp_id, passkey_request.client_data_hash.len(), passkey_request.allowed_credentials, - )); + ); - match serde_json::to_string(&passkey_request) { - Ok(request_json) => { - debug_log(&format!("Sending assertion request: {}", request_json)); - crate::ipc::send_passkey_request(RequestType::Assertion, request_json, &request.rpid) - } - Err(e) => { - debug_log(&format!( - "ERROR: Failed to serialize assertion request: {}", - e - )); - None - } - } + let request_json = serde_json::to_string(&passkey_request) + .map_err(|err| format!("Failed to serialize assertion request: {err}"))?; + tracing::debug!(?request_json, "Sending assertion request"); + let callback = Arc::new(TimedCallback::new()); + ipc_client.prepare_passkey_assertion(passkey_request, callback.clone()); + callback + .wait_for_response(Duration::from_secs(30)) + .map_err(|_| "Registration request timed out".to_string())? + .map_err(|err| err.to_string()) } /// Creates a WebAuthn get assertion response from Bitwarden's assertion response @@ -264,15 +274,16 @@ unsafe fn create_get_assertion_response( /// Implementation of PluginGetAssertion moved from com_provider.rs pub unsafe fn plugin_get_assertion( + ipc_client: &WindowsProviderClient, request: *const WebAuthnPluginOperationRequest, response: *mut WebAuthnPluginOperationResponse, -) -> HRESULT { - debug_log("PluginGetAssertion() called"); +) -> Result<(), HRESULT> { + tracing::debug!("PluginGetAssertion() called"); // Validate input parameters if request.is_null() || response.is_null() { - debug_log("Invalid parameters passed to PluginGetAssertion"); - return HRESULT(-1); + tracing::debug!("Invalid parameters passed to PluginGetAssertion"); + return Err(HRESULT(-1)); } let req = &*request; @@ -284,8 +295,8 @@ pub unsafe fn plugin_get_assertion( )); if req.encoded_request_byte_count == 0 || req.encoded_request_pointer.is_null() { - debug_log("ERROR: No encoded request data provided"); - return HRESULT(-1); + tracing::debug!("ERROR: No encoded request data provided"); + return Err(HRESULT(-1)); } let encoded_request_slice = std::slice::from_raw_parts( @@ -294,133 +305,93 @@ pub unsafe fn plugin_get_assertion( ); // Try to decode the request using Windows API - match decode_get_assertion_request(encoded_request_slice) { - Ok(decoded_wrapper) => { - let decoded_request = decoded_wrapper.as_ref(); - debug_log("Successfully decoded get assertion request using Windows API"); + let decoded_wrapper = decode_get_assertion_request(encoded_request_slice).map_err(|err| { + tracing::debug!("Failed to decode get assertion request: {err}"); + HRESULT(-1) + })?; + let decoded_request = decoded_wrapper.as_ref(); + tracing::debug!("Successfully decoded get assertion request using Windows API"); - // Extract RP information - let rpid = if decoded_request.pwszRpId.is_null() { - debug_log("ERROR: RP ID is null"); - return HRESULT(-1); - } else { - match wstr_to_string(decoded_request.pwszRpId) { - Ok(id) => id, - Err(e) => { - debug_log(&format!("ERROR: Failed to decode RP ID: {}", e)); - return HRESULT(-1); - } - } - }; - - // Extract client data hash - let client_data_hash = if decoded_request.cbClientDataHash == 0 - || decoded_request.pbClientDataHash.is_null() - { - debug_log("ERROR: Client data hash is required for assertion"); - return HRESULT(-1); - } else { - let hash_slice = std::slice::from_raw_parts( - decoded_request.pbClientDataHash, - decoded_request.cbClientDataHash as usize, - ); - hash_slice.to_vec() - }; - - // Extract user verification requirement from authenticator options - let user_verification = if !decoded_request.pAuthenticatorOptions.is_null() { - let auth_options = &*decoded_request.pAuthenticatorOptions; - match auth_options.user_verification { - 1 => Some(UserVerificationRequirement::Required), - -1 => Some(UserVerificationRequirement::Discouraged), - 0 | _ => Some(UserVerificationRequirement::Preferred), // Default or undefined - } - } else { - None - }; - - // Extract allowed credentials from credential list - let allowed_credentials = parse_credential_list(&decoded_request.CredentialList); - - // Create Windows assertion request - let assertion_request = WindowsAssertionRequest { - rpid: rpid.clone(), - client_data_hash, - allowed_credentials: allowed_credentials.clone(), - user_verification: user_verification.unwrap_or_default(), - }; - - debug_log(&format!( - "Get assertion request - RP: {}, Allowed credentials: {:?}", - rpid, allowed_credentials - )); - - // Send assertion request - if let Some(passkey_response) = - send_assertion_request(&transaction_id, &assertion_request) - { - debug_log(&format!( - "Assertion response received: {:?}", - passkey_response - )); - - // Create proper WebAuthn response from passkey_response - match passkey_response { - PasskeyResponse::AssertionResponse { - credential_id, - authenticator_data, - signature, - user_handle, - rp_id: _, - client_data_hash: _, - } => { - debug_log("Creating WebAuthn get assertion response"); - - match create_get_assertion_response( - credential_id, - authenticator_data, - signature, - user_handle, - ) { - Ok(webauthn_response) => { - debug_log("Successfully created WebAuthn assertion response"); - (*response).encoded_response_byte_count = - (*webauthn_response).encoded_response_byte_count; - (*response).encoded_response_pointer = - (*webauthn_response).encoded_response_pointer; - HRESULT(0) - } - Err(e) => { - debug_log(&format!( - "ERROR: Failed to create WebAuthn assertion response: {}", - e - )); - HRESULT(-1) - } - } - } - PasskeyResponse::Error { message } => { - debug_log(&format!("Assertion request failed: {}", message)); - HRESULT(-1) - } - _ => { - debug_log("ERROR: Unexpected response type for assertion request"); - HRESULT(-1) - } - } - } else { - debug_log("ERROR: No response from assertion request"); - HRESULT(-1) + // Extract RP information + let rpid = if decoded_request.pwszRpId.is_null() { + tracing::debug!("ERROR: RP ID is null"); + return Err(HRESULT(-1)); + } else { + match wstr_to_string(decoded_request.pwszRpId) { + Ok(id) => id, + Err(e) => { + tracing::debug!("ERROR: Failed to decode RP ID: {}", e); + return Err(HRESULT(-1)); } } - Err(e) => { - debug_log(&format!( - "ERROR: Failed to decode get assertion request: {}", - e - )); - HRESULT(-1) + }; + + // Extract client data hash + let client_data_hash = + if decoded_request.cbClientDataHash == 0 || decoded_request.pbClientDataHash.is_null() { + tracing::debug!("ERROR: Client data hash is required for assertion"); + return Err(HRESULT(-1)); + } else { + let hash_slice = std::slice::from_raw_parts( + decoded_request.pbClientDataHash, + decoded_request.cbClientDataHash as usize, + ); + hash_slice.to_vec() + }; + + // Extract user verification requirement from authenticator options + let user_verification = if !decoded_request.pAuthenticatorOptions.is_null() { + let auth_options = &*decoded_request.pAuthenticatorOptions; + match auth_options.user_verification { + 1 => Some(UserVerificationRequirement::Required), + -1 => Some(UserVerificationRequirement::Discouraged), + 0 | _ => Some(UserVerificationRequirement::Preferred), // Default or undefined } - } + } else { + None + }; + + // Extract allowed credentials from credential list + let allowed_credentials = parse_credential_list(&decoded_request.CredentialList); + + // Create Windows assertion request + let assertion_request = WindowsAssertionRequest { + rpid: rpid.clone(), + client_data_hash, + allowed_credentials: allowed_credentials.clone(), + user_verification: user_verification.unwrap_or_default(), + }; + + debug_log(&format!( + "Get assertion request - RP: {}, Allowed credentials: {:?}", + rpid, allowed_credentials + )); + + // Send assertion request + let passkey_response = send_assertion_request(ipc_client, &transaction_id, &assertion_request) + .map_err(|err| { + tracing::error!("Assertion request failed: {err}"); + HRESULT(-1) + })?; + tracing::debug!("Assertion response received: {:?}", passkey_response); + + // Create proper WebAuthn response from passkey_response + tracing::debug!("Creating WebAuthn get assertion response"); + + let webauthn_response = create_get_assertion_response( + passkey_response.credential_id, + passkey_response.authenticator_data, + passkey_response.signature, + passkey_response.user_handle, + ) + .map_err(|err| { + format!("Failed to create WebAuthn assertion response: {err}"); + HRESULT(-1) + })?; + tracing::debug!("Successfully created WebAuthn assertion response"); + (*response).encoded_response_byte_count = (*webauthn_response).encoded_response_byte_count; + (*response).encoded_response_pointer = (*webauthn_response).encoded_response_pointer; + Ok(()) } #[cfg(test)] diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/src/com_provider.rs b/apps/desktop/desktop_native/windows_plugin_authenticator/src/com_provider.rs index 1d83df4d9db..1efddab66a6 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/com_provider.rs +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/src/com_provider.rs @@ -82,7 +82,7 @@ pub unsafe fn parse_credential_list(credential_list: &WEBAUTHN_CREDENTIAL_LIST) let mut allowed_credentials = Vec::new(); if credential_list.cCredentials == 0 || credential_list.ppCredentials.is_null() { - debug_log("No credentials in credential list"); + tracing::debug!("No credentials in credential list"); return allowed_credentials; } @@ -99,7 +99,7 @@ pub unsafe fn parse_credential_list(credential_list: &WEBAUTHN_CREDENTIAL_LIST) for (i, &credential_ptr) in credentials_array.iter().enumerate() { if credential_ptr.is_null() { - debug_log(&format!("WARNING: Credential {} is null, skipping", i)); + tracing::debug!("WARNING: Credential {} is null, skipping", i); continue; } @@ -145,11 +145,11 @@ impl IPluginAuthenticator_Impl for PluginAuthenticatorComObject_Impl { request: *const WebAuthnPluginOperationRequest, response: *mut WebAuthnPluginOperationResponse, ) -> HRESULT { - debug_log("MakeCredential() called"); - debug_log("version2"); + tracing::debug!("MakeCredential() called"); + tracing::debug!("version2"); // Convert to legacy format for internal processing if request.is_null() || response.is_null() { - debug_log("MakeCredential: Invalid request or response pointers passed"); + tracing::debug!("MakeCredential: Invalid request or response pointers passed"); return HRESULT(-1); } @@ -164,23 +164,27 @@ impl IPluginAuthenticator_Impl for PluginAuthenticatorComObject_Impl { request: *const WebAuthnPluginOperationRequest, response: *mut WebAuthnPluginOperationResponse, ) -> HRESULT { - debug_log("GetAssertion() called"); + tracing::debug!("GetAssertion() called"); if request.is_null() || response.is_null() { return HRESULT(-1); } - plugin_get_assertion(request, response) + + match plugin_get_assertion(&self.client, request, response) { + Ok(()) => S_OK, + Err(err) => err, + } } unsafe fn CancelOperation( &self, _request: *const WebAuthnPluginCancelOperationRequest, ) -> HRESULT { - debug_log("CancelOperation() called"); + tracing::debug!("CancelOperation() called"); HRESULT(0) } unsafe fn GetLockStatus(&self, lock_status: *mut PluginLockStatus) -> HRESULT { - debug_log("GetLockStatus() called"); + tracing::debug!("GetLockStatus() called"); if lock_status.is_null() { return HRESULT(-2147024809); // E_INVALIDARG } @@ -196,10 +200,10 @@ impl IClassFactory_Impl for Factory_Impl { iid: *const windows_core::GUID, object: *mut *mut core::ffi::c_void, ) -> windows_core::Result<()> { - debug_log("Creating COM server instance."); - debug_log("Trying to connect to Bitwarden IPC"); + tracing::debug!("Creating COM server instance."); + tracing::debug!("Trying to connect to Bitwarden IPC"); let client = WindowsProviderClient::connect(); - debug_log("Connected to Bitwarden IPC"); + tracing::debug!("Connected to Bitwarden IPC"); let unknown: IInspectable = PluginAuthenticatorComObject { client }.into(); // TODO: IUnknown ? unsafe { unknown.query(iid, object).ok() } } diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/src/ipc2/assertion.rs b/apps/desktop/desktop_native/windows_plugin_authenticator/src/ipc2/assertion.rs index ebef42151d4..32a9b9a0b67 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/ipc2/assertion.rs +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/src/ipc2/assertion.rs @@ -7,12 +7,12 @@ use super::{BitwardenError, Callback, Position, UserVerification}; #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PasskeyAssertionRequest { - rp_id: String, - client_data_hash: Vec, - user_verification: UserVerification, - allowed_credentials: Vec>, - window_xy: Position, - //extension_input: Vec, TODO: Implement support for extensions + pub rp_id: String, + pub client_data_hash: Vec, + pub user_verification: UserVerification, + pub allowed_credentials: Vec>, + pub window_xy: Position, + // pub extension_input: Vec, TODO: Implement support for extensions } #[derive(Debug, Serialize, Deserialize)] @@ -28,15 +28,15 @@ pub struct PasskeyAssertionWithoutUserInterfaceRequest { window_xy: Position, } -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct PasskeyAssertionResponse { - rp_id: String, - user_handle: Vec, - signature: Vec, - client_data_hash: Vec, - authenticator_data: Vec, - credential_id: Vec, + pub rp_id: String, + pub user_handle: Vec, + pub signature: Vec, + pub client_data_hash: Vec, + pub authenticator_data: Vec, + pub credential_id: Vec, } pub trait PreparePasskeyAssertionCallback: Send + Sync { diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/src/ipc2/mod.rs b/apps/desktop/desktop_native/windows_plugin_authenticator/src/ipc2/mod.rs index 6db7fe389c7..256c23c215b 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/ipc2/mod.rs +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/src/ipc2/mod.rs @@ -2,8 +2,12 @@ use std::{ collections::HashMap, error::Error, fmt::Display, - sync::{atomic::AtomicU32, Arc, Mutex, Once}, - time::Instant, + sync::{ + atomic::AtomicU32, + mpsc::{self, Receiver, Sender}, + Arc, Mutex, Once, + }, + time::{Duration, Instant}, }; use futures::FutureExt; @@ -308,3 +312,58 @@ impl WindowsProviderClient { } } } + +pub struct TimedCallback { + tx: Mutex>>>, + rx: Mutex>>, +} + +impl TimedCallback { + pub fn new() -> Self { + let (tx, rx) = mpsc::channel(); + Self { + tx: Mutex::new(Some(tx)), + rx: Mutex::new(rx), + } + } + + pub fn wait_for_response( + &self, + timeout: Duration, + ) -> Result, mpsc::RecvTimeoutError> { + self.rx.lock().unwrap().recv_timeout(timeout) + } + + fn send(&self, response: Result) { + match self.tx.lock().unwrap().take() { + Some(tx) => { + if let Err(_) = tx.send(response) { + tracing::error!("Windows provider channel closed before receiving IPC response from Electron") + } + } + None => { + tracing::error!("Callback channel used before response: multi-threading issue?"); + } + } + } +} + +impl PreparePasskeyRegistrationCallback for TimedCallback { + fn on_complete(&self, credential: PasskeyRegistrationResponse) { + self.send(Ok(credential)); + } + + fn on_error(&self, error: BitwardenError) { + self.send(Err(error)) + } +} + +impl PreparePasskeyAssertionCallback for TimedCallback { + fn on_complete(&self, credential: PasskeyAssertionResponse) { + self.send(Ok(credential)); + } + + fn on_error(&self, error: BitwardenError) { + self.send(Err(error)) + } +} diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/src/make_credential.rs b/apps/desktop/desktop_native/windows_plugin_authenticator/src/make_credential.rs index 37472208e29..5aad751f7a9 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/make_credential.rs +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/src/make_credential.rs @@ -18,8 +18,8 @@ use crate::com_provider::{ use crate::ipc2::{ self, BitwardenError, PasskeyAssertionRequest, PasskeyAssertionResponse, PasskeyRegistrationRequest, PasskeyRegistrationResponse, Position, - PreparePasskeyAssertionCallback, PreparePasskeyRegistrationCallback, UserVerification, - WindowsProviderClient, + PreparePasskeyAssertionCallback, PreparePasskeyRegistrationCallback, TimedCallback, + UserVerification, WindowsProviderClient, }; use crate::types::UserVerificationRequirement; use crate::util::{debug_log, delay_load, wstr_to_string, WindowsString}; @@ -262,7 +262,7 @@ impl Drop for DecodedMakeCredentialRequest { fn drop(&mut self) { if !self.ptr.is_null() { if let Some(free_fn) = self.free_fn { - debug_log("Freeing decoded make credential request"); + tracing::debug!("Freeing decoded make credential request"); unsafe { free_fn(self.ptr as *mut WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST); } @@ -275,7 +275,7 @@ impl Drop for DecodedMakeCredentialRequest { unsafe fn decode_make_credential_request( encoded_request: &[u8], ) -> Result { - debug_log("Attempting to decode make credential request using Windows API"); + tracing::debug!("Attempting to decode make credential request using Windows API"); // Try to load the Windows API decode function let decode_fn = match delay_load::( @@ -318,7 +318,7 @@ unsafe fn decode_make_credential_request( } if make_credential_request.is_null() { - debug_log("ERROR: Windows API succeeded but returned null pointer"); + tracing::debug!("ERROR: Windows API succeeded but returned null pointer"); return Err("Windows API returned null pointer".to_string()); } @@ -356,86 +356,13 @@ fn send_registration_request( let request_json = serde_json::to_string(&passkey_request) .map_err(|err| format!("Failed to serialize registration request: {err}"))?; - debug_log(&format!("Sending registration request: {}", request_json)); - let callback = Arc::new(Callback::new()); + tracing::debug!("Sending registration request: {}", request_json); + let callback = Arc::new(TimedCallback::new()); ipc_client.prepare_passkey_registration(passkey_request, callback.clone()); callback .wait_for_response(Duration::from_secs(30)) .map_err(|_| "Registration request timed out".to_string())? .map_err(|err| err.to_string()) - - /* - { - Ok(Ok(response)) => { - tracing::debug!("Received registration response from Electron: {response:?}"); - Some(response) - } - Ok(Err(err)) => { - tracing::error!("Registration request failed: {err}"); - None - } - Err(_) => { - tracing::error!("Timed out waiting for registration response"); - None - } - } - */ - // crate::ipc::send_passkey_request(RequestType::Registration, request_json, &request.rpid) -} - -struct Callback { - tx: Mutex>>>, - rx: Mutex>>, -} - -impl Callback { - fn new() -> Self { - let (tx, rx) = mpsc::channel(); - Self { - tx: Mutex::new(Some(tx)), - rx: Mutex::new(rx), - } - } - - fn wait_for_response( - &self, - timeout: Duration, - ) -> Result, mpsc::RecvTimeoutError> { - self.rx.lock().unwrap().recv_timeout(timeout) - } - - fn send(&self, response: Result) { - match self.tx.lock().unwrap().take() { - Some(tx) => { - if let Err(_) = tx.send(response) { - tracing::error!("Windows provider channel closed before receiving IPC response from Electron") - } - } - None => { - tracing::error!("Callback channel used before response: multi-threading issue?"); - } - } - } -} - -impl PreparePasskeyRegistrationCallback for Callback { - fn on_complete(&self, credential: PasskeyRegistrationResponse) { - self.send(Ok(credential)); - } - - fn on_error(&self, error: BitwardenError) { - self.send(Err(error)) - } -} - -impl PreparePasskeyAssertionCallback for Callback { - fn on_complete(&self, credential: PasskeyAssertionResponse) { - self.send(Ok(credential)); - } - - fn on_error(&self, error: BitwardenError) { - self.send(Err(error)) - } } /// Creates a CTAP make credential response from Bitwarden's WebAuthn registration response @@ -560,7 +487,7 @@ unsafe fn create_make_credential_response( encoded_response_pointer: response_ptr, }, ); - debug_log(&format!("CTAP-encoded attestation object: {response:?}")); + tracing::debug!("CTAP-encoded attestation object: {response:?}"); Ok(operation_response_ptr) */ } @@ -571,15 +498,15 @@ pub unsafe fn plugin_make_credential( request: *const WebAuthnPluginOperationRequest, response: *mut WebAuthnPluginOperationResponse, ) -> Result<(), HRESULT> { - debug_log("=== PluginMakeCredential() called ==="); + tracing::debug!("=== PluginMakeCredential() called ==="); if request.is_null() { - debug_log("ERROR: NULL request pointer"); + tracing::debug!("ERROR: NULL request pointer"); return Err(HRESULT(-1)); } if response.is_null() { - debug_log("ERROR: NULL response pointer"); + tracing::debug!("ERROR: NULL response pointer"); return Err(HRESULT(-1)); } @@ -587,7 +514,7 @@ pub unsafe fn plugin_make_credential( let transaction_id = format!("{:?}", req.transaction_id); if req.encoded_request_byte_count == 0 || req.encoded_request_pointer.is_null() { - debug_log("ERROR: No encoded request data provided"); + tracing::debug!("ERROR: No encoded request data provided"); return Err(HRESULT(-1)); } @@ -609,24 +536,24 @@ pub unsafe fn plugin_make_credential( HRESULT(-1) })?; let decoded_request = decoded_wrapper.as_ref(); - debug_log("Successfully decoded make credential request using Windows API"); + tracing::debug!("Successfully decoded make credential request using Windows API"); // Extract RP information if decoded_request.pRpInformation.is_null() { - debug_log("ERROR: RP information is null"); + tracing::debug!("ERROR: RP information is null"); return Err(HRESULT(-1)); } let rp_info = &*decoded_request.pRpInformation; let rpid = if rp_info.pwszId.is_null() { - debug_log("ERROR: RP ID is null"); + tracing::debug!("ERROR: RP ID is null"); return Err(HRESULT(-1)); } else { match wstr_to_string(rp_info.pwszId) { Ok(id) => id, Err(e) => { - debug_log(&format!("ERROR: Failed to decode RP ID: {}", e)); + tracing::debug!("ERROR: Failed to decode RP ID: {}", e); return Err(HRESULT(-1)); } } @@ -640,14 +567,14 @@ pub unsafe fn plugin_make_credential( // Extract user information if decoded_request.pUserInformation.is_null() { - debug_log("ERROR: User information is null"); + tracing::debug!("ERROR: User information is null"); return Err(HRESULT(-1)); } let user = &*decoded_request.pUserInformation; let user_id = if user.pbId.is_null() || user.cbId == 0 { - debug_log("ERROR: User ID is required for registration"); + tracing::debug!("ERROR: User ID is required for registration"); return Err(HRESULT(-1)); } else { let id_slice = std::slice::from_raw_parts(user.pbId, user.cbId as usize); @@ -655,13 +582,13 @@ pub unsafe fn plugin_make_credential( }; let user_name = if user.pwszName.is_null() { - debug_log("ERROR: User name is required for registration"); + tracing::debug!("ERROR: User name is required for registration"); return Err(HRESULT(-1)); } else { match wstr_to_string(user.pwszName) { Ok(name) => name, Err(_) => { - debug_log("ERROR: Failed to decode user name"); + tracing::debug!("ERROR: Failed to decode user name"); return Err(HRESULT(-1)); } } @@ -678,7 +605,7 @@ pub unsafe fn plugin_make_credential( // Extract client data hash let client_data_hash = if decoded_request.cbClientDataHash == 0 || decoded_request.pbClientDataHash.is_null() { - debug_log("ERROR: Client data hash is required for registration"); + tracing::debug!("ERROR: Client data hash is required for registration"); return Err(HRESULT(-1)); } else { let hash_slice = std::slice::from_raw_parts( @@ -764,10 +691,10 @@ pub unsafe fn plugin_make_credential( )); // Create proper WebAuthn response from passkey_response - debug_log("Creating WebAuthn make credential response"); + tracing::debug!("Creating WebAuthn make credential response"); let mut webauthn_response = create_make_credential_response(passkey_response.attestation_object).map_err(|err| { - debug_log(&format!("ERROR: Failed to create WebAuthn response: {err}")); + tracing::debug!("ERROR: Failed to create WebAuthn response: {err}"); HRESULT(-1) })?; debug_log(&format!( @@ -775,7 +702,7 @@ pub unsafe fn plugin_make_credential( )); (*response).encoded_response_byte_count = webauthn_response.len() as u32; (*response).encoded_response_pointer = webauthn_response.as_mut_ptr(); - debug_log(&format!("Set pointer, returning HRESULT(0)")); + tracing::debug!("Set pointer, returning HRESULT(0)"); _ = ManuallyDrop::new(webauthn_response); Ok(()) }