mirror of
https://github.com/bitwarden/browser
synced 2026-02-21 11:54:02 +00:00
Switch from NCrypt to BCrypt API
The initial implementation was based on the Window sample, which uses NCrypt for RSA keys. However, we were not using NCrypt for key storage (just using ephemeral key handles), and we needed to specify the curves for the ECDSA algorithms anyway. Because of that, we did not have any advantage using NCrypt, so this switches to BCrypt for a slightly simpler API and ECDSA support.
This commit is contained in:
@@ -5,13 +5,16 @@ use std::{
|
||||
};
|
||||
|
||||
use windows::{
|
||||
core::{GUID, HRESULT, PCWSTR},
|
||||
core::{GUID, HRESULT},
|
||||
Win32::Security::Cryptography::{
|
||||
BCryptCreateHash, BCryptDestroyHash, BCryptFinishHash, BCryptGetProperty, BCryptHashData,
|
||||
NCryptImportKey, NCryptOpenStorageProvider, NCryptVerifySignature, BCRYPT_HASH_HANDLE,
|
||||
BCRYPT_HASH_LENGTH, BCRYPT_KEY_BLOB, BCRYPT_OBJECT_LENGTH, BCRYPT_PKCS1_PADDING_INFO,
|
||||
BCRYPT_PUBLIC_KEY_BLOB, BCRYPT_RSAPUBLIC_MAGIC, BCRYPT_SHA256_ALGORITHM,
|
||||
BCRYPT_SHA256_ALG_HANDLE, NCRYPT_FLAGS, NCRYPT_PAD_PKCS1_FLAG,
|
||||
BCryptCreateHash, BCryptDestroyHash, BCryptDestroyKey, BCryptFinishHash, BCryptGetProperty,
|
||||
BCryptHashData, BCryptImportKeyPair, BCryptVerifySignature,
|
||||
BCRYPT_ECDSA_P256_ALG_HANDLE, BCRYPT_ECDSA_P384_ALG_HANDLE, BCRYPT_ECDSA_P521_ALG_HANDLE,
|
||||
BCRYPT_ECDSA_PUBLIC_P256_MAGIC, BCRYPT_ECDSA_PUBLIC_P384_MAGIC,
|
||||
BCRYPT_ECDSA_PUBLIC_P521_MAGIC, BCRYPT_FLAGS, BCRYPT_HASH_HANDLE, BCRYPT_HASH_LENGTH,
|
||||
BCRYPT_KEY_BLOB, BCRYPT_KEY_HANDLE, BCRYPT_OBJECT_LENGTH, BCRYPT_PAD_PKCS1,
|
||||
BCRYPT_PKCS1_PADDING_INFO, BCRYPT_PUBLIC_KEY_BLOB, BCRYPT_RSA_ALG_HANDLE,
|
||||
BCRYPT_RSAPUBLIC_MAGIC, BCRYPT_SHA256_ALGORITHM, BCRYPT_SHA256_ALG_HANDLE,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -134,27 +137,6 @@ fn verify_signature(
|
||||
) -> Result<(), windows::core::Error> {
|
||||
// Verify the signature over the hash of dataBuffer using the hKey
|
||||
unsafe {
|
||||
tracing::debug!("Getting provider");
|
||||
// Get the provider
|
||||
let mut provider = MaybeUninit::uninit();
|
||||
|
||||
NCryptOpenStorageProvider(provider.as_mut_ptr(), PCWSTR::null(), 0)?;
|
||||
let provider = provider.assume_init();
|
||||
|
||||
tracing::debug!("Getting key handle");
|
||||
// Create a NCrypt key handle from the public key
|
||||
let mut key_handle = MaybeUninit::uninit();
|
||||
NCryptImportKey(
|
||||
provider,
|
||||
None,
|
||||
BCRYPT_PUBLIC_KEY_BLOB,
|
||||
None,
|
||||
key_handle.as_mut_ptr(),
|
||||
public_key.as_ref(),
|
||||
NCRYPT_FLAGS(0),
|
||||
)?;
|
||||
let key_handle = key_handle.assume_init();
|
||||
|
||||
// BCRYPT_KEY_BLOB is a base structure for all types of keys used in the BCRYPT API.
|
||||
// Cf. https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_key_blob.
|
||||
//
|
||||
@@ -168,27 +150,51 @@ fn verify_signature(
|
||||
// RSA, P-256, P-384 and P-512.
|
||||
let key_blob = public_key.pbPublicKey.as_ref();
|
||||
tracing::debug!(" got key magic: {}", key_blob.Magic);
|
||||
let (padding_info, cng_flags) = if key_blob.Magic == BCRYPT_RSAPUBLIC_MAGIC.0 {
|
||||
let (alg_handle, padding_info, bcrypt_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)
|
||||
(BCRYPT_RSA_ALG_HANDLE, Some(padding_info), BCRYPT_PAD_PKCS1)
|
||||
} else if key_blob.Magic == BCRYPT_ECDSA_PUBLIC_P256_MAGIC {
|
||||
tracing::debug!("Detected ECDSA P-256 key");
|
||||
(BCRYPT_ECDSA_P256_ALG_HANDLE, None, BCRYPT_FLAGS(0))
|
||||
} else if key_blob.Magic == BCRYPT_ECDSA_PUBLIC_P384_MAGIC {
|
||||
tracing::debug!("Detected ECDSA P-384 key");
|
||||
(BCRYPT_ECDSA_P384_ALG_HANDLE, None, BCRYPT_FLAGS(0))
|
||||
} else if key_blob.Magic == BCRYPT_ECDSA_PUBLIC_P521_MAGIC {
|
||||
tracing::debug!("Detected ECDSA P-521 key");
|
||||
(BCRYPT_ECDSA_P521_ALG_HANDLE, None, BCRYPT_FLAGS(0))
|
||||
} else {
|
||||
tracing::debug!("Non-RSA key, no PKCS1 padding added");
|
||||
(None, NCRYPT_FLAGS(0))
|
||||
tracing::error!("Unsupported key type: magic={}", key_blob.Magic);
|
||||
// NTE_BAD_ALGID
|
||||
return Err(windows::core::Error::from_hresult(HRESULT(0x80090008u32 as i32)));
|
||||
};
|
||||
|
||||
tracing::debug!("Getting key handle");
|
||||
let mut key_handle = MaybeUninit::uninit();
|
||||
BCryptImportKeyPair(
|
||||
alg_handle,
|
||||
None,
|
||||
BCRYPT_PUBLIC_KEY_BLOB,
|
||||
key_handle.as_mut_ptr(),
|
||||
public_key.as_ref(),
|
||||
0,
|
||||
)
|
||||
.ok()?;
|
||||
let key_handle = BcryptKey(key_handle.assume_init());
|
||||
|
||||
tracing::debug!("Verifying signature");
|
||||
NCryptVerifySignature(
|
||||
key_handle,
|
||||
BCryptVerifySignature(
|
||||
key_handle.0,
|
||||
padding_info
|
||||
.as_ref()
|
||||
.map(|padding: &BCRYPT_PKCS1_PADDING_INFO| std::ptr::from_ref(padding).cast()),
|
||||
hash,
|
||||
signature,
|
||||
cng_flags,
|
||||
)?;
|
||||
bcrypt_flags,
|
||||
)
|
||||
.ok()?;
|
||||
tracing::debug!("Verified");
|
||||
Ok(())
|
||||
}
|
||||
@@ -304,6 +310,20 @@ impl Drop for BcryptHash {
|
||||
}
|
||||
}
|
||||
|
||||
struct BcryptKey(BCRYPT_KEY_HANDLE);
|
||||
|
||||
impl Drop for BcryptKey {
|
||||
fn drop(&mut self) {
|
||||
if !self.0.is_invalid() {
|
||||
unsafe {
|
||||
if let Err(err) = BCryptDestroyKey(self.0).to_hresult().ok() {
|
||||
tracing::error!("Failed to clean up key handle: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Public key for verifying a signature over an operation request or user verification response buffer.
|
||||
pub struct VerifyingKey {
|
||||
/// Length of buffer
|
||||
|
||||
Reference in New Issue
Block a user