1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-07 04:03:29 +00:00

squash use request hash

This commit is contained in:
Isaiah Inuwa
2025-12-05 10:25:47 -06:00
parent 54841014d6
commit 2a2f36ed97
6 changed files with 74 additions and 38 deletions

View File

@@ -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)
}

View File

@@ -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::<BCRYPT_KEY_BLOB>() {
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<Vec<u8>, 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::<u32>()];
@@ -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::<BCRYPT_KEY_BLOB>() {
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)
}
}

View File

@@ -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<u8>,
/// SHA-256 hash of the request.
pub request_hash: Vec<u8>,
}
impl PluginMakeCredentialRequest {
@@ -483,6 +486,17 @@ impl TryFrom<NonNull<WEBAUTHN_PLUGIN_OPERATION_REQUEST>> 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<NonNull<WEBAUTHN_PLUGIN_OPERATION_REQUEST>> 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<u8>,
pub request_hash: Vec<u8>,
}
impl PluginGetAssertionRequest {
@@ -839,6 +855,17 @@ impl TryFrom<NonNull<WEBAUTHN_PLUGIN_OPERATION_REQUEST>> 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<NonNull<WEBAUTHN_PLUGIN_OPERATION_REQUEST>> for PluginGetAssertionR
request.cbEncodedRequest as usize,
)
.to_vec(),
request_hash,
})
}
}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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<Vec<u8>, D::Error> {