From 2a2f36ed97accd3caf3b6ada50df2c111777593f Mon Sep 17 00:00:00 2001 From: Isaiah Inuwa Date: Fri, 5 Dec 2025 10:25:47 -0600 Subject: [PATCH] squash use request hash --- .../win_webauthn/src/plugin/com.rs | 5 +- .../win_webauthn/src/plugin/crypto.rs | 70 ++++++++++--------- .../win_webauthn/src/plugin/types.rs | 28 ++++++++ .../src/assert.rs | 2 +- .../src/make_credential.rs | 2 +- .../windows_plugin_authenticator/src/util.rs | 5 +- 6 files changed, 74 insertions(+), 38 deletions(-) diff --git a/apps/desktop/desktop_native/win_webauthn/src/plugin/com.rs b/apps/desktop/desktop_native/win_webauthn/src/plugin/com.rs index c5037d3d9bc..95ff453b147 100644 --- a/apps/desktop/desktop_native/win_webauthn/src/plugin/com.rs +++ b/apps/desktop/desktop_native/win_webauthn/src/plugin/com.rs @@ -436,6 +436,9 @@ unsafe fn verify_operation_request( tracing::debug!("Verifying request"); let request_data = std::slice::from_raw_parts(request.pbEncodedRequest, request.cbEncodedRequest as usize); + let request_hash = crypto::hash_sha256(request_data).map_err(|err| { + WinWebAuthnError::with_cause(ErrorKind::WindowsInternal, "failed to hash request", err) + })?; let signature = std::slice::from_raw_parts( request.pbRequestSignature, request.cbRequestSignature as usize, @@ -449,5 +452,5 @@ unsafe fn verify_operation_request( ) })?; tracing::debug!("Verifying signature"); - op_pub_key.verify_signature(request_data, signature) + op_pub_key.verify_signature(&request_hash, signature) } diff --git a/apps/desktop/desktop_native/win_webauthn/src/plugin/crypto.rs b/apps/desktop/desktop_native/win_webauthn/src/plugin/crypto.rs index da69a42e6b0..a62ddc9fa88 100644 --- a/apps/desktop/desktop_native/win_webauthn/src/plugin/crypto.rs +++ b/apps/desktop/desktop_native/win_webauthn/src/plugin/crypto.rs @@ -90,9 +90,10 @@ pub(super) fn get_user_verification_public_key( fn verify_signature( public_key: &SigningKey, - data: &[u8], + hash: &[u8], signature: &[u8], ) -> Result<(), windows::core::Error> { + // Verify the signature over the hash of dataBuffer using the hKey unsafe { tracing::debug!("Getting provider"); // Get the provider @@ -115,8 +116,41 @@ fn verify_signature( )?; let key_handle = key_handle.assume_init(); - // Verify the signature over the hash of dataBuffer using the hKey + tracing::debug!("Trying to read public key slice as key blob"); + let public_key = public_key.as_ref(); + if public_key.len() < size_of::() { + return Err(windows::core::Error::from_hresult(E_INVALIDARG)); + } + let key_blob: &BCRYPT_KEY_BLOB = &*public_key.as_ptr().cast(); + tracing::debug!(" got key magic: {}", key_blob.Magic); + let (padding_info, cng_flags) = if key_blob.Magic == BCRYPT_RSAPUBLIC_MAGIC.0 { + tracing::debug!("Detected RSA key, adding PKCS1 padding"); + let padding_info = BCRYPT_PKCS1_PADDING_INFO { + pszAlgId: BCRYPT_SHA256_ALGORITHM, + }; + (Some(padding_info), NCRYPT_PAD_PKCS1_FLAG) + } else { + tracing::debug!("Non-RSA key, no PKCS1 padding added"); + (None, NCRYPT_FLAGS(0)) + }; + tracing::debug!("Verifying signature"); + NCryptVerifySignature( + key_handle, + padding_info + .as_ref() + .map(|padding: &BCRYPT_PKCS1_PADDING_INFO| std::ptr::from_ref(padding).cast()), + hash, + signature, + cng_flags, + )?; + tracing::debug!("Verified"); + Ok(()) + } +} + +pub(super) fn hash_sha256(data: &[u8]) -> Result, windows::core::Error> { + unsafe { tracing::debug!("Getting length of hash buffer object"); // Get length of SHA-256 buffer let mut len_size_buf = [0; size_of::()]; @@ -174,37 +208,7 @@ fn verify_signature( hash_buffer.set_len(hash_output_len); BCryptFinishHash(hash_handle, hash_buffer.as_mut_slice(), 0).ok()?; tracing::debug!(" Hash: {hash_buffer:?}"); - - tracing::debug!("Trying to read public key slice as key blob"); - let public_key = public_key.as_ref(); - if public_key.len() < size_of::() { - return Err(windows::core::Error::from_hresult(E_INVALIDARG)); - } - let key_blob: &BCRYPT_KEY_BLOB = &*public_key.as_ptr().cast(); - tracing::debug!(" got key magic: {}", key_blob.Magic); - let (padding_info, cng_flags) = if key_blob.Magic == BCRYPT_RSAPUBLIC_MAGIC.0 { - tracing::debug!("Detected RSA key, adding PKCS1 padding"); - let padding_info = BCRYPT_PKCS1_PADDING_INFO { - pszAlgId: BCRYPT_SHA256_ALGORITHM, - }; - (Some(padding_info), NCRYPT_PAD_PKCS1_FLAG) - } else { - tracing::debug!("Non-RSA key, no PKCS1 padding added"); - (None, NCRYPT_FLAGS(0)) - }; - - tracing::debug!("Verifying signature"); - NCryptVerifySignature( - key_handle, - padding_info - .as_ref() - .map(|padding: &BCRYPT_PKCS1_PADDING_INFO| std::ptr::from_ref(padding).cast()), - hash_buffer.as_slice(), - signature, - cng_flags, - )?; - tracing::debug!("Verified"); - Ok(()) + Ok(hash_buffer) } } diff --git a/apps/desktop/desktop_native/win_webauthn/src/plugin/types.rs b/apps/desktop/desktop_native/win_webauthn/src/plugin/types.rs index 4ade8d22dff..252670cdc72 100644 --- a/apps/desktop/desktop_native/win_webauthn/src/plugin/types.rs +++ b/apps/desktop/desktop_native/win_webauthn/src/plugin/types.rs @@ -13,6 +13,7 @@ use windows::{ }; use crate::{ + plugin::crypto, types::UserId, util::{webauthn_call, WindowsString}, CredentialId, ErrorKind, WinWebAuthnError, @@ -391,6 +392,8 @@ pub struct PluginMakeCredentialRequest { pub window_handle: HWND, pub transaction_id: GUID, pub request_signature: Vec, + /// SHA-256 hash of the request. + pub request_hash: Vec, } impl PluginMakeCredentialRequest { @@ -483,6 +486,17 @@ impl TryFrom> for PluginMakeCredentia "Unknown plugin operation request type", )); } + let request_slice = std::slice::from_raw_parts( + request.pbEncodedRequest, + request.cbEncodedRequest as usize, + ); + let request_hash = crypto::hash_sha256(request_slice).map_err(|err| { + WinWebAuthnError::with_cause( + ErrorKind::WindowsInternal, + "failed to hash request", + err, + ) + })?; let mut registration_request = MaybeUninit::uninit(); webauthn_decode_make_credential_request( request.cbEncodedRequest, @@ -508,6 +522,7 @@ impl TryFrom> for PluginMakeCredentia request.cbEncodedRequest as usize, ) .to_vec(), + request_hash, }) } } @@ -775,6 +790,7 @@ pub struct PluginGetAssertionRequest { pub window_handle: HWND, pub transaction_id: GUID, pub request_signature: Vec, + pub request_hash: Vec, } impl PluginGetAssertionRequest { @@ -839,6 +855,17 @@ impl TryFrom> for PluginGetAssertionR "Unknown plugin operation request type", )); } + let request_slice = std::slice::from_raw_parts( + request.pbEncodedRequest, + request.cbEncodedRequest as usize, + ); + let request_hash = crypto::hash_sha256(request_slice).map_err(|err| { + WinWebAuthnError::with_cause( + ErrorKind::WindowsInternal, + "failed to hash request", + err, + ) + })?; let mut assertion_request: *mut WEBAUTHN_CTAPCBOR_GET_ASSERTION_REQUEST = std::ptr::null_mut(); webauthn_decode_get_assertion_request( @@ -863,6 +890,7 @@ impl TryFrom> for PluginGetAssertionR request.cbEncodedRequest as usize, ) .to_vec(), + request_hash, }) } } 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 32dcf625aca..497fef34436 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/assert.rs +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/src/assert.rs @@ -55,7 +55,7 @@ pub fn get_assertion( rp_id, allowed_credential_ids ); - let context = create_context_string(request.transaction_id); + let context = create_context_string(request.transaction_id, &request.request_hash); // Send assertion request let assertion_request = PasskeyAssertionRequest { 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 eb8cd3d767a..c5eead87817 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 @@ -92,7 +92,7 @@ pub fn make_credential( .center_position() .unwrap_or((640, 480)); - let context = create_context_string(request.transaction_id); + let context = create_context_string(request.transaction_id, &request.request_hash); // Create Windows registration request let registration_request = PasskeyRegistrationRequest { diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/src/util.rs b/apps/desktop/desktop_native/windows_plugin_authenticator/src/util.rs index 426e9ff783e..d563cb9baae 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/util.rs +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/src/util.rs @@ -49,8 +49,9 @@ impl HwndExt for HWND { } } -pub fn create_context_string(transaction_id: GUID) -> String { - STANDARD.encode(transaction_id.to_u128().to_le_bytes().to_vec()) +pub fn create_context_string(transaction_id: GUID, request_hash: &[u8]) -> String { + let context = [&transaction_id.to_u128().to_le_bytes(), request_hash].concat(); + STANDARD.encode(context) } pub fn deserialize_b64<'de, D: Deserializer<'de>>(deserializer: D) -> Result, D::Error> {