use bytes::{Bytes, BytesMut};
use openssl::symm;
use crate::{
crypto::{StreamCipher, cipher::CipherType, nonce_advance},
error::SecioError,
};
pub(crate) struct OpenSsLCrypt {
cipher: symm::Cipher,
cipher_type: CipherType,
key: Bytes,
iv: BytesMut,
}
impl OpenSsLCrypt {
pub fn new(cipher_type: CipherType, key: &[u8]) -> Self {
let cipher = match cipher_type {
CipherType::Aes128Gcm => symm::Cipher::aes_128_gcm(),
CipherType::Aes256Gcm => symm::Cipher::aes_256_gcm(),
#[cfg(ossl110)]
CipherType::ChaCha20Poly1305 => symm::Cipher::chacha20_poly1305(),
#[cfg(not(ossl110))]
_ => panic!(
"Cipher type {:?} does not supported by OpenSsLCrypt yet",
cipher_type
),
};
let nonce_size = cipher_type.iv_size();
let nonce = BytesMut::from(vec![0u8; nonce_size].as_slice());
OpenSsLCrypt {
cipher,
cipher_type,
key: Bytes::from(key.to_owned()),
iv: nonce,
}
}
pub fn encrypt(&mut self, input: &[u8]) -> Result<Vec<u8>, SecioError> {
nonce_advance(self.iv.as_mut());
let tag_size = self.cipher_type.tag_size();
let mut tag = vec![0; tag_size];
let mut output =
symm::encrypt_aead(self.cipher, &self.key, Some(&self.iv), &[], input, &mut tag)?;
output.append(&mut tag);
Ok(output)
}
pub fn decrypt(&mut self, input: &[u8]) -> Result<Vec<u8>, SecioError> {
nonce_advance(self.iv.as_mut());
let crypt_data_len = input
.len()
.checked_sub(self.cipher_type.tag_size())
.ok_or(SecioError::FrameTooShort)?;
openssl::symm::decrypt_aead(
self.cipher,
&self.key,
Some(&self.iv),
&[],
&input[..crypt_data_len],
&input[crypt_data_len..],
)
.map_err(Into::into)
}
}
impl StreamCipher for OpenSsLCrypt {
fn encrypt(&mut self, input: &[u8]) -> Result<Vec<u8>, SecioError> {
self.encrypt(input)
}
fn decrypt(&mut self, input: &[u8]) -> Result<Vec<u8>, SecioError> {
self.decrypt(input)
}
}
#[cfg(test)]
mod test {
use super::{CipherType, OpenSsLCrypt};
use proptest::prelude::*;
fn test_openssl(mode: CipherType, message: &[u8]) {
let key = (0..mode.key_size())
.map(|_| rand::random::<u8>())
.collect::<Vec<_>>();
let mut encryptor = OpenSsLCrypt::new(mode, &key[0..]);
let mut decryptor = OpenSsLCrypt::new(mode, &key[0..]);
let encrypted_msg = encryptor.encrypt(message).unwrap();
let decrypted_msg = decryptor.decrypt(&encrypted_msg[..]).unwrap();
assert_eq!(message, &decrypted_msg[..]);
let encrypted_msg = encryptor.encrypt(message).unwrap();
let decrypted_msg = decryptor.decrypt(&encrypted_msg[..]).unwrap();
assert_eq!(message, &decrypted_msg[..]);
}
proptest! {
#[test]
fn test_aes_128_gcm(message: Vec<u8>) {
test_openssl(CipherType::Aes128Gcm, &message)
}
#[test]
fn test_aes_256_gcm(message: Vec<u8>) {
test_openssl(CipherType::Aes256Gcm, &message)
}
#[cfg(any(ossl110))]
#[test]
fn test_chacha20_poly1305(message: Vec<u8>) {
test_openssl(CipherType::ChaCha20Poly1305, &message)
}
}
}