use crate::pool::Pool;
use crate::{Error, Status};
use alloc::ffi::CString;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
use core::ffi::c_char;
use core::marker::PhantomData;
use core::ptr;
pub struct CryptoDriver<'pool> {
driver: *const apr_sys::apr_crypto_driver_t,
_pool: PhantomData<&'pool Pool<'pool>>,
}
pub struct Crypto<'pool> {
factory: *mut apr_sys::apr_crypto_t,
_pool: PhantomData<&'pool Pool<'pool>>,
}
pub struct CryptoBlock<'pool> {
block: *mut apr_sys::apr_crypto_block_t,
_pool: PhantomData<&'pool Pool<'pool>>,
}
pub struct CryptoKey<'pool> {
key: *mut apr_sys::apr_crypto_key_t,
_pool: PhantomData<&'pool Pool<'pool>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BlockCipherMode {
ECB,
CBC,
}
impl From<BlockCipherMode> for apr_sys::apr_crypto_block_key_mode_e {
fn from(mode: BlockCipherMode) -> Self {
match mode {
BlockCipherMode::ECB => apr_sys::apr_crypto_block_key_mode_e_APR_MODE_ECB,
BlockCipherMode::CBC => apr_sys::apr_crypto_block_key_mode_e_APR_MODE_CBC,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BlockCipherAlgorithm {
AES128,
AES192,
AES256,
DES3,
}
impl From<BlockCipherAlgorithm> for apr_sys::apr_crypto_block_key_type_e {
fn from(algo: BlockCipherAlgorithm) -> Self {
match algo {
BlockCipherAlgorithm::AES128 => apr_sys::apr_crypto_block_key_type_e_APR_KEY_AES_128,
BlockCipherAlgorithm::AES192 => apr_sys::apr_crypto_block_key_type_e_APR_KEY_AES_192,
BlockCipherAlgorithm::AES256 => apr_sys::apr_crypto_block_key_type_e_APR_KEY_AES_256,
BlockCipherAlgorithm::DES3 => apr_sys::apr_crypto_block_key_type_e_APR_KEY_3DES_192,
}
}
}
pub fn init() -> Result<(), Error> {
crate::pool::with_tmp_pool(|pool| {
let status = unsafe { apr_sys::apr_crypto_init(pool.as_ptr() as *mut apr_sys::apr_pool_t) };
if status == apr_sys::APR_SUCCESS as i32 {
Ok(())
} else {
Err(Error::from_status(Status::from(status)))
}
})
}
pub fn encrypt_aes256(key: &[u8], data: &[u8], iv: Option<&[u8]>) -> Result<Vec<u8>, Error> {
crate::pool::with_tmp_pool(|pool| {
let driver = get_driver("openssl", pool)?;
let crypto = driver.make_crypto(pool)?;
let crypto_key = crypto.make_key(
BlockCipherAlgorithm::AES256,
BlockCipherMode::CBC,
key,
pool,
)?;
crypto.encrypt(&crypto_key, data, iv, pool)
})
}
pub fn decrypt_aes256(key: &[u8], data: &[u8], iv: Option<&[u8]>) -> Result<Vec<u8>, Error> {
crate::pool::with_tmp_pool(|pool| {
let driver = get_driver("openssl", pool)?;
let crypto = driver.make_crypto(pool)?;
let crypto_key = crypto.make_key(
BlockCipherAlgorithm::AES256,
BlockCipherMode::CBC,
key,
pool,
)?;
crypto.decrypt(&crypto_key, data, iv, pool)
})
}
pub fn get_driver<'pool>(
name: &str,
pool: &'pool Pool<'pool>,
) -> Result<CryptoDriver<'pool>, Error> {
let name_cstr = CString::new(name)
.map_err(|_| Error::from_status(Status::from(apr_sys::APR_EINVAL as i32)))?;
let mut driver: *const apr_sys::apr_crypto_driver_t = ptr::null();
let params_ptr: *const c_char = ptr::null();
let mut error_ptr: *const apr_sys::apu_err_t = ptr::null();
let status = unsafe {
apr_sys::apr_crypto_get_driver(
&mut driver,
name_cstr.as_ptr(),
params_ptr,
&mut error_ptr,
pool.as_ptr() as *mut apr_sys::apr_pool_t,
)
};
if status == apr_sys::APR_SUCCESS as i32 {
Ok(CryptoDriver {
driver,
_pool: PhantomData,
})
} else {
Err(Error::from_status(Status::from(status)))
}
}
impl Crypto<'_> {
pub fn init(pool: &Pool<'_>) -> Result<(), Error> {
let status = unsafe { apr_sys::apr_crypto_init(pool.as_ptr() as *mut apr_sys::apr_pool_t) };
if status == apr_sys::APR_SUCCESS as i32 {
Ok(())
} else {
Err(Error::from_status(Status::from(status)))
}
}
}
impl<'pool> CryptoDriver<'pool> {
pub fn make_crypto(&self, pool: &'pool Pool<'pool>) -> Result<Crypto<'pool>, Error> {
let mut factory: *mut apr_sys::apr_crypto_t = ptr::null_mut();
let params_ptr: *const c_char = ptr::null();
let status = unsafe {
apr_sys::apr_crypto_make(
&mut factory,
self.driver,
params_ptr,
pool.as_ptr() as *mut apr_sys::apr_pool_t,
)
};
if status == apr_sys::APR_SUCCESS as i32 {
Ok(Crypto {
factory,
_pool: PhantomData,
})
} else {
Err(Error::from_status(Status::from(status)))
}
}
}
impl<'pool> Crypto<'pool> {
pub fn make_key(
&self,
algorithm: BlockCipherAlgorithm,
mode: BlockCipherMode,
key_data: &[u8],
pool: &'pool Pool<'pool>,
) -> Result<CryptoKey<'pool>, Error> {
let mut key: *mut apr_sys::apr_crypto_key_t = ptr::null_mut();
let mut iv_size: apr_sys::apr_size_t = 0;
let status = unsafe {
apr_sys::apr_crypto_passphrase(
&mut key,
&mut iv_size,
key_data.as_ptr() as *const c_char,
key_data.len() as apr_sys::apr_size_t,
ptr::null(), 0, algorithm.into(),
mode.into(),
1, 4096, self.factory,
pool.as_ptr() as *mut apr_sys::apr_pool_t,
)
};
if status == apr_sys::APR_SUCCESS as i32 {
Ok(CryptoKey {
key,
_pool: PhantomData,
})
} else {
Err(Error::from_status(Status::from(status)))
}
}
pub fn encrypt(
&self,
key: &CryptoKey,
plaintext: &[u8],
iv: Option<&[u8]>,
pool: &Pool<'_>,
) -> Result<Vec<u8>, Error> {
let mut block: *mut apr_sys::apr_crypto_block_t = ptr::null_mut();
let mut block_size: apr_sys::apr_size_t = 0;
let mut iv_ptr = iv.map(|v| v.as_ptr()).unwrap_or(ptr::null());
let status = unsafe {
apr_sys::apr_crypto_block_encrypt_init(
&mut block,
&mut iv_ptr,
key.key,
&mut block_size,
pool.as_ptr() as *mut apr_sys::apr_pool_t,
)
};
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
unsafe {
apr_sys::apr_crypto_block_encrypt(
&mut ptr::null_mut(),
&mut block_size,
ptr::null(),
0,
block,
);
}
let mut ciphertext = vec![0u8; plaintext.len() + block_size as usize];
let mut out_ptr = ciphertext.as_mut_ptr();
let mut out_len = ciphertext.len() as apr_sys::apr_size_t;
let status = unsafe {
apr_sys::apr_crypto_block_encrypt(
&mut out_ptr,
&mut out_len,
plaintext.as_ptr(),
plaintext.len() as apr_sys::apr_size_t,
block,
)
};
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
let mut final_len = ciphertext.len() as apr_sys::apr_size_t - out_len;
let status =
unsafe { apr_sys::apr_crypto_block_encrypt_finish(out_ptr, &mut final_len, block) };
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
unsafe {
apr_sys::apr_crypto_block_cleanup(block);
}
ciphertext.truncate((out_len + final_len) as usize);
Ok(ciphertext)
}
pub fn decrypt(
&self,
key: &CryptoKey,
ciphertext: &[u8],
iv: Option<&[u8]>,
pool: &Pool<'_>,
) -> Result<Vec<u8>, Error> {
let mut block: *mut apr_sys::apr_crypto_block_t = ptr::null_mut();
let mut block_size: apr_sys::apr_size_t = 0;
let iv_ptr = iv.map(|v| v.as_ptr()).unwrap_or(ptr::null());
let status = unsafe {
apr_sys::apr_crypto_block_decrypt_init(
&mut block,
&mut block_size,
iv_ptr,
key.key,
pool.as_ptr() as *mut apr_sys::apr_pool_t,
)
};
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
let mut plaintext = vec![0u8; ciphertext.len()];
let mut out_ptr = plaintext.as_mut_ptr();
let mut out_len = plaintext.len() as apr_sys::apr_size_t;
let status = unsafe {
apr_sys::apr_crypto_block_decrypt(
&mut out_ptr,
&mut out_len,
ciphertext.as_ptr(),
ciphertext.len() as apr_sys::apr_size_t,
block,
)
};
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
let mut final_len = plaintext.len() as apr_sys::apr_size_t - out_len;
let status =
unsafe { apr_sys::apr_crypto_block_decrypt_finish(out_ptr, &mut final_len, block) };
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
unsafe {
apr_sys::apr_crypto_block_cleanup(block);
}
plaintext.truncate((out_len + final_len) as usize);
Ok(plaintext)
}
}
impl<'pool> CryptoBlock<'pool> {
pub fn encrypt_init(
key: &CryptoKey,
iv: Option<&[u8]>,
pool: &'pool Pool<'pool>,
) -> Result<Self, Error> {
let mut block: *mut apr_sys::apr_crypto_block_t = ptr::null_mut();
let mut block_size: apr_sys::apr_size_t = 0;
let mut iv_ptr = iv.map(|v| v.as_ptr()).unwrap_or(ptr::null());
let status = unsafe {
apr_sys::apr_crypto_block_encrypt_init(
&mut block,
&mut iv_ptr,
key.key,
&mut block_size,
pool.as_ptr() as *mut apr_sys::apr_pool_t,
)
};
if status == apr_sys::APR_SUCCESS as i32 {
Ok(CryptoBlock {
block,
_pool: PhantomData,
})
} else {
Err(Error::from_status(Status::from(status)))
}
}
pub fn decrypt_init(
key: &CryptoKey,
iv: Option<&[u8]>,
pool: &'pool Pool<'pool>,
) -> Result<Self, Error> {
let mut block: *mut apr_sys::apr_crypto_block_t = ptr::null_mut();
let mut block_size: apr_sys::apr_size_t = 0;
let iv_ptr = iv.map(|v| v.as_ptr()).unwrap_or(ptr::null());
let status = unsafe {
apr_sys::apr_crypto_block_decrypt_init(
&mut block,
&mut block_size,
iv_ptr,
key.key,
pool.as_ptr() as *mut apr_sys::apr_pool_t,
)
};
if status == apr_sys::APR_SUCCESS as i32 {
Ok(CryptoBlock {
block,
_pool: PhantomData,
})
} else {
Err(Error::from_status(Status::from(status)))
}
}
pub fn encrypt(&mut self, plaintext: &[u8]) -> Result<Vec<u8>, Error> {
let mut block_size: apr_sys::apr_size_t = 0;
unsafe {
apr_sys::apr_crypto_block_encrypt(
&mut ptr::null_mut(),
&mut block_size,
ptr::null(),
0,
self.block,
);
}
let mut ciphertext = vec![0u8; plaintext.len() + block_size as usize];
let mut out_ptr = ciphertext.as_mut_ptr();
let mut out_len = ciphertext.len() as apr_sys::apr_size_t;
let status = unsafe {
apr_sys::apr_crypto_block_encrypt(
&mut out_ptr,
&mut out_len,
plaintext.as_ptr(),
plaintext.len() as apr_sys::apr_size_t,
self.block,
)
};
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
let mut final_len = ciphertext.len() as apr_sys::apr_size_t - out_len;
let status = unsafe {
apr_sys::apr_crypto_block_encrypt_finish(out_ptr, &mut final_len, self.block)
};
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
ciphertext.truncate((out_len + final_len) as usize);
Ok(ciphertext)
}
pub fn decrypt(&mut self, ciphertext: &[u8]) -> Result<Vec<u8>, Error> {
let mut plaintext = vec![0u8; ciphertext.len()];
let mut out_ptr = plaintext.as_mut_ptr();
let mut out_len = plaintext.len() as apr_sys::apr_size_t;
let status = unsafe {
apr_sys::apr_crypto_block_decrypt(
&mut out_ptr,
&mut out_len,
ciphertext.as_ptr(),
ciphertext.len() as apr_sys::apr_size_t,
self.block,
)
};
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
let mut final_len = plaintext.len() as apr_sys::apr_size_t - out_len;
let status = unsafe {
apr_sys::apr_crypto_block_decrypt_finish(out_ptr, &mut final_len, self.block)
};
if status != apr_sys::APR_SUCCESS as i32 {
return Err(Error::from_status(Status::from(status)));
}
plaintext.truncate((out_len + final_len) as usize);
Ok(plaintext)
}
pub fn as_ptr(&self) -> *mut apr_sys::apr_crypto_block_t {
self.block
}
}
impl<'pool> Drop for CryptoBlock<'pool> {
fn drop(&mut self) {
unsafe {
apr_sys::apr_crypto_block_cleanup(self.block);
}
}
}
pub fn crypto_drivers(pool: &Pool<'_>) -> Vec<String> {
let drivers = ["openssl", "nss", "commoncrypto", "mscapi", "mscng"];
let mut available = Vec::new();
for name in &drivers {
if get_driver(name, pool).is_ok() {
available.push(name.to_string());
}
}
available
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_crypto_init() {
let pool = Pool::new();
let _ = Crypto::init(&pool);
}
#[test]
fn test_crypto_drivers() {
let pool = Pool::new();
let _ = Crypto::init(&pool);
let _drivers = crypto_drivers(&pool);
}
#[test]
fn test_encrypt_decrypt() {
let pool = Pool::new();
if Crypto::init(&pool).is_err() {
return; }
let driver = match get_driver("openssl", &pool)
.or_else(|_| get_driver("nss", &pool))
.or_else(|_| get_driver("commoncrypto", &pool))
{
Ok(d) => d,
Err(_) => return, };
let crypto = match driver.make_crypto(&pool) {
Ok(c) => c,
Err(_) => return,
};
let key_data = b"thisisasecretkey";
let key = match crypto.make_key(
BlockCipherAlgorithm::AES128,
BlockCipherMode::CBC,
key_data,
&pool,
) {
Ok(k) => k,
Err(_) => return,
};
let plaintext = b"Hello, World! This is a test.";
let iv = b"1234567890123456";
let ciphertext = match crypto.encrypt(&key, plaintext, Some(iv), &pool) {
Ok(c) => c,
Err(_) => return,
};
assert!(!ciphertext.is_empty());
assert_ne!(&ciphertext[..], plaintext);
let decrypted = match crypto.decrypt(&key, &ciphertext, Some(iv), &pool) {
Ok(p) => p,
Err(_) => return,
};
assert_eq!(&decrypted[..], plaintext);
}
#[test]
fn test_crypto_block_encrypt_decrypt() {
let pool = Pool::new();
if Crypto::init(&pool).is_err() {
return; }
let driver = match get_driver("openssl", &pool)
.or_else(|_| get_driver("nss", &pool))
.or_else(|_| get_driver("commoncrypto", &pool))
{
Ok(d) => d,
Err(_) => return, };
let crypto = match driver.make_crypto(&pool) {
Ok(c) => c,
Err(_) => return,
};
let key_data = b"thisisasecretkey";
let key = match crypto.make_key(
BlockCipherAlgorithm::AES128,
BlockCipherMode::CBC,
key_data,
&pool,
) {
Ok(k) => k,
Err(_) => return,
};
let plaintext = b"Hello, World! This is a test.";
let iv = b"1234567890123456";
let mut encrypt_block = match CryptoBlock::encrypt_init(&key, Some(iv), &pool) {
Ok(b) => b,
Err(_) => return,
};
let ciphertext = match encrypt_block.encrypt(plaintext) {
Ok(c) => c,
Err(_) => return,
};
assert!(!ciphertext.is_empty());
assert_ne!(&ciphertext[..], plaintext);
let mut decrypt_block = match CryptoBlock::decrypt_init(&key, Some(iv), &pool) {
Ok(b) => b,
Err(_) => return,
};
let decrypted = match decrypt_block.decrypt(&ciphertext) {
Ok(p) => p,
Err(_) => return,
};
assert_eq!(&decrypted[..], plaintext);
}
#[test]
fn test_crypto_block_as_ptr() {
let pool = Pool::new();
if Crypto::init(&pool).is_err() {
return; }
let driver = match get_driver("openssl", &pool)
.or_else(|_| get_driver("nss", &pool))
.or_else(|_| get_driver("commoncrypto", &pool))
{
Ok(d) => d,
Err(_) => return, };
let crypto = match driver.make_crypto(&pool) {
Ok(c) => c,
Err(_) => return,
};
let key_data = b"thisisasecretkey";
let key = match crypto.make_key(
BlockCipherAlgorithm::AES128,
BlockCipherMode::CBC,
key_data,
&pool,
) {
Ok(k) => k,
Err(_) => return,
};
let iv = b"1234567890123456";
let block = match CryptoBlock::encrypt_init(&key, Some(iv), &pool) {
Ok(b) => b,
Err(_) => return,
};
let ptr = block.as_ptr();
assert!(!ptr.is_null());
}
#[test]
fn test_crypto_block_multiple_operations() {
let pool = Pool::new();
if Crypto::init(&pool).is_err() {
return; }
let driver = match get_driver("openssl", &pool)
.or_else(|_| get_driver("nss", &pool))
.or_else(|_| get_driver("commoncrypto", &pool))
{
Ok(d) => d,
Err(_) => return, };
let crypto = match driver.make_crypto(&pool) {
Ok(c) => c,
Err(_) => return,
};
let key_data = b"thisisasecretkey";
let key = match crypto.make_key(
BlockCipherAlgorithm::AES256,
BlockCipherMode::CBC,
key_data,
&pool,
) {
Ok(k) => k,
Err(_) => return,
};
let messages = [
(b"First message".as_ref(), b"1234567890123456"),
(b"Second message here", b"6543210987654321"),
(b"Third and final message!", b"abcdefghijklmnop"),
];
for (plaintext, iv) in &messages {
let mut encrypt_block = match CryptoBlock::encrypt_init(&key, Some(*iv), &pool) {
Ok(b) => b,
Err(_) => return,
};
let ciphertext = match encrypt_block.encrypt(plaintext) {
Ok(c) => c,
Err(_) => return,
};
let mut decrypt_block = match CryptoBlock::decrypt_init(&key, Some(*iv), &pool) {
Ok(b) => b,
Err(_) => return,
};
let decrypted = match decrypt_block.decrypt(&ciphertext) {
Ok(p) => p,
Err(_) => return,
};
assert_eq!(&decrypted[..], *plaintext);
}
}
}