From 6d12ed13cc6df4587d77e75510b1a920a64ad370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20=C3=85berg?= Date: Thu, 10 Jul 2025 15:00:50 +0200 Subject: [PATCH] Moved impl into their own files --- .../src/assert.rs | 181 +++++++- .../src/com_provider.rs | 413 +----------------- .../src/make_credential.rs | 257 ++++++++++- 3 files changed, 433 insertions(+), 418 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 bc3a81b09f8..b4210f5feb6 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/assert.rs +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/src/assert.rs @@ -3,9 +3,12 @@ use std::alloc::{alloc, Layout}; use std::ptr; use windows_core::{s, HRESULT}; -use crate::com_provider::ExperimentalWebAuthnPluginOperationResponse; +use crate::com_provider::{ + parse_credential_list, ExperimentalWebAuthnPluginOperationRequest, + ExperimentalWebAuthnPluginOperationResponse, +}; use crate::types::*; -use crate::util::{debug_log, delay_load}; +use crate::util::{debug_log, delay_load, wstr_to_string}; use crate::webauthn::WEBAUTHN_CREDENTIAL_LIST; // Windows API types for WebAuthn (from webauthn.h.sample) @@ -73,7 +76,7 @@ impl Drop for DecodedGetAssertionRequest { } // Function to decode get assertion request using Windows API -pub unsafe fn decode_get_assertion_request( +unsafe fn decode_get_assertion_request( encoded_request: &[u8], ) -> Result { debug_log("Attempting to decode get assertion request using Windows API"); @@ -125,7 +128,7 @@ pub struct WindowsAssertionRequest { } /// Helper for assertion requests -pub fn send_assertion_request( +fn send_assertion_request( transaction_id: &str, request: &WindowsAssertionRequest, ) -> Option { @@ -161,7 +164,7 @@ pub fn send_assertion_request( } /// Creates a WebAuthn get assertion response from Bitwarden's assertion response -pub unsafe fn create_get_assertion_response( +unsafe fn create_get_assertion_response( credential_id: Vec, authenticator_data: Vec, signature: Vec, @@ -255,3 +258,171 @@ pub unsafe fn create_get_assertion_response( Ok(operation_response_ptr) } + +/// Implementation of EXPERIMENTAL_PluginGetAssertion moved from com_provider.rs +pub unsafe fn experimental_plugin_get_assertion( + request: *const ExperimentalWebAuthnPluginOperationRequest, + response: *mut *mut ExperimentalWebAuthnPluginOperationResponse, +) -> HRESULT { + debug_log("EXPERIMENTAL_PluginGetAssertion() called"); + + // Validate input parameters + if request.is_null() || response.is_null() { + debug_log("Invalid parameters passed to EXPERIMENTAL_PluginGetAssertion"); + return HRESULT(-1); + } + + let req = &*request; + let transaction_id = format!("{:?}", req.transaction_id); + + debug_log(&format!( + "Get assertion request - Transaction: {}", + transaction_id + )); + + if req.encoded_request_byte_count == 0 || req.encoded_request_pointer.is_null() { + debug_log("ERROR: No encoded request data provided"); + *response = ptr::null_mut(); + return HRESULT(-1); + } + + let encoded_request_slice = std::slice::from_raw_parts( + req.encoded_request_pointer, + req.encoded_request_byte_count as usize, + ); + + // 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"); + + // Extract RP information + let rpid = if decoded_request.pwszRpId.is_null() { + debug_log("ERROR: RP ID is null"); + *response = ptr::null_mut(); + 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)); + *response = ptr::null_mut(); + 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"); + *response = ptr::null_mut(); + 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.len() + )); + + // 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 = webauthn_response; + HRESULT(0) + } + Err(e) => { + debug_log(&format!( + "ERROR: Failed to create WebAuthn assertion response: {}", + e + )); + *response = ptr::null_mut(); + HRESULT(-1) + } + } + } + PasskeyResponse::Error { message } => { + debug_log(&format!("Assertion request failed: {}", message)); + *response = ptr::null_mut(); + HRESULT(-1) + } + _ => { + debug_log("ERROR: Unexpected response type for assertion request"); + *response = ptr::null_mut(); + HRESULT(-1) + } + } + } else { + debug_log("ERROR: No response from assertion request"); + *response = ptr::null_mut(); + HRESULT(-1) + } + } + Err(e) => { + debug_log(&format!( + "ERROR: Failed to decode get assertion request: {}", + e + )); + *response = ptr::null_mut(); + HRESULT(-1) + } + } +} 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 c5189f30828..cb48a7397b9 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 @@ -2,16 +2,9 @@ use std::ptr; use windows::Win32::System::Com::*; use windows_core::{implement, interface, IInspectable, IUnknown, Interface, HRESULT}; -use crate::assert::{ - create_get_assertion_response, decode_get_assertion_request, send_assertion_request, - WindowsAssertionRequest, -}; -use crate::make_credential::{ - create_make_credential_response, decode_make_credential_request, send_registration_request, - WindowsRegistrationRequest, -}; -use crate::types::*; -use crate::util::{debug_log, wstr_to_string}; +use crate::assert::experimental_plugin_get_assertion; +use crate::make_credential::experimental_plugin_make_credential; +use crate::util::debug_log; use crate::webauthn::WEBAUTHN_CREDENTIAL_LIST; /// Used when creating and asserting credentials. @@ -134,243 +127,7 @@ impl EXPERIMENTAL_IPluginAuthenticator_Impl for PluginAuthenticatorComObject_Imp request: *const ExperimentalWebAuthnPluginOperationRequest, response: *mut *mut ExperimentalWebAuthnPluginOperationResponse, ) -> HRESULT { - debug_log("=== EXPERIMENTAL_PluginMakeCredential() called ==="); - - if request.is_null() { - debug_log("ERROR: NULL request pointer"); - return HRESULT(-1); - } - - if response.is_null() { - debug_log("ERROR: NULL response pointer"); - return HRESULT(-1); - } - - let req = &*request; - 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"); - return HRESULT(-1); - } - - let encoded_request_slice = std::slice::from_raw_parts( - req.encoded_request_pointer, - req.encoded_request_byte_count as usize, - ); - - debug_log(&format!( - "Encoded request: {} bytes", - encoded_request_slice.len() - )); - - // Try to decode the request using Windows API - match decode_make_credential_request(encoded_request_slice) { - Ok(decoded_wrapper) => { - let decoded_request = decoded_wrapper.as_ref(); - debug_log("Successfully decoded make credential request using Windows API"); - - // Extract RP information - if decoded_request.pRpInformation.is_null() { - debug_log("ERROR: RP information is null"); - return HRESULT(-1); - } - - let rp_info = &*decoded_request.pRpInformation; - - let rpid = if rp_info.pwszId.is_null() { - debug_log("ERROR: RP ID is null"); - return 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)); - return HRESULT(-1); - } - } - }; - - // let rp_name = if rp_info.pwszName.is_null() { - // String::new() - // } else { - // wstr_to_string(rp_info.pwszName).unwrap_or_default() - // }; - - // Extract user information - if decoded_request.pUserInformation.is_null() { - debug_log("ERROR: User information is null"); - return 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"); - return HRESULT(-1); - } else { - let id_slice = std::slice::from_raw_parts(user.pbId, user.cbId as usize); - id_slice.to_vec() - }; - - let user_name = if user.pwszName.is_null() { - debug_log("ERROR: User name is required for registration"); - return HRESULT(-1); - } else { - match wstr_to_string(user.pwszName) { - Ok(name) => name, - Err(_) => { - debug_log("ERROR: Failed to decode user name"); - return HRESULT(-1); - } - } - }; - - let user_display_name = if user.pwszDisplayName.is_null() { - None - } else { - wstr_to_string(user.pwszDisplayName).ok() - }; - - let user_info = (user_id, user_name, user_display_name); - - // 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"); - 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 supported algorithms - let supported_algorithms = if decoded_request - .WebAuthNCredentialParameters - .cCredentialParameters - > 0 - && !decoded_request - .WebAuthNCredentialParameters - .pCredentialParameters - .is_null() - { - let params_count = decoded_request - .WebAuthNCredentialParameters - .cCredentialParameters as usize; - let params_ptr = decoded_request - .WebAuthNCredentialParameters - .pCredentialParameters; - - (0..params_count) - .map(|i| unsafe { &*params_ptr.add(i) }.lAlg) - .collect() - } else { - Vec::new() - }; - - // 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 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() { - debug_log(&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, - }; - - debug_log(&format!( - "Make credential request - RP: {}, User: {}", - rpid, registration_request.user_name - )); - - // Send registration request - if let Some(passkey_response) = - send_registration_request(&transaction_id, ®istration_request) - { - debug_log(&format!( - "Registration response received: {:?}", - passkey_response - )); - - // Create proper WebAuthn response from passkey_response - match passkey_response { - PasskeyResponse::RegistrationResponse { - credential_id: _, - attestation_object, - rp_id: _, - client_data_hash: _, - } => { - debug_log("Creating WebAuthn make credential response"); - - match create_make_credential_response(attestation_object) { - Ok(webauthn_response) => { - debug_log("Successfully created WebAuthn response"); - *response = webauthn_response; - HRESULT(0) - } - Err(e) => { - debug_log(&format!( - "ERROR: Failed to create WebAuthn response: {}", - e - )); - *response = ptr::null_mut(); - HRESULT(-1) - } - } - } - PasskeyResponse::Error { message } => { - debug_log(&format!("Registration request failed: {}", message)); - *response = ptr::null_mut(); - HRESULT(-1) - } - _ => { - debug_log("ERROR: Unexpected response type for registration request"); - *response = ptr::null_mut(); - HRESULT(-1) - } - } - } else { - debug_log("ERROR: No response from registration request"); - *response = ptr::null_mut(); - HRESULT(-1) - } - } - Err(e) => { - debug_log(&format!( - "ERROR: Failed to decode make credential request: {}", - e - )); - *response = ptr::null_mut(); - HRESULT(-1) - } - } + experimental_plugin_make_credential(request, response) } unsafe fn EXPERIMENTAL_PluginGetAssertion( @@ -378,167 +135,7 @@ impl EXPERIMENTAL_IPluginAuthenticator_Impl for PluginAuthenticatorComObject_Imp request: *const ExperimentalWebAuthnPluginOperationRequest, response: *mut *mut ExperimentalWebAuthnPluginOperationResponse, ) -> HRESULT { - debug_log("EXPERIMENTAL_PluginGetAssertion() called"); - - // Validate input parameters - if request.is_null() || response.is_null() { - debug_log("Invalid parameters passed to EXPERIMENTAL_PluginGetAssertion"); - return HRESULT(-1); - } - - let req = &*request; - let transaction_id = format!("{:?}", req.transaction_id); - - debug_log(&format!( - "Get assertion request - Transaction: {}", - transaction_id - )); - - if req.encoded_request_byte_count == 0 || req.encoded_request_pointer.is_null() { - debug_log("ERROR: No encoded request data provided"); - *response = ptr::null_mut(); - return HRESULT(-1); - } - - let encoded_request_slice = std::slice::from_raw_parts( - req.encoded_request_pointer, - req.encoded_request_byte_count as usize, - ); - - // 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"); - - // Extract RP information - let rpid = if decoded_request.pwszRpId.is_null() { - debug_log("ERROR: RP ID is null"); - *response = ptr::null_mut(); - 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)); - *response = ptr::null_mut(); - 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"); - *response = ptr::null_mut(); - 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.len() - )); - - // 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 = webauthn_response; - HRESULT(0) - } - Err(e) => { - debug_log(&format!( - "ERROR: Failed to create WebAuthn assertion response: {}", - e - )); - *response = ptr::null_mut(); - HRESULT(-1) - } - } - } - PasskeyResponse::Error { message } => { - debug_log(&format!("Assertion request failed: {}", message)); - *response = ptr::null_mut(); - HRESULT(-1) - } - _ => { - debug_log("ERROR: Unexpected response type for assertion request"); - *response = ptr::null_mut(); - HRESULT(-1) - } - } - } else { - debug_log("ERROR: No response from assertion request"); - *response = ptr::null_mut(); - HRESULT(-1) - } - } - Err(e) => { - debug_log(&format!( - "ERROR: Failed to decode get assertion request: {}", - e - )); - *response = ptr::null_mut(); - HRESULT(-1) - } - } + experimental_plugin_get_assertion(request, response) } unsafe fn EXPERIMENTAL_PluginCancelOperation( 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 ca64e4b3315..fa0a027c47f 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 @@ -3,9 +3,12 @@ use std::alloc::{alloc, Layout}; use std::ptr; use windows_core::{s, HRESULT}; -use crate::com_provider::ExperimentalWebAuthnPluginOperationResponse; +use crate::com_provider::{ + parse_credential_list, ExperimentalWebAuthnPluginOperationRequest, + ExperimentalWebAuthnPluginOperationResponse, +}; use crate::types::*; -use crate::util::{debug_log, delay_load}; +use crate::util::{debug_log, delay_load, wstr_to_string}; use crate::webauthn::WEBAUTHN_CREDENTIAL_LIST; /// Windows WebAuthn registration request context @@ -124,7 +127,7 @@ impl Drop for DecodedMakeCredentialRequest { } // Function to decode make credential request using Windows API -pub unsafe fn decode_make_credential_request( +unsafe fn decode_make_credential_request( encoded_request: &[u8], ) -> Result { debug_log("Attempting to decode make credential request using Windows API"); @@ -182,7 +185,7 @@ pub unsafe fn decode_make_credential_request( } /// Helper for registration requests -pub fn send_registration_request( +fn send_registration_request( transaction_id: &str, request: &WindowsRegistrationRequest, ) -> Option { @@ -217,7 +220,7 @@ pub fn send_registration_request( } /// Creates a WebAuthn make credential response from Bitwarden's registration response -pub unsafe fn create_make_credential_response( +unsafe fn create_make_credential_response( attestation_object: Vec, ) -> std::result::Result<*mut ExperimentalWebAuthnPluginOperationResponse, HRESULT> { // Use the attestation object directly as the encoded response @@ -253,3 +256,247 @@ pub unsafe fn create_make_credential_response( Ok(operation_response_ptr) } + +/// Implementation of EXPERIMENTAL_PluginMakeCredential moved from com_provider.rs +pub unsafe fn experimental_plugin_make_credential( + request: *const ExperimentalWebAuthnPluginOperationRequest, + response: *mut *mut ExperimentalWebAuthnPluginOperationResponse, +) -> HRESULT { + debug_log("=== EXPERIMENTAL_PluginMakeCredential() called ==="); + + if request.is_null() { + debug_log("ERROR: NULL request pointer"); + return HRESULT(-1); + } + + if response.is_null() { + debug_log("ERROR: NULL response pointer"); + return HRESULT(-1); + } + + let req = &*request; + 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"); + return HRESULT(-1); + } + + let encoded_request_slice = std::slice::from_raw_parts( + req.encoded_request_pointer, + req.encoded_request_byte_count as usize, + ); + + debug_log(&format!( + "Encoded request: {} bytes", + encoded_request_slice.len() + )); + + // Try to decode the request using Windows API + match decode_make_credential_request(encoded_request_slice) { + Ok(decoded_wrapper) => { + let decoded_request = decoded_wrapper.as_ref(); + debug_log("Successfully decoded make credential request using Windows API"); + + // Extract RP information + if decoded_request.pRpInformation.is_null() { + debug_log("ERROR: RP information is null"); + return HRESULT(-1); + } + + let rp_info = &*decoded_request.pRpInformation; + + let rpid = if rp_info.pwszId.is_null() { + debug_log("ERROR: RP ID is null"); + return 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)); + return HRESULT(-1); + } + } + }; + + // let rp_name = if rp_info.pwszName.is_null() { + // String::new() + // } else { + // wstr_to_string(rp_info.pwszName).unwrap_or_default() + // }; + + // Extract user information + if decoded_request.pUserInformation.is_null() { + debug_log("ERROR: User information is null"); + return 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"); + return HRESULT(-1); + } else { + let id_slice = std::slice::from_raw_parts(user.pbId, user.cbId as usize); + id_slice.to_vec() + }; + + let user_name = if user.pwszName.is_null() { + debug_log("ERROR: User name is required for registration"); + return HRESULT(-1); + } else { + match wstr_to_string(user.pwszName) { + Ok(name) => name, + Err(_) => { + debug_log("ERROR: Failed to decode user name"); + return HRESULT(-1); + } + } + }; + + let user_display_name = if user.pwszDisplayName.is_null() { + None + } else { + wstr_to_string(user.pwszDisplayName).ok() + }; + + let user_info = (user_id, user_name, user_display_name); + + // 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"); + 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 supported algorithms + let supported_algorithms = if decoded_request + .WebAuthNCredentialParameters + .cCredentialParameters + > 0 + && !decoded_request + .WebAuthNCredentialParameters + .pCredentialParameters + .is_null() + { + let params_count = decoded_request + .WebAuthNCredentialParameters + .cCredentialParameters as usize; + let params_ptr = decoded_request + .WebAuthNCredentialParameters + .pCredentialParameters; + + (0..params_count) + .map(|i| unsafe { &*params_ptr.add(i) }.lAlg) + .collect() + } else { + Vec::new() + }; + + // 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 excluded credentials from credential list + let excluded_credentials = parse_credential_list(&decoded_request.CredentialList); + if !excluded_credentials.is_empty() { + debug_log(&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, + }; + + debug_log(&format!( + "Make credential request - RP: {}, User: {}", + rpid, registration_request.user_name + )); + + // Send registration request + if let Some(passkey_response) = + send_registration_request(&transaction_id, ®istration_request) + { + debug_log(&format!( + "Registration response received: {:?}", + passkey_response + )); + + // Create proper WebAuthn response from passkey_response + match passkey_response { + PasskeyResponse::RegistrationResponse { + credential_id: _, + attestation_object, + rp_id: _, + client_data_hash: _, + } => { + debug_log("Creating WebAuthn make credential response"); + + match create_make_credential_response(attestation_object) { + Ok(webauthn_response) => { + debug_log("Successfully created WebAuthn response"); + *response = webauthn_response; + HRESULT(0) + } + Err(e) => { + debug_log(&format!( + "ERROR: Failed to create WebAuthn response: {}", + e + )); + *response = ptr::null_mut(); + HRESULT(-1) + } + } + } + PasskeyResponse::Error { message } => { + debug_log(&format!("Registration request failed: {}", message)); + *response = ptr::null_mut(); + HRESULT(-1) + } + _ => { + debug_log("ERROR: Unexpected response type for registration request"); + *response = ptr::null_mut(); + HRESULT(-1) + } + } + } else { + debug_log("ERROR: No response from registration request"); + *response = ptr::null_mut(); + HRESULT(-1) + } + } + Err(e) => { + debug_log(&format!( + "ERROR: Failed to decode make credential request: {}", + e + )); + *response = ptr::null_mut(); + HRESULT(-1) + } + } +}