dipper 0.5.3

An out-of-the-box modular dependency injection web application framework.
Documentation
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>>;
}

// https://github.com/RustCrypto/block-modes

#[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());
    }
}