ncrypt 0.2.0

WIP rust bindings for the win32 ncrypt api
Documentation
#![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 {
    // Aes,
    // AesCmac,
    // AesGmac,
    // CapiKdf,
    // Des,
    // Desx,
    // Dh,
    // Dsa,
    // EcdhP256,
    // EcdhP384,
    // EcdhP521,
    // EcdsaP256,
    // EcdsaP384,
    // EcdsaP521,
    // Md2,
    // Md5,
    // Rc2,
    // Rc4,
    // Rng,
    // DualEcRng,
    // Fips186DsaRng,
    // Rsa,
    // RsaSign,
    // Sha1,
    // Sha256,
    // Sha384,
    // Sha512,
    // ...
    Rsa,
}

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum ExportType {
    RsaPublicKey,
    // RsaPrivateKey,
    // RsaKeyPair,
}

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

    /// Rsa decrypts given data padded with Pkcs1
    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)
    }

    /// Returns the key length in bytes (if applicable)
    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)
    }
}