1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-02 17:53:41 +00:00

Wire up Windows IPC for GetAssertion

This commit is contained in:
Isaiah Inuwa
2025-11-08 21:14:00 -06:00
parent 0dae3a9c5b
commit 2196a8339c
5 changed files with 237 additions and 276 deletions

View File

@@ -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<DecodedGetAssertionRequest, String> {
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<WebAuthNDecodeGetAssertionRequestFn> =
@@ -122,38 +130,40 @@ pub struct WindowsAssertionRequest {
/// Helper for assertion requests
fn send_assertion_request(
ipc_client: &WindowsProviderClient,
transaction_id: &str,
request: &WindowsAssertionRequest,
) -> Option<PasskeyResponse> {
) -> Result<PasskeyAssertionResponse, String> {
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)]

View File

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

View File

@@ -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<u8>,
user_verification: UserVerification,
allowed_credentials: Vec<Vec<u8>>,
window_xy: Position,
//extension_input: Vec<u8>, TODO: Implement support for extensions
pub rp_id: String,
pub client_data_hash: Vec<u8>,
pub user_verification: UserVerification,
pub allowed_credentials: Vec<Vec<u8>>,
pub window_xy: Position,
// pub extension_input: Vec<u8>, 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<u8>,
signature: Vec<u8>,
client_data_hash: Vec<u8>,
authenticator_data: Vec<u8>,
credential_id: Vec<u8>,
pub rp_id: String,
pub user_handle: Vec<u8>,
pub signature: Vec<u8>,
pub client_data_hash: Vec<u8>,
pub authenticator_data: Vec<u8>,
pub credential_id: Vec<u8>,
}
pub trait PreparePasskeyAssertionCallback: Send + Sync {

View File

@@ -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<T> {
tx: Mutex<Option<Sender<Result<T, BitwardenError>>>>,
rx: Mutex<Receiver<Result<T, BitwardenError>>>,
}
impl<T> TimedCallback<T> {
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<Result<T, BitwardenError>, mpsc::RecvTimeoutError> {
self.rx.lock().unwrap().recv_timeout(timeout)
}
fn send(&self, response: Result<T, BitwardenError>) {
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<PasskeyRegistrationResponse> {
fn on_complete(&self, credential: PasskeyRegistrationResponse) {
self.send(Ok(credential));
}
fn on_error(&self, error: BitwardenError) {
self.send(Err(error))
}
}
impl PreparePasskeyAssertionCallback for TimedCallback<PasskeyAssertionResponse> {
fn on_complete(&self, credential: PasskeyAssertionResponse) {
self.send(Ok(credential));
}
fn on_error(&self, error: BitwardenError) {
self.send(Err(error))
}
}

View File

@@ -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<DecodedMakeCredentialRequest, String> {
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::<WebAuthNDecodeMakeCredentialRequestFn>(
@@ -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<T> {
tx: Mutex<Option<Sender<Result<T, BitwardenError>>>>,
rx: Mutex<Receiver<Result<T, BitwardenError>>>,
}
impl<T> Callback<T> {
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<Result<T, BitwardenError>, mpsc::RecvTimeoutError> {
self.rx.lock().unwrap().recv_timeout(timeout)
}
fn send(&self, response: Result<T, BitwardenError>) {
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<PasskeyRegistrationResponse> {
fn on_complete(&self, credential: PasskeyRegistrationResponse) {
self.send(Ok(credential));
}
fn on_error(&self, error: BitwardenError) {
self.send(Err(error))
}
}
impl PreparePasskeyAssertionCallback for Callback<PasskeyAssertionResponse> {
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(())
}