mirror of
https://github.com/bitwarden/browser
synced 2026-02-06 03:33:30 +00:00
Sort of get window position from handle on Windows plugin
This commit is contained in:
@@ -14,7 +14,6 @@ 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;
|
||||
|
||||
@@ -119,47 +118,23 @@ unsafe fn decode_get_assertion_request(
|
||||
))
|
||||
}
|
||||
|
||||
/// Windows WebAuthn assertion request context
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WindowsAssertionRequest {
|
||||
pub rpid: String,
|
||||
pub client_data_hash: Vec<u8>,
|
||||
pub allowed_credentials: Vec<Vec<u8>>,
|
||||
pub user_verification: UserVerificationRequirement,
|
||||
}
|
||||
|
||||
/// Helper for assertion requests
|
||||
fn send_assertion_request(
|
||||
ipc_client: &WindowsProviderClient,
|
||||
transaction_id: &str,
|
||||
request: &WindowsAssertionRequest,
|
||||
request: PasskeyAssertionRequest,
|
||||
) -> 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(),
|
||||
client_data_hash: request.client_data_hash.clone(),
|
||||
allowed_credentials: request.allowed_credentials.clone(),
|
||||
user_verification,
|
||||
window_xy: Position { x: 400, y: 400 },
|
||||
};
|
||||
|
||||
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,
|
||||
request.rp_id,
|
||||
request.client_data_hash.len(),
|
||||
request.allowed_credentials,
|
||||
);
|
||||
|
||||
let request_json = serde_json::to_string(&passkey_request)
|
||||
let request_json = serde_json::to_string(&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());
|
||||
ipc_client.prepare_passkey_assertion(request, callback.clone());
|
||||
callback
|
||||
.wait_for_response(Duration::from_secs(30))
|
||||
.map_err(|_| "Registration request timed out".to_string())?
|
||||
@@ -288,6 +263,7 @@ pub unsafe fn plugin_get_assertion(
|
||||
|
||||
let req = &*request;
|
||||
let transaction_id = format!("{:?}", req.transaction_id);
|
||||
let coords = req.window_coordinates().unwrap_or((400, 400));
|
||||
|
||||
debug_log(&format!(
|
||||
"Get assertion request - Transaction: {}",
|
||||
@@ -343,33 +319,38 @@ pub unsafe fn plugin_get_assertion(
|
||||
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
|
||||
1 => UserVerification::Required,
|
||||
-1 => UserVerification::Discouraged,
|
||||
0 | _ => UserVerification::Preferred, // Default or undefined
|
||||
}
|
||||
} else {
|
||||
None
|
||||
UserVerification::Preferred // Default or undefined
|
||||
};
|
||||
|
||||
// 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(),
|
||||
let assertion_request = PasskeyAssertionRequest {
|
||||
rp_id: rpid.clone(),
|
||||
client_data_hash,
|
||||
allowed_credentials: allowed_credentials.clone(),
|
||||
user_verification: user_verification.unwrap_or_default(),
|
||||
window_xy: Position {
|
||||
x: coords.0,
|
||||
y: coords.1,
|
||||
},
|
||||
user_verification,
|
||||
};
|
||||
|
||||
debug_log(&format!(
|
||||
tracing::debug!(
|
||||
"Get assertion request - RP: {}, Allowed credentials: {:?}",
|
||||
rpid, allowed_credentials
|
||||
));
|
||||
rpid,
|
||||
allowed_credentials
|
||||
);
|
||||
|
||||
// Send assertion request
|
||||
let passkey_response = send_assertion_request(ipc_client, &transaction_id, &assertion_request)
|
||||
.map_err(|err| {
|
||||
let passkey_response =
|
||||
send_assertion_request(ipc_client, assertion_request).map_err(|err| {
|
||||
tracing::error!("Assertion request failed: {err}");
|
||||
HRESULT(-1)
|
||||
})?;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use windows::Win32::Foundation::S_OK;
|
||||
use windows::Win32::Foundation::{RECT, S_OK};
|
||||
use windows::Win32::System::Com::*;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetWindowRect;
|
||||
use windows_core::{implement, interface, IInspectable, IUnknown, Interface, HRESULT};
|
||||
|
||||
use crate::assert::plugin_get_assertion;
|
||||
@@ -39,6 +40,18 @@ pub struct WebAuthnPluginOperationRequest {
|
||||
pub encoded_request_pointer: *mut u8,
|
||||
}
|
||||
|
||||
impl WebAuthnPluginOperationRequest {
|
||||
pub fn window_coordinates(&self) -> Result<(i32, i32), windows::core::Error> {
|
||||
let mut window: RECT = RECT::default();
|
||||
unsafe {
|
||||
GetWindowRect(self.window_handle, &mut window)?;
|
||||
}
|
||||
// TODO: This isn't quite right, but it's closer than what we had
|
||||
let center_x = (window.right + window.left) / 2;
|
||||
let center_y = (window.bottom + window.top) / 2;
|
||||
Ok((center_x, center_y))
|
||||
}
|
||||
}
|
||||
/// Used as a response when creating and asserting credentials.
|
||||
/// Header File Name: _WEBAUTHN_PLUGIN_OPERATION_RESPONSE
|
||||
/// Header File Usage: MakeCredential()
|
||||
|
||||
@@ -14,9 +14,7 @@ mod util;
|
||||
mod webauthn;
|
||||
|
||||
// Re-export main functionality
|
||||
pub use assert::WindowsAssertionRequest;
|
||||
pub use com_registration::{add_authenticator, initialize_com_library, register_com_library};
|
||||
pub use make_credential::WindowsRegistrationRequest;
|
||||
pub use types::UserVerificationRequirement;
|
||||
|
||||
/// Handles initialization and registration for the Bitwarden desktop app as a
|
||||
|
||||
@@ -1,43 +1,21 @@
|
||||
use serde_json;
|
||||
use std::alloc::{alloc, Layout};
|
||||
use std::collections::HashMap;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::sync::mpsc::Receiver;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::{
|
||||
mpsc::{self, Sender},
|
||||
Arc,
|
||||
};
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{ptr, slice};
|
||||
use windows_core::{s, HRESULT};
|
||||
|
||||
use crate::com_provider::{
|
||||
parse_credential_list, WebAuthnPluginOperationRequest, WebAuthnPluginOperationResponse,
|
||||
};
|
||||
use crate::ipc2::{
|
||||
self, BitwardenError, PasskeyAssertionRequest, PasskeyAssertionResponse,
|
||||
PasskeyRegistrationRequest, PasskeyRegistrationResponse, Position,
|
||||
PreparePasskeyAssertionCallback, PreparePasskeyRegistrationCallback, TimedCallback,
|
||||
PasskeyRegistrationRequest, PasskeyRegistrationResponse, Position, TimedCallback,
|
||||
UserVerification, WindowsProviderClient,
|
||||
};
|
||||
use crate::types::UserVerificationRequirement;
|
||||
use crate::util::{debug_log, delay_load, wstr_to_string, WindowsString};
|
||||
use crate::webauthn::WEBAUTHN_CREDENTIAL_LIST;
|
||||
|
||||
/// Windows WebAuthn registration request context
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WindowsRegistrationRequest {
|
||||
pub rpid: String,
|
||||
pub user_id: Vec<u8>,
|
||||
pub user_name: String,
|
||||
pub user_display_name: Option<String>,
|
||||
pub client_data_hash: Vec<u8>,
|
||||
pub excluded_credentials: Vec<Vec<u8>>,
|
||||
pub user_verification: UserVerificationRequirement,
|
||||
pub supported_algorithms: Vec<i32>,
|
||||
}
|
||||
|
||||
// Windows API types for WebAuthn (from webauthn.h.sample)
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
@@ -331,34 +309,16 @@ unsafe fn decode_make_credential_request(
|
||||
/// Helper for registration requests
|
||||
fn send_registration_request(
|
||||
ipc_client: &WindowsProviderClient,
|
||||
transaction_id: &str,
|
||||
request: &WindowsRegistrationRequest,
|
||||
request: PasskeyRegistrationRequest,
|
||||
) -> Result<PasskeyRegistrationResponse, String> {
|
||||
debug_log(&format!("Registration request data - RP ID: {}, User ID: {} bytes, User name: {}, Client data hash: {} bytes, Algorithms: {:?}, Excluded credentials: {}",
|
||||
request.rpid, request.user_id.len(), request.user_name, request.client_data_hash.len(), request.supported_algorithms, request.excluded_credentials.len()));
|
||||
request.rp_id, request.user_handle.len(), request.user_name, request.client_data_hash.len(), request.supported_algorithms, request.excluded_credentials.len()));
|
||||
|
||||
let user_verification = match request.user_verification {
|
||||
UserVerificationRequirement::Discouraged => UserVerification::Discouraged,
|
||||
UserVerificationRequirement::Preferred => UserVerification::Preferred,
|
||||
UserVerificationRequirement::Required => UserVerification::Required,
|
||||
};
|
||||
let passkey_request = PasskeyRegistrationRequest {
|
||||
rp_id: request.rpid.clone(),
|
||||
// transaction_id: transaction_id.to_string(),
|
||||
user_handle: request.user_id.clone(),
|
||||
user_name: request.user_name.clone(),
|
||||
client_data_hash: request.client_data_hash.clone(),
|
||||
user_verification,
|
||||
window_xy: Position { x: 400, y: 400 }, // TODO: Get actual window position
|
||||
supported_algorithms: request.supported_algorithms.clone(),
|
||||
excluded_credentials: request.excluded_credentials.clone(),
|
||||
};
|
||||
|
||||
let request_json = serde_json::to_string(&passkey_request)
|
||||
let request_json = serde_json::to_string(&request)
|
||||
.map_err(|err| format!("Failed to serialize registration request: {err}"))?;
|
||||
tracing::debug!("Sending registration request: {}", request_json);
|
||||
let callback = Arc::new(TimedCallback::new());
|
||||
ipc_client.prepare_passkey_registration(passkey_request, callback.clone());
|
||||
ipc_client.prepare_passkey_registration(request, callback.clone());
|
||||
callback
|
||||
.wait_for_response(Duration::from_secs(30))
|
||||
.map_err(|_| "Registration request timed out".to_string())?
|
||||
@@ -513,6 +473,8 @@ pub unsafe fn plugin_make_credential(
|
||||
let req = &*request;
|
||||
let transaction_id = format!("{:?}", req.transaction_id);
|
||||
|
||||
let coords = req.window_coordinates().unwrap_or((400, 400));
|
||||
|
||||
if req.encoded_request_byte_count == 0 || req.encoded_request_pointer.is_null() {
|
||||
tracing::error!("No encoded request data provided");
|
||||
return Err(HRESULT(-1));
|
||||
@@ -643,12 +605,12 @@ pub unsafe fn plugin_make_credential(
|
||||
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
|
||||
1 => UserVerification::Required,
|
||||
-1 => UserVerification::Discouraged,
|
||||
0 | _ => UserVerification::Preferred, // Default or undefined
|
||||
}
|
||||
} else {
|
||||
None
|
||||
UserVerification::Preferred // Default or undefined
|
||||
};
|
||||
|
||||
// Extract excluded credentials from credential list
|
||||
@@ -661,15 +623,19 @@ pub unsafe fn plugin_make_credential(
|
||||
}
|
||||
|
||||
// Create Windows registration request
|
||||
let registration_request = WindowsRegistrationRequest {
|
||||
rpid: rpid.clone(),
|
||||
user_id: user_info.0,
|
||||
let registration_request = PasskeyRegistrationRequest {
|
||||
rp_id: rpid.clone(),
|
||||
user_handle: user_info.0,
|
||||
user_name: user_info.1,
|
||||
user_display_name: user_info.2,
|
||||
// user_display_name: user_info.2,
|
||||
client_data_hash,
|
||||
excluded_credentials,
|
||||
user_verification: user_verification.unwrap_or_default(),
|
||||
user_verification: user_verification,
|
||||
supported_algorithms,
|
||||
window_xy: Position {
|
||||
x: coords.0,
|
||||
y: coords.1,
|
||||
},
|
||||
};
|
||||
|
||||
debug_log(&format!(
|
||||
@@ -679,12 +645,10 @@ pub unsafe fn plugin_make_credential(
|
||||
|
||||
// Send registration request
|
||||
let passkey_response =
|
||||
send_registration_request(ipc_client, &transaction_id, ®istration_request).map_err(
|
||||
|err| {
|
||||
tracing::error!("Registration request failed: {err}");
|
||||
HRESULT(-1)
|
||||
},
|
||||
)?;
|
||||
send_registration_request(ipc_client, registration_request).map_err(|err| {
|
||||
tracing::error!("Registration request failed: {err}");
|
||||
HRESULT(-1)
|
||||
})?;
|
||||
debug_log(&format!(
|
||||
"Registration response received: {:?}",
|
||||
passkey_response
|
||||
|
||||
Reference in New Issue
Block a user