#![cfg(target_os = "windows")]
use std::{
borrow::Cow,
ffi::c_void,
mem::{size_of, MaybeUninit},
ptr::{null, null_mut},
};
use widestring::{U16CStr, U16CString};
use windows_sys::Win32::Security::Cryptography::{
NCryptCreatePersistedKey, NCryptDecrypt, NCryptDeleteKey, NCryptEncrypt, NCryptEnumKeys,
NCryptExportKey, NCryptFinalizeKey, NCryptFreeBuffer, NCryptFreeObject, NCryptGetProperty,
NCryptOpenKey, NCryptOpenStorageProvider, BCRYPT_RSAPUBLIC_BLOB, BCRYPT_RSA_ALGORITHM,
MS_KEY_STORAGE_PROVIDER, MS_PLATFORM_CRYPTO_PROVIDER, MS_SMART_CARD_KEY_STORAGE_PROVIDER,
NCRYPT_BLOCK_LENGTH_PROPERTY, NCRYPT_KEY_HANDLE, NCRYPT_PAD_PKCS1_FLAG, NCRYPT_PROV_HANDLE,
};
mod blob_export;
mod error;
pub use blob_export::RsaKeyBlob;
pub use error::*;
pub struct StorageProvider(NCRYPT_PROV_HANDLE);
#[must_use]
pub struct Key(NCRYPT_KEY_HANDLE);
#[must_use]
pub struct KeyBuilder(NCRYPT_KEY_HANDLE);
#[derive(Debug, Clone, Copy)]
pub enum ProviderName {
KeyStorage,
SmartCard,
PlatformCrypto,
}
#[derive(Debug, Clone, Copy)]
pub enum Algorithm {
Rsa,
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum ExportType {
RsaPublicKey,
}
pub struct KeyIterator<'a> {
provider: &'a StorageProvider,
enum_state: *mut c_void,
finished: bool,
}
pub struct KeyInfo<'a> {
pub name: String,
pub algorithm: String,
provider: &'a StorageProvider,
}
impl StorageProvider {
pub fn open(name: ProviderName) -> Result<Self, Error> {
let name = name.as_wide_string();
let mut provider = MaybeUninit::zeroed();
Error::from_status(unsafe {
NCryptOpenStorageProvider(provider.as_mut_ptr(), name.as_ptr(), 0)
})?;
Ok(Self(unsafe { provider.assume_init() }))
}
pub fn open_key(&self, name: &str) -> Result<Key, Error> {
let name = U16CString::from_str_truncate(name);
let mut key = MaybeUninit::zeroed();
Error::from_status(unsafe {
NCryptOpenKey(self.0, key.as_mut_ptr(), name.as_ptr(), 0, 0)
})?;
Ok(Key(unsafe { key.assume_init() }))
}
pub fn create_persisted_key(&self, name: &str, alg: Algorithm) -> Result<KeyBuilder, Error> {
let name = U16CString::from_str_truncate(name);
let alg = alg.as_wide_string();
let mut key = MaybeUninit::zeroed();
Error::from_status(unsafe {
NCryptCreatePersistedKey(self.0, key.as_mut_ptr(), alg.as_ptr(), name.as_ptr(), 0, 0)
})?;
Ok(KeyBuilder(unsafe { key.assume_init() }))
}
pub fn enum_keys(&self) -> KeyIterator {
KeyIterator {
provider: self,
enum_state: null_mut(),
finished: false,
}
}
}
impl Drop for StorageProvider {
fn drop(&mut self) {
unsafe {
NCryptFreeObject(self.0);
}
}
}
impl Key {
pub fn delete(self) {
unsafe {
NCryptDeleteKey(self.0, 0);
}
std::mem::forget(self)
}
pub fn export(&self, export_type: ExportType) -> Result<RsaKeyBlob, Error> {
let blob_type = export_type.as_wide_string();
let mut required_size = 0;
Error::from_status(unsafe {
NCryptExportKey(
self.0,
0,
blob_type.as_ptr(),
null_mut(),
null_mut(),
0,
&mut required_size,
0,
)
})?;
let mut output = vec![0_u8; required_size as _];
Error::from_status(unsafe {
NCryptExportKey(
self.0,
0,
blob_type.as_ptr(),
null_mut(),
output.as_mut_ptr(),
output.len() as u32,
&mut required_size,
0,
)
})?;
Ok(RsaKeyBlob::parse(&output)?)
}
pub fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
let mut required_size = 0;
Error::from_status(unsafe {
NCryptEncrypt(
self.0,
data.as_ptr(),
data.len() as u32,
null(),
null_mut(),
0,
&mut required_size,
NCRYPT_PAD_PKCS1_FLAG,
)
})?;
let mut output = vec![0_u8; required_size as _];
Error::from_status(unsafe {
NCryptEncrypt(
self.0,
data.as_ptr(),
data.len() as u32,
null(),
output.as_mut_ptr(),
output.len() as u32,
&mut required_size,
NCRYPT_PAD_PKCS1_FLAG,
)
})?;
output.resize(required_size as _, 0);
Ok(output)
}
pub fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
let mut required_size = 0;
Error::from_status(unsafe {
NCryptDecrypt(
self.0,
data.as_ptr(),
data.len() as u32,
null(),
null_mut(),
0,
&mut required_size,
NCRYPT_PAD_PKCS1_FLAG,
)
})?;
let mut output = vec![0_u8; required_size as _];
Error::from_status(unsafe {
NCryptDecrypt(
self.0,
data.as_ptr(),
data.len() as u32,
null(),
output.as_mut_ptr(),
output.len() as u32,
&mut required_size,
NCRYPT_PAD_PKCS1_FLAG,
)
})?;
output.resize(required_size as usize, 0);
Ok(output)
}
pub fn key_length(&self) -> Result<u32, Error> {
let mut out = 0_u32;
let mut required = 0;
Error::from_status(unsafe {
NCryptGetProperty(
self.0,
NCRYPT_BLOCK_LENGTH_PROPERTY,
&mut out as *mut u32 as *mut u8,
size_of::<u32>() as u32,
&mut required,
0,
)
})?;
Ok(out)
}
}
impl Drop for Key {
fn drop(&mut self) {
unsafe {
NCryptFreeObject(self.0);
}
}
}
impl KeyBuilder {
pub fn finalize(self) -> Result<Key, Error> {
let key = self.0;
Error::from_status(unsafe { NCryptFinalizeKey(key, 0) })?;
std::mem::forget(self);
Ok(Key(key))
}
}
impl Drop for KeyBuilder {
fn drop(&mut self) {
Key(self.0).delete()
}
}
impl ProviderName {
fn as_wide_string(&self) -> Cow<U16CStr> {
unsafe {
match self {
Self::KeyStorage => Cow::Borrowed(U16CStr::from_ptr_str(MS_KEY_STORAGE_PROVIDER)),
Self::SmartCard => {
Cow::Borrowed(U16CStr::from_ptr_str(MS_SMART_CARD_KEY_STORAGE_PROVIDER))
}
Self::PlatformCrypto => {
Cow::Borrowed(U16CStr::from_ptr_str(MS_PLATFORM_CRYPTO_PROVIDER))
}
}
}
}
}
impl Algorithm {
fn as_wide_string(&self) -> Cow<U16CStr> {
unsafe {
match self {
Self::Rsa => Cow::Borrowed(U16CStr::from_ptr_str(BCRYPT_RSA_ALGORITHM)),
}
}
}
}
impl ExportType {
fn as_wide_string(&self) -> &U16CStr {
unsafe {
match self {
Self::RsaPublicKey => U16CStr::from_ptr_str(BCRYPT_RSAPUBLIC_BLOB),
}
}
}
}
impl<'a> Iterator for KeyIterator<'a> {
type Item = KeyInfo<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
let mut key_name = null_mut();
let status = unsafe {
NCryptEnumKeys(
self.provider.0,
null(),
&mut key_name,
&mut self.enum_state,
0,
)
};
if status != 0 {
if !self.enum_state.is_null() {
unsafe {
NCryptFreeBuffer(self.enum_state);
}
}
return None;
}
let key_info = unsafe {
KeyInfo {
name: U16CStr::from_ptr_str((*key_name).pszName).to_string_lossy(),
algorithm: U16CStr::from_ptr_str((*key_name).pszAlgid).to_string_lossy(),
provider: self.provider,
}
};
unsafe {
NCryptFreeBuffer(key_name as _);
}
Some(key_info)
}
}
impl<'a> KeyInfo<'a> {
pub fn open(&self) -> Result<Key, Error> {
self.provider.open_key(&self.name)
}
}