mirror of
https://github.com/bitwarden/browser
synced 2026-02-02 09:43:29 +00:00
Refactor to WindowsRequest instead of RequestContext
This commit is contained in:
@@ -26,11 +26,21 @@ pub struct EXPERIMENTAL_WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST {
|
||||
|
||||
pub type PEXPERIMENTAL_WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST = *mut EXPERIMENTAL_WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST;
|
||||
|
||||
#[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 pCredentials: *const u8, // Placeholder
|
||||
pub ppCredentials: *const *const WEBAUTHN_CREDENTIAL_EX,
|
||||
}
|
||||
|
||||
// Windows API function signatures for decoding get assertion requests
|
||||
@@ -105,60 +115,89 @@ pub unsafe fn decode_get_assertion_request(encoded_request: &[u8]) -> Result<Dec
|
||||
Ok(DecodedGetAssertionRequest::new(pp_get_assertion_request, free_fn))
|
||||
}
|
||||
|
||||
/// Context information parsed from the incoming request
|
||||
/// Windows WebAuthn assertion request context
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RequestContext {
|
||||
pub rpid: Option<String>,
|
||||
pub struct WindowsAssertionRequest {
|
||||
pub rpid: String,
|
||||
pub client_data_hash: Vec<u8>,
|
||||
pub allowed_credentials: Vec<Vec<u8>>,
|
||||
pub user_verification: Option<UserVerificationRequirement>,
|
||||
pub user_id: Option<Vec<u8>>,
|
||||
pub user_name: Option<String>,
|
||||
pub user_display_name: Option<String>,
|
||||
pub client_data_hash: Option<Vec<u8>>,
|
||||
pub supported_algorithms: Vec<i32>, // COSE algorithm identifiers
|
||||
pub user_verification: UserVerificationRequirement,
|
||||
}
|
||||
|
||||
impl Default for RequestContext {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
rpid: None,
|
||||
allowed_credentials: Vec::new(),
|
||||
user_verification: None,
|
||||
user_id: None,
|
||||
user_name: None,
|
||||
user_display_name: None,
|
||||
client_data_hash: None,
|
||||
supported_algorithms: Vec::new(),
|
||||
}
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
/// Parse allowed credentials from WEBAUTHN_CREDENTIAL_LIST
|
||||
pub unsafe fn parse_credential_list(credential_list: &WEBAUTHN_CREDENTIAL_LIST) -> Vec<Vec<u8>> {
|
||||
let mut allowed_credentials = Vec::new();
|
||||
|
||||
if credential_list.cCredentials == 0 || credential_list.ppCredentials.is_null() {
|
||||
util::message("No credentials in credential list");
|
||||
return allowed_credentials;
|
||||
}
|
||||
|
||||
util::message(&format!("Parsing {} credentials from credential list", credential_list.cCredentials));
|
||||
|
||||
// ppCredentials is an array of pointers to WEBAUTHN_CREDENTIAL_EX
|
||||
let credentials_array = std::slice::from_raw_parts(
|
||||
credential_list.ppCredentials,
|
||||
credential_list.cCredentials as usize
|
||||
);
|
||||
|
||||
for (i, &credential_ptr) in credentials_array.iter().enumerate() {
|
||||
if credential_ptr.is_null() {
|
||||
util::message(&format!("WARNING: Credential {} is null, skipping", i));
|
||||
continue;
|
||||
}
|
||||
|
||||
let credential = &*credential_ptr;
|
||||
|
||||
if credential.cbId == 0 || credential.pbId.is_null() {
|
||||
util::message(&format!("WARNING: Credential {} has invalid ID, skipping", i));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract credential ID bytes
|
||||
let credential_id_slice = std::slice::from_raw_parts(
|
||||
credential.pbId,
|
||||
credential.cbId as usize
|
||||
);
|
||||
|
||||
allowed_credentials.push(credential_id_slice.to_vec());
|
||||
util::message(&format!("Parsed credential {}: {} bytes", i, credential.cbId));
|
||||
}
|
||||
|
||||
util::message(&format!("Successfully parsed {} allowed credentials", allowed_credentials.len()));
|
||||
allowed_credentials
|
||||
}
|
||||
|
||||
/// Helper for assertion requests
|
||||
pub fn send_assertion_request(rpid: &str, transaction_id: &str, context: &RequestContext) -> Option<PasskeyResponse> {
|
||||
// Extract client data hash from context - this is required for WebAuthn
|
||||
let client_data_hash = match &context.client_data_hash {
|
||||
Some(hash) if !hash.is_empty() => hash.clone(),
|
||||
_ => {
|
||||
util::message("ERROR: Client data hash is required for assertion but not provided");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let request = PasskeyAssertionRequest {
|
||||
rp_id: rpid.to_string(),
|
||||
pub fn send_assertion_request(transaction_id: &str, request: &WindowsAssertionRequest) -> Option<PasskeyResponse> {
|
||||
let passkey_request = PasskeyAssertionRequest {
|
||||
rp_id: request.rpid.clone(),
|
||||
transaction_id: transaction_id.to_string(),
|
||||
client_data_hash,
|
||||
allowed_credentials: context.allowed_credentials.clone(),
|
||||
user_verification: context.user_verification.unwrap_or_default(),
|
||||
client_data_hash: request.client_data_hash.clone(),
|
||||
allowed_credentials: request.allowed_credentials.clone(),
|
||||
user_verification: request.user_verification.clone(),
|
||||
};
|
||||
|
||||
util::message(&format!("Assertion request data - RP ID: {}, Client data hash: {} bytes, Allowed credentials: {}",
|
||||
rpid, request.client_data_hash.len(), request.allowed_credentials.len()));
|
||||
request.rpid, request.client_data_hash.len(), request.allowed_credentials.len()));
|
||||
|
||||
match serde_json::to_string(&request) {
|
||||
match serde_json::to_string(&passkey_request) {
|
||||
Ok(request_json) => {
|
||||
util::message(&format!("Sending assertion request: {}", request_json));
|
||||
crate::ipc::send_passkey_request(RequestType::Assertion, request_json, rpid)
|
||||
crate::ipc::send_passkey_request(RequestType::Assertion, request_json, &request.rpid)
|
||||
},
|
||||
Err(e) => {
|
||||
util::message(&format!("ERROR: Failed to serialize assertion request: {}", e));
|
||||
|
||||
@@ -4,8 +4,8 @@ use std::ptr;
|
||||
|
||||
use crate::types::*;
|
||||
use crate::utils::{self as util, wstr_to_string};
|
||||
use crate::assert::{RequestContext, decode_get_assertion_request, create_get_assertion_response, send_assertion_request};
|
||||
use crate::make_credential::{decode_make_credential_request, create_make_credential_response, send_registration_request};
|
||||
use crate::assert::{WindowsAssertionRequest, decode_get_assertion_request, create_get_assertion_response, send_assertion_request, parse_credential_list};
|
||||
use crate::make_credential::{WindowsRegistrationRequest, decode_make_credential_request, create_make_credential_response, send_registration_request};
|
||||
|
||||
/// Used when creating and asserting credentials.
|
||||
/// Header File Name: _EXPERIMENTAL_WEBAUTHN_PLUGIN_OPERATION_REQUEST
|
||||
@@ -224,22 +224,30 @@ impl EXPERIMENTAL_IPluginAuthenticator_Impl for PluginAuthenticatorComObject_Imp
|
||||
None
|
||||
};
|
||||
|
||||
// Create request context from properly decoded data
|
||||
let mut request_context = RequestContext::default();
|
||||
request_context.rpid = Some(rpid.clone());
|
||||
request_context.user_id = Some(user_info.0);
|
||||
request_context.user_name = Some(user_info.1);
|
||||
request_context.user_display_name = user_info.2;
|
||||
request_context.client_data_hash = Some(client_data_hash);
|
||||
request_context.supported_algorithms = supported_algorithms;
|
||||
request_context.user_verification = user_verification;
|
||||
// Extract excluded credentials from credential list (for make credential, these are credentials to exclude)
|
||||
let excluded_credentials = parse_credential_list(&decoded_request.CredentialList);
|
||||
if !excluded_credentials.is_empty() {
|
||||
util::message(&format!("Found {} excluded credentials for make credential", excluded_credentials.len()));
|
||||
}
|
||||
|
||||
// Create Windows registration request
|
||||
let registration_request = WindowsRegistrationRequest {
|
||||
rpid: rpid.clone(),
|
||||
user_id: user_info.0,
|
||||
user_name: user_info.1,
|
||||
user_display_name: user_info.2,
|
||||
client_data_hash,
|
||||
excluded_credentials,
|
||||
user_verification: user_verification.unwrap_or_default(),
|
||||
supported_algorithms,
|
||||
};
|
||||
|
||||
util::message(&format!("Make credential request - RP: {}, User: {}",
|
||||
rpid,
|
||||
request_context.user_name.as_deref().unwrap_or("unknown")));
|
||||
registration_request.user_name));
|
||||
|
||||
// Send registration request
|
||||
if let Some(passkey_response) = send_registration_request(&rpid, &transaction_id, &request_context) {
|
||||
if let Some(passkey_response) = send_registration_request(&transaction_id, ®istration_request) {
|
||||
util::message(&format!("Registration response received: {:?}", passkey_response));
|
||||
|
||||
// Create proper WebAuthn response from passkey_response
|
||||
@@ -361,17 +369,21 @@ impl EXPERIMENTAL_IPluginAuthenticator_Impl for PluginAuthenticatorComObject_Imp
|
||||
None
|
||||
};
|
||||
|
||||
// Create request context from properly decoded data
|
||||
let mut request_context = RequestContext::default();
|
||||
request_context.rpid = Some(rpid.clone());
|
||||
request_context.client_data_hash = Some(client_data_hash);
|
||||
request_context.user_verification = user_verification;
|
||||
// TODO: Extract allowed credentials from CredentialList if available
|
||||
// Extract allowed credentials from credential list
|
||||
let allowed_credentials = parse_credential_list(&decoded_request.CredentialList);
|
||||
|
||||
util::message(&format!("Get assertion request - RP: {}", rpid));
|
||||
// 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(),
|
||||
};
|
||||
|
||||
util::message(&format!("Get assertion request - RP: {}, Allowed credentials: {}", rpid, allowed_credentials.len()));
|
||||
|
||||
// Send assertion request
|
||||
if let Some(passkey_response) = send_assertion_request(&rpid, &transaction_id, &request_context) {
|
||||
if let Some(passkey_response) = send_assertion_request(&transaction_id, &assertion_request) {
|
||||
util::message(&format!("Assertion response received: {:?}", passkey_response));
|
||||
|
||||
// Create proper WebAuthn response from passkey_response
|
||||
|
||||
@@ -23,7 +23,8 @@ mod com_buffer;
|
||||
// Re-export main functionality
|
||||
pub use sync::{send_sync_request, sync_credentials_to_windows, get_credentials_from_windows};
|
||||
pub use ipc::{set_request_sender, send_passkey_request};
|
||||
pub use types::{PasskeyRequest, PasskeyResponse, SyncedCredential, RequestEvent, RequestType};
|
||||
pub use types::{PasskeyRequest, PasskeyResponse, SyncedCredential, RequestEvent, RequestType, UserVerificationRequirement};
|
||||
pub use assert::{WindowsAssertionRequest, WindowsRegistrationRequest};
|
||||
pub use com_registration::{initialize_com_library, register_com_library, add_authenticator};
|
||||
|
||||
// Re-export utilities
|
||||
|
||||
@@ -6,7 +6,7 @@ use windows_core::{HRESULT, s};
|
||||
use crate::types::*;
|
||||
use crate::utils::{self as util, delay_load};
|
||||
use crate::com_provider::ExperimentalWebAuthnPluginOperationResponse;
|
||||
use crate::assert::RequestContext;
|
||||
use crate::assert::{WindowsRegistrationRequest, parse_credential_list};
|
||||
|
||||
// Windows API types for WebAuthn (from webauthn.h.sample)
|
||||
#[repr(C)]
|
||||
@@ -44,11 +44,21 @@ pub struct WEBAUTHN_COSE_CREDENTIAL_PARAMETERS {
|
||||
pub pCredentialParameters: *const WEBAUTHN_COSE_CREDENTIAL_PARAMETER,
|
||||
}
|
||||
|
||||
#[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 pCredentials: *const u8, // Placeholder
|
||||
pub ppCredentials: *const *const WEBAUTHN_CREDENTIAL_EX,
|
||||
}
|
||||
|
||||
// Make Credential Request structure (from sample header)
|
||||
@@ -161,57 +171,25 @@ pub unsafe fn decode_make_credential_request(encoded_request: &[u8]) -> Result<D
|
||||
}
|
||||
|
||||
/// Helper for registration requests
|
||||
pub fn send_registration_request(rpid: &str, transaction_id: &str, context: &RequestContext) -> Option<PasskeyResponse> {
|
||||
// Validate required fields
|
||||
if rpid.is_empty() {
|
||||
util::message("ERROR: RP ID is required but empty");
|
||||
return None;
|
||||
}
|
||||
pub fn send_registration_request(transaction_id: &str, request: &WindowsRegistrationRequest) -> Option<PasskeyResponse> {
|
||||
util::message(&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()));
|
||||
|
||||
// Extract user ID from context - this is required for registration
|
||||
let user_id = match &context.user_id {
|
||||
Some(id) if !id.is_empty() => id.clone(),
|
||||
_ => {
|
||||
util::message("ERROR: User ID is required for registration but not provided");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// Extract user name from context - this is required for registration
|
||||
let user_name = match &context.user_name {
|
||||
Some(name) if !name.is_empty() => name.clone(),
|
||||
_ => {
|
||||
util::message("ERROR: User name is required for registration but not provided");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// Extract client data hash from context - this is required for WebAuthn
|
||||
let client_data_hash = match &context.client_data_hash {
|
||||
Some(hash) if !hash.is_empty() => hash.clone(),
|
||||
_ => {
|
||||
util::message("ERROR: Client data hash is required for registration but not provided");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
util::message(&format!("Registration request data - RP ID: {}, User ID: {} bytes, User name: {}, Client data hash: {} bytes, Algorithms: {:?}",
|
||||
rpid, user_id.len(), user_name, client_data_hash.len(), context.supported_algorithms));
|
||||
|
||||
let request = PasskeyRegistrationRequest {
|
||||
rp_id: rpid.to_string(),
|
||||
let passkey_request = PasskeyRegistrationRequest {
|
||||
rp_id: request.rpid.clone(),
|
||||
transaction_id: transaction_id.to_string(),
|
||||
user_id,
|
||||
user_name,
|
||||
client_data_hash,
|
||||
user_verification: context.user_verification.unwrap_or_default(),
|
||||
supported_algorithms: context.supported_algorithms.clone(),
|
||||
user_id: request.user_id.clone(),
|
||||
user_name: request.user_name.clone(),
|
||||
client_data_hash: request.client_data_hash.clone(),
|
||||
user_verification: request.user_verification.clone(),
|
||||
supported_algorithms: request.supported_algorithms.clone(),
|
||||
excluded_credentials: request.excluded_credentials.clone(),
|
||||
};
|
||||
|
||||
match serde_json::to_string(&request) {
|
||||
match serde_json::to_string(&passkey_request) {
|
||||
Ok(request_json) => {
|
||||
util::message(&format!("Sending registration request: {}", request_json));
|
||||
crate::ipc::send_passkey_request(RequestType::Registration, request_json, rpid)
|
||||
crate::ipc::send_passkey_request(RequestType::Registration, request_json, &request.rpid)
|
||||
},
|
||||
Err(e) => {
|
||||
util::message(&format!("ERROR: Failed to serialize registration request: {}", e));
|
||||
|
||||
@@ -58,6 +58,7 @@ pub struct PasskeyRegistrationRequest {
|
||||
pub client_data_hash: Vec<u8>,
|
||||
pub user_verification: UserVerificationRequirement,
|
||||
pub supported_algorithms: Vec<i32>, // COSE algorithm identifiers
|
||||
pub excluded_credentials: Vec<Vec<u8>>, // Credentials to exclude from creation
|
||||
}
|
||||
|
||||
/// Sync request structure
|
||||
|
||||
Reference in New Issue
Block a user