mirror of
https://github.com/bitwarden/browser
synced 2026-02-08 12:40:26 +00:00
Remove old make_credential implementation
This commit is contained in:
@@ -1,112 +0,0 @@
|
||||
use windows::Win32::Foundation::RECT;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetWindowRect;
|
||||
|
||||
use crate::webauthn::WEBAUTHN_CREDENTIAL_LIST;
|
||||
|
||||
/// Plugin request type enum as defined in the IDL
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum WebAuthnPluginRequestType {
|
||||
CTAP2_CBOR = 0x01,
|
||||
}
|
||||
|
||||
/*
|
||||
/// Plugin lock status enum as defined in the IDL
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum PluginLockStatus {
|
||||
PluginLocked = 0,
|
||||
PluginUnlocked = 1,
|
||||
}
|
||||
*/
|
||||
|
||||
/// Used when creating and asserting credentials.
|
||||
/// Header File Name: _WEBAUTHN_PLUGIN_OPERATION_REQUEST
|
||||
/// Header File Usage: MakeCredential()
|
||||
/// GetAssertion()
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct WebAuthnPluginOperationRequest {
|
||||
pub window_handle: windows::Win32::Foundation::HWND,
|
||||
pub transaction_id: windows::core::GUID,
|
||||
pub request_signature_byte_count: u32,
|
||||
pub request_signature_pointer: *mut u8,
|
||||
pub request_type: WebAuthnPluginRequestType,
|
||||
pub encoded_request_byte_count: u32,
|
||||
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()
|
||||
/// GetAssertion()
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct WebAuthnPluginOperationResponse {
|
||||
pub encoded_response_byte_count: u32,
|
||||
pub encoded_response_pointer: *mut u8,
|
||||
}
|
||||
|
||||
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() {
|
||||
tracing::debug!("No credentials in credential list");
|
||||
return allowed_credentials;
|
||||
}
|
||||
|
||||
tracing::debug!(
|
||||
"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() {
|
||||
tracing::debug!("WARNING: Credential {} is null, skipping", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
let credential = &*credential_ptr;
|
||||
|
||||
if credential.cbId == 0 || credential.pbId.is_null() {
|
||||
tracing::debug!("WARNING: Credential {} has invalid ID, skipping", i);
|
||||
continue;
|
||||
}
|
||||
// Extract credential ID bytes
|
||||
// For some reason, we're getting hex strings from Windows instead of bytes.
|
||||
let credential_id_slice =
|
||||
std::slice::from_raw_parts(credential.pbId, credential.cbId as usize);
|
||||
|
||||
tracing::debug!(
|
||||
"Parsed credential {}: {} bytes, {:?}",
|
||||
i,
|
||||
credential.cbId,
|
||||
&credential_id_slice,
|
||||
);
|
||||
allowed_credentials.push(credential_id_slice.to_vec());
|
||||
}
|
||||
|
||||
tracing::debug!(
|
||||
"Successfully parsed {} allowed credentials",
|
||||
allowed_credentials.len()
|
||||
);
|
||||
allowed_credentials
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
// New modular structure
|
||||
mod assert;
|
||||
mod com_buffer;
|
||||
mod com_provider;
|
||||
// mod com_provider;
|
||||
mod ipc2;
|
||||
mod make_credential;
|
||||
mod types;
|
||||
|
||||
@@ -1,21 +1,12 @@
|
||||
use serde_json;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use windows::core::{s, HRESULT};
|
||||
|
||||
use crate::com_provider::{
|
||||
parse_credential_list, WebAuthnPluginOperationRequest, WebAuthnPluginOperationResponse,
|
||||
};
|
||||
use crate::ipc2::{
|
||||
PasskeyRegistrationRequest, PasskeyRegistrationResponse, Position, TimedCallback,
|
||||
UserVerification, WindowsProviderClient,
|
||||
};
|
||||
use crate::util::{delay_load, wstr_to_string, WindowsString};
|
||||
use crate::webauthn::WEBAUTHN_CREDENTIAL_LIST;
|
||||
use crate::win_webauthn::{
|
||||
CtapTransport, ErrorKind, HwndExt, PluginMakeCredentialRequest, PluginMakeCredentialResponse,
|
||||
WinWebAuthnError,
|
||||
@@ -131,296 +122,6 @@ pub fn make_credential(
|
||||
Ok(webauthn_response)
|
||||
}
|
||||
|
||||
// Windows API types for WebAuthn (from webauthn.h.sample)
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct WEBAUTHN_RP_ENTITY_INFORMATION {
|
||||
pub dwVersion: u32,
|
||||
pub pwszId: *const u16, // PCWSTR
|
||||
pub pwszName: *const u16, // PCWSTR
|
||||
pub pwszIcon: *const u16, // PCWSTR
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct WEBAUTHN_USER_ENTITY_INFORMATION {
|
||||
pub dwVersion: u32,
|
||||
pub cbId: u32, // DWORD
|
||||
pub pbId: *const u8, // PBYTE
|
||||
pub pwszName: *const u16, // PCWSTR
|
||||
pub pwszIcon: *const u16, // PCWSTR
|
||||
pub pwszDisplayName: *const u16, // PCWSTR
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct WEBAUTHN_COSE_CREDENTIAL_PARAMETER {
|
||||
pub dwVersion: u32,
|
||||
pub pwszCredentialType: *const u16, // LPCWSTR
|
||||
pub lAlg: i32, // LONG - COSE algorithm identifier
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct WEBAUTHN_COSE_CREDENTIAL_PARAMETERS {
|
||||
pub cCredentialParameters: u32,
|
||||
pub pCredentialParameters: *const WEBAUTHN_COSE_CREDENTIAL_PARAMETER,
|
||||
}
|
||||
|
||||
// Make Credential Request structure (from sample header)
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST {
|
||||
pub dwVersion: u32,
|
||||
pub cbRpId: u32,
|
||||
pub pbRpId: *const u8,
|
||||
pub cbClientDataHash: u32,
|
||||
pub pbClientDataHash: *const u8,
|
||||
pub pRpInformation: *const WEBAUTHN_RP_ENTITY_INFORMATION,
|
||||
pub pUserInformation: *const WEBAUTHN_USER_ENTITY_INFORMATION,
|
||||
pub WebAuthNCredentialParameters: WEBAUTHN_COSE_CREDENTIAL_PARAMETERS, // Matches C++ sample
|
||||
pub CredentialList: WEBAUTHN_CREDENTIAL_LIST,
|
||||
pub cbCborExtensionsMap: u32,
|
||||
pub pbCborExtensionsMap: *const u8,
|
||||
pub pAuthenticatorOptions: *const crate::webauthn::WebAuthnCtapCborAuthenticatorOptions,
|
||||
// Add other fields as needed...
|
||||
}
|
||||
|
||||
struct WEBAUTHN_HMAC_SECRET_SALT {
|
||||
/// Size of pbFirst.
|
||||
cbFirst: u32,
|
||||
// _Field_size_bytes_(cbFirst)
|
||||
/// Required
|
||||
pbFirst: *mut u8,
|
||||
|
||||
/// Size of pbSecond.
|
||||
cbSecond: u32,
|
||||
// _Field_size_bytes_(cbSecond)
|
||||
pbSecond: *mut u8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct WEBAUTHN_EXTENSION {
|
||||
pwszExtensionIdentifier: *const u16,
|
||||
cbExtension: u32,
|
||||
pvExtension: *mut u8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct WEBAUTHN_EXTENSIONS {
|
||||
cExtensions: u32,
|
||||
// _Field_size_(cExtensions)
|
||||
pExtensions: *mut WEBAUTHN_EXTENSION,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct WEBAUTHN_CREDENTIAL_ATTESTATION {
|
||||
/// Version of this structure, to allow for modifications in the future.
|
||||
dwVersion: u32,
|
||||
|
||||
/// Attestation format type
|
||||
pwszFormatType: *const u16, // PCWSTR
|
||||
|
||||
/// Size of cbAuthenticatorData.
|
||||
cbAuthenticatorData: u32,
|
||||
/// Authenticator data that was created for this credential.
|
||||
//_Field_size_bytes_(cbAuthenticatorData)
|
||||
pbAuthenticatorData: *mut u8,
|
||||
|
||||
/// Size of CBOR encoded attestation information
|
||||
/// 0 => encoded as CBOR null value.
|
||||
cbAttestation: u32,
|
||||
///Encoded CBOR attestation information
|
||||
// _Field_size_bytes_(cbAttestation)
|
||||
pbAttestation: *mut u8,
|
||||
|
||||
dwAttestationDecodeType: u32,
|
||||
/// Following depends on the dwAttestationDecodeType
|
||||
/// WEBAUTHN_ATTESTATION_DECODE_NONE
|
||||
/// NULL - not able to decode the CBOR attestation information
|
||||
/// WEBAUTHN_ATTESTATION_DECODE_COMMON
|
||||
/// PWEBAUTHN_COMMON_ATTESTATION;
|
||||
pvAttestationDecode: *mut u8,
|
||||
|
||||
/// The CBOR encoded Attestation Object to be returned to the RP.
|
||||
cbAttestationObject: u32,
|
||||
// _Field_size_bytes_(cbAttestationObject)
|
||||
pbAttestationObject: *mut u8,
|
||||
|
||||
/// The CredentialId bytes extracted from the Authenticator Data.
|
||||
/// Used by Edge to return to the RP.
|
||||
cbCredentialId: u32,
|
||||
// _Field_size_bytes_(cbCredentialId)
|
||||
pbCredentialId: *mut u8,
|
||||
|
||||
//
|
||||
// Following fields have been added in WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_2
|
||||
//
|
||||
/// Since VERSION 2
|
||||
Extensions: WEBAUTHN_EXTENSIONS,
|
||||
|
||||
//
|
||||
// Following fields have been added in WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_3
|
||||
//
|
||||
/// One of the WEBAUTHN_CTAP_TRANSPORT_* bits will be set corresponding to
|
||||
/// the transport that was used.
|
||||
dwUsedTransport: u32,
|
||||
|
||||
//
|
||||
// Following fields have been added in WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_4
|
||||
//
|
||||
bEpAtt: bool,
|
||||
bLargeBlobSupported: bool,
|
||||
bResidentKey: bool,
|
||||
|
||||
//
|
||||
// Following fields have been added in WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_5
|
||||
//
|
||||
bPrfEnabled: bool,
|
||||
|
||||
//
|
||||
// Following fields have been added in WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_6
|
||||
//
|
||||
cbUnsignedExtensionOutputs: u32,
|
||||
// _Field_size_bytes_(cbUnsignedExtensionOutputs)
|
||||
pbUnsignedExtensionOutputs: *mut u8,
|
||||
|
||||
//
|
||||
// Following fields have been added in WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_7
|
||||
//
|
||||
pHmacSecret: *const WEBAUTHN_HMAC_SECRET_SALT,
|
||||
|
||||
// ThirdPartyPayment Credential or not.
|
||||
bThirdPartyPayment: bool,
|
||||
|
||||
//
|
||||
// Following fields have been added in WEBAUTHN_CREDENTIAL_ATTESTATION_VERSION_8
|
||||
//
|
||||
|
||||
// Multiple WEBAUTHN_CTAP_TRANSPORT_* bits will be set corresponding to
|
||||
// the transports that are supported.
|
||||
dwTransports: u32,
|
||||
|
||||
// UTF-8 encoded JSON serialization of the client data.
|
||||
cbClientDataJSON: u32,
|
||||
// _Field_size_bytes_(cbClientDataJSON)
|
||||
pbClientDataJSON: *mut u8,
|
||||
|
||||
// UTF-8 encoded JSON serialization of the RegistrationResponse.
|
||||
cbRegistrationResponseJSON: u32,
|
||||
// _Field_size_bytes_(cbRegistrationResponseJSON)
|
||||
pbRegistrationResponseJSON: *mut u8,
|
||||
}
|
||||
|
||||
// Windows API function signatures
|
||||
type WebAuthNDecodeMakeCredentialRequestFn = unsafe extern "stdcall" fn(
|
||||
cbEncoded: u32,
|
||||
pbEncoded: *const u8,
|
||||
ppMakeCredentialRequest: *mut *mut WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST,
|
||||
) -> HRESULT;
|
||||
|
||||
type WebAuthNFreeDecodedMakeCredentialRequestFn = unsafe extern "stdcall" fn(
|
||||
pMakeCredentialRequest: *mut WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST,
|
||||
);
|
||||
|
||||
type WebAuthNEncodeMakeCredentialResponseFn = unsafe extern "stdcall" fn(
|
||||
cbEncoded: *const WEBAUTHN_CREDENTIAL_ATTESTATION,
|
||||
pbEncoded: *mut u32,
|
||||
response_bytes: *mut *mut u8,
|
||||
) -> HRESULT;
|
||||
|
||||
// RAII wrapper for decoded make credential request
|
||||
pub struct DecodedMakeCredentialRequest {
|
||||
ptr: *const WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST,
|
||||
free_fn: Option<WebAuthNFreeDecodedMakeCredentialRequestFn>,
|
||||
}
|
||||
|
||||
impl DecodedMakeCredentialRequest {
|
||||
fn new(
|
||||
ptr: *const WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST,
|
||||
free_fn: Option<WebAuthNFreeDecodedMakeCredentialRequestFn>,
|
||||
) -> Self {
|
||||
Self { ptr, free_fn }
|
||||
}
|
||||
|
||||
pub fn as_ref(&self) -> &WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST {
|
||||
unsafe { &*self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DecodedMakeCredentialRequest {
|
||||
fn drop(&mut self) {
|
||||
if !self.ptr.is_null() {
|
||||
if let Some(free_fn) = self.free_fn {
|
||||
tracing::debug!("Freeing decoded make credential request");
|
||||
unsafe {
|
||||
free_fn(self.ptr as *mut WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to decode make credential request using Windows API
|
||||
unsafe fn decode_make_credential_request(
|
||||
encoded_request: &[u8],
|
||||
) -> Result<DecodedMakeCredentialRequest, String> {
|
||||
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>(
|
||||
s!("webauthn.dll"),
|
||||
s!("WebAuthNDecodeMakeCredentialRequest"),
|
||||
) {
|
||||
Some(func) => func,
|
||||
None => {
|
||||
return Err(
|
||||
"Failed to load WebAuthNDecodeMakeCredentialRequest from webauthn.dll".to_string(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Try to load the free function (optional, might not be available in all versions)
|
||||
let free_fn = delay_load::<WebAuthNFreeDecodedMakeCredentialRequestFn>(
|
||||
s!("webauthn.dll"),
|
||||
s!("WebAuthNFreeDecodedMakeCredentialRequest"),
|
||||
);
|
||||
|
||||
// Prepare parameters for the API call
|
||||
let cb_encoded = encoded_request.len() as u32;
|
||||
let pb_encoded = encoded_request.as_ptr();
|
||||
let mut make_credential_request: *mut WEBAUTHN_CTAPCBOR_MAKE_CREDENTIAL_REQUEST =
|
||||
std::ptr::null_mut();
|
||||
|
||||
// Call the Windows API function
|
||||
let result = decode_fn(cb_encoded, pb_encoded, &mut make_credential_request);
|
||||
|
||||
// Check if the call succeeded (following C++ THROW_IF_FAILED pattern)
|
||||
if result.is_err() {
|
||||
tracing::debug!(
|
||||
"ERROR: WebAuthNDecodeMakeCredentialRequest failed with HRESULT: 0x{:08x}",
|
||||
result.0
|
||||
);
|
||||
return Err(format!(
|
||||
"Windows API call failed with HRESULT: 0x{:08x}",
|
||||
result.0
|
||||
));
|
||||
}
|
||||
|
||||
if make_credential_request.is_null() {
|
||||
tracing::error!("Windows API succeeded but returned null pointer");
|
||||
return Err("Windows API returned null pointer".to_string());
|
||||
}
|
||||
|
||||
Ok(DecodedMakeCredentialRequest::new(
|
||||
make_credential_request,
|
||||
free_fn,
|
||||
))
|
||||
}
|
||||
|
||||
/// Helper for registration requests
|
||||
fn send_registration_request(
|
||||
ipc_client: &WindowsProviderClient,
|
||||
@@ -502,331 +203,21 @@ fn create_make_credential_response(
|
||||
client_data_json: None,
|
||||
registration_response_json: None,
|
||||
};
|
||||
/*
|
||||
{
|
||||
dwVersion: 8,
|
||||
pwszFormatType: att_fmt.as_ptr(),
|
||||
cbAuthenticatorData: authenticator_data.len() as u32,
|
||||
pbAuthenticatorData: authenticator_data.as_ptr() as *mut u8,
|
||||
cbAttestation: 0,
|
||||
pbAttestation: ptr::null_mut(),
|
||||
dwAttestationDecodeType: 0,
|
||||
pvAttestationDecode: ptr::null_mut(),
|
||||
cbAttestationObject: 0,
|
||||
pbAttestationObject: ptr::null_mut(),
|
||||
cbCredentialId: 0,
|
||||
pbCredentialId: ptr::null_mut(),
|
||||
Extensions: WEBAUTHN_EXTENSIONS {
|
||||
cExtensions: 0,
|
||||
pExtensions: ptr::null_mut(),
|
||||
},
|
||||
dwUsedTransport: 0x00000010, // INTERNAL
|
||||
bEpAtt: false,
|
||||
bLargeBlobSupported: false,
|
||||
bResidentKey: false,
|
||||
bPrfEnabled: false,
|
||||
cbUnsignedExtensionOutputs: 0,
|
||||
pbUnsignedExtensionOutputs: ptr::null_mut(),
|
||||
pHmacSecret: ptr::null_mut(),
|
||||
bThirdPartyPayment: false,
|
||||
dwTransports: 0x00000030, // INTERNAL, HYBRID
|
||||
cbClientDataJSON: 0,
|
||||
pbClientDataJSON: ptr::null_mut(),
|
||||
cbRegistrationResponseJSON: 0,
|
||||
pbRegistrationResponseJSON: ptr::null_mut(),
|
||||
};
|
||||
*/
|
||||
attestation.to_ctap_response()
|
||||
}
|
||||
|
||||
/// Implementation of PluginMakeCredential moved from com_provider.rs
|
||||
pub unsafe fn plugin_make_credential(
|
||||
ipc_client: &WindowsProviderClient,
|
||||
request: *const WebAuthnPluginOperationRequest,
|
||||
response: *mut WebAuthnPluginOperationResponse,
|
||||
) -> Result<(), HRESULT> {
|
||||
tracing::debug!("=== PluginMakeCredential() called ===");
|
||||
|
||||
if request.is_null() {
|
||||
tracing::error!("NULL request pointer");
|
||||
return Err(HRESULT(-1));
|
||||
}
|
||||
|
||||
if response.is_null() {
|
||||
tracing::error!("NULL response pointer");
|
||||
return Err(HRESULT(-1));
|
||||
}
|
||||
|
||||
let req = &*request;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
let encoded_request_slice = std::slice::from_raw_parts(
|
||||
req.encoded_request_pointer,
|
||||
req.encoded_request_byte_count as usize,
|
||||
);
|
||||
|
||||
tracing::debug!("Encoded request: {} bytes", encoded_request_slice.len());
|
||||
|
||||
// Try to decode the request using Windows API
|
||||
let decoded_wrapper = decode_make_credential_request(encoded_request_slice).map_err(|err| {
|
||||
tracing::debug!("ERROR: Failed to decode make credential request: {err}");
|
||||
HRESULT(-1)
|
||||
})?;
|
||||
let decoded_request = decoded_wrapper.as_ref();
|
||||
tracing::debug!("Successfully decoded make credential request using Windows API");
|
||||
|
||||
// Extract RP information
|
||||
if decoded_request.pRpInformation.is_null() {
|
||||
tracing::error!("RP information is null");
|
||||
return Err(HRESULT(-1));
|
||||
}
|
||||
|
||||
let rp_info = &*decoded_request.pRpInformation;
|
||||
|
||||
let rpid = if rp_info.pwszId.is_null() {
|
||||
tracing::error!("RP ID is null");
|
||||
return Err(HRESULT(-1));
|
||||
} else {
|
||||
match wstr_to_string(rp_info.pwszId) {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to decode RP ID: {}", e);
|
||||
return Err(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() {
|
||||
tracing::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 {
|
||||
tracing::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);
|
||||
id_slice.to_vec()
|
||||
};
|
||||
|
||||
let user_name = if user.pwszName.is_null() {
|
||||
tracing::error!("User name is required for registration");
|
||||
return Err(HRESULT(-1));
|
||||
} else {
|
||||
match wstr_to_string(user.pwszName) {
|
||||
Ok(name) => name,
|
||||
Err(_) => {
|
||||
tracing::error!("Failed to decode user name");
|
||||
return Err(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() {
|
||||
tracing::error!("Client data hash is required for registration");
|
||||
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 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 => UserVerification::Required,
|
||||
-1 => UserVerification::Discouraged,
|
||||
0 | _ => UserVerification::Preferred, // Default or undefined
|
||||
}
|
||||
} else {
|
||||
UserVerification::Preferred // Default or undefined
|
||||
};
|
||||
|
||||
// Extract excluded credentials from credential list
|
||||
let excluded_credentials = parse_credential_list(&decoded_request.CredentialList);
|
||||
if !excluded_credentials.is_empty() {
|
||||
tracing::debug!(
|
||||
"Found {} excluded credentials for make credential",
|
||||
excluded_credentials.len()
|
||||
);
|
||||
}
|
||||
|
||||
let transaction_id = req.transaction_id.to_u128().to_le_bytes().to_vec();
|
||||
|
||||
// Create Windows registration request
|
||||
let registration_request = PasskeyRegistrationRequest {
|
||||
rp_id: rpid.clone(),
|
||||
user_handle: user_info.0,
|
||||
user_name: user_info.1,
|
||||
// user_display_name: user_info.2,
|
||||
client_data_hash,
|
||||
excluded_credentials,
|
||||
user_verification: user_verification,
|
||||
supported_algorithms,
|
||||
window_xy: Position {
|
||||
x: coords.0,
|
||||
y: coords.1,
|
||||
},
|
||||
context: transaction_id,
|
||||
};
|
||||
|
||||
tracing::debug!(
|
||||
"Make credential request - RP: {}, User: {}",
|
||||
rpid,
|
||||
registration_request.user_name
|
||||
);
|
||||
|
||||
// Send registration request
|
||||
let passkey_response =
|
||||
send_registration_request(ipc_client, registration_request).map_err(|err| {
|
||||
tracing::error!("Registration request failed: {err}");
|
||||
HRESULT(-1)
|
||||
})?;
|
||||
tracing::debug!("Registration response received: {:?}", passkey_response);
|
||||
|
||||
// Create proper WebAuthn response from passkey_response
|
||||
tracing::debug!("Creating WebAuthn make credential response");
|
||||
let mut webauthn_response =
|
||||
create_make_credential_response(passkey_response.attestation_object).map_err(|err| {
|
||||
tracing::error!("Failed to create WebAuthn response: {err}");
|
||||
HRESULT(-1)
|
||||
})?;
|
||||
tracing::debug!("Successfully created WebAuthn response: {webauthn_response:?}");
|
||||
(*response).encoded_response_byte_count = webauthn_response.len() as u32;
|
||||
(*response).encoded_response_pointer = webauthn_response.as_mut_ptr();
|
||||
tracing::debug!("Set pointer, returning HRESULT(0)");
|
||||
_ = ManuallyDrop::new(webauthn_response);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ptr;
|
||||
|
||||
use windows::core::s;
|
||||
|
||||
use crate::{
|
||||
make_credential::{
|
||||
create_make_credential_response, WebAuthNEncodeMakeCredentialResponseFn,
|
||||
WEBAUTHN_CREDENTIAL_ATTESTATION, WEBAUTHN_EXTENSIONS,
|
||||
},
|
||||
util::{delay_load, WindowsString},
|
||||
};
|
||||
use super::create_make_credential_response;
|
||||
#[test]
|
||||
fn test_encode_make_credential_custom() {
|
||||
let webauthn_att_obj = vec![
|
||||
163, 99, 102, 109, 116, 100, 110, 111, 110, 101, 103, 97, 116, 116, 83, 116, 109, 116,
|
||||
160, 104, 97, 117, 116, 104, 68, 97, 116, 97, 68, 1, 2, 3, 4,
|
||||
];
|
||||
let ctap_att_obj = unsafe { create_make_credential_response(webauthn_att_obj).unwrap() };
|
||||
let ctap_att_obj = create_make_credential_response(webauthn_att_obj).unwrap();
|
||||
println!("{ctap_att_obj:?}");
|
||||
let expected = vec![163, 1, 100, 110, 111, 110, 101, 2, 68, 1, 2, 3, 4, 3, 160];
|
||||
assert_eq!(expected, ctap_att_obj);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_make_credential() {
|
||||
let response = unsafe {
|
||||
let webauthn_encode_make_credential_response =
|
||||
delay_load::<WebAuthNEncodeMakeCredentialResponseFn>(
|
||||
s!("webauthn.dll"),
|
||||
s!("WebAuthNEncodeMakeCredentialResponse"),
|
||||
)
|
||||
.unwrap();
|
||||
let mut authenticator_data = vec![1, 2, 3, 4];
|
||||
let att_fmt = "none".to_utf16();
|
||||
let attestation = WEBAUTHN_CREDENTIAL_ATTESTATION {
|
||||
dwVersion: 8,
|
||||
pwszFormatType: att_fmt.as_ptr(),
|
||||
cbAuthenticatorData: authenticator_data.len() as u32,
|
||||
pbAuthenticatorData: authenticator_data.as_mut_ptr(),
|
||||
cbAttestation: 0,
|
||||
pbAttestation: ptr::null_mut(),
|
||||
dwAttestationDecodeType: 0,
|
||||
pvAttestationDecode: ptr::null_mut(),
|
||||
cbAttestationObject: 0,
|
||||
pbAttestationObject: ptr::null_mut(),
|
||||
cbCredentialId: 0,
|
||||
pbCredentialId: ptr::null_mut(),
|
||||
Extensions: WEBAUTHN_EXTENSIONS {
|
||||
cExtensions: 0,
|
||||
pExtensions: ptr::null_mut(),
|
||||
},
|
||||
dwUsedTransport: 0x00000010, // INTERNAL
|
||||
bEpAtt: false,
|
||||
bLargeBlobSupported: false,
|
||||
bResidentKey: false,
|
||||
bPrfEnabled: false,
|
||||
cbUnsignedExtensionOutputs: 0,
|
||||
pbUnsignedExtensionOutputs: ptr::null_mut(),
|
||||
pHmacSecret: ptr::null_mut(),
|
||||
bThirdPartyPayment: false,
|
||||
dwTransports: 0x00000030, // INTERNAL, HYBRID
|
||||
cbClientDataJSON: 0,
|
||||
pbClientDataJSON: ptr::null_mut(),
|
||||
cbRegistrationResponseJSON: 0,
|
||||
pbRegistrationResponseJSON: ptr::null_mut(),
|
||||
};
|
||||
let mut len = 0;
|
||||
let mut response_ptr = ptr::null_mut();
|
||||
let result =
|
||||
webauthn_encode_make_credential_response(&attestation, &mut len, &mut response_ptr);
|
||||
assert!(result.is_ok());
|
||||
Vec::from_raw_parts(response_ptr, len as usize, len as usize)
|
||||
};
|
||||
println!("{response:?}");
|
||||
assert_eq!(165, response[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ impl IPluginAuthenticator_Impl for PluginAuthenticatorComObject_Impl {
|
||||
tracing::debug!("MakeCredential called");
|
||||
if response.is_null() {
|
||||
tracing::warn!(
|
||||
"GetAssertion called with null response pointer from Windows. Aborting request."
|
||||
"MakeCredential called with null response pointer from Windows. Aborting request."
|
||||
);
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
@@ -115,7 +115,7 @@ impl IPluginAuthenticator_Impl for PluginAuthenticatorComObject_Impl {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
tracing::warn!(
|
||||
"GetAssertion called with null request pointer from Windows. Aborting request."
|
||||
"MakeCredential called with null request pointer from Windows. Aborting request."
|
||||
);
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user