use aes::{
Aes128,
cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit, block_padding::Pkcs7},
};
use base64::prelude::*;
pub trait AesCrypt {
fn encrypt(plaintext: &[u8], key: &[u8; 16], iv: &[u8; 16]) -> String;
fn decrypt(ciphertext_base64: &str, key: &[u8; 16], iv: &[u8; 16]) -> anyhow::Result<Vec<u8>>;
}
#[non_exhaustive]
pub struct Aes128Cbc;
impl AesCrypt for Aes128Cbc {
fn encrypt(plaintext: &[u8], key: &[u8; 16], iv: &[u8; 16]) -> String {
let ciphertext =
cbc::Encryptor::<Aes128>::new(key.into(), iv.into()).encrypt_padded_vec_mut::<Pkcs7>(plaintext);
#[allow(clippy::let_and_return)]
let ciphertext_base64 = BASE64_STANDARD_NO_PAD.encode(ciphertext);
ciphertext_base64
}
fn decrypt(ciphertext_base64: &str, key: &[u8; 16], iv: &[u8; 16]) -> anyhow::Result<Vec<u8>> {
let ciphertext = BASE64_STANDARD_NO_PAD.decode(ciphertext_base64)?;
let plaintext =
cbc::Decryptor::<Aes128>::new(key.into(), iv.into()).decrypt_padded_vec_mut::<Pkcs7>(&ciphertext)?;
Ok(plaintext)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_aes128_cbc() {
let key = [0x42; 16];
let iv = [0x24; 16];
let plaintext = *b"hello world! this is my plaintext.";
let ciphertext_base64 = Aes128Cbc::encrypt(&plaintext, &key, &iv);
assert_eq!(
"x/4kfvl7IfB8vdJstdNGv9J4Z8sA2UhnI+FZl4+5pfkUz7IopxDeQXHjlue2z4We",
ciphertext_base64
);
let plaintext2 = Aes128Cbc::decrypt(&ciphertext_base64, &key, &iv).unwrap();
assert_eq!(&plaintext, plaintext2.as_slice());
}
}