use std::fs;
use std::path::Path;
use aes::Aes256;
use aes::cipher::block_padding::Pkcs7;
use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
use aes_gcm_siv::aead::{Aead, KeyInit};
use aes_gcm_siv::{Aes256GcmSiv, Nonce};
use cbc::{Decryptor, Encryptor};
use hex_simd::AsciiCase;
use crate::Error;
pub(crate) fn encrypt_and_save_to_file<P, T, E>(
plaintext: String,
file_path: P,
password: T,
nonce: E,
) -> Result<(), Error>
where
P: AsRef<Path>,
T: AsRef<str>,
E: AsRef<str>,
{
let (cipher, nonce) = make_cipher_and_nonce(password, nonce)?;
let ciphertext = cipher
.encrypt(&nonce, plaintext.as_bytes())
.map_err(|err| Error::AesGcmSiv(err.to_string()))?;
fs::write(
file_path,
base64_simd::STANDARD.encode_to_string(&ciphertext),
)?;
Ok(())
}
pub(crate) fn decrypt_from_file<P, T, E>(
file_path: P,
password: T,
nonce: E,
) -> Result<String, Error>
where
P: AsRef<Path>,
T: AsRef<str>,
E: AsRef<str>,
{
let mut ciphertext = fs::read(file_path)?;
let ciphertext = base64_simd::STANDARD.decode_inplace(ciphertext.as_mut_slice())?;
let (cipher, nonce) = make_cipher_and_nonce(password, nonce)?;
let plaintext = cipher
.decrypt(&nonce, &*ciphertext)
.map_err(|err| Error::AesGcmSiv(err.to_string()))?;
Ok(unsafe { String::from_utf8_unchecked(plaintext) })
}
fn make_cipher_and_nonce<T, E>(password: T, nonce: E) -> Result<(Aes256GcmSiv, Nonce), Error>
where
T: AsRef<str>,
E: AsRef<str>,
{
let key = crate::sha256(password.as_ref().as_bytes());
let cipher = Aes256GcmSiv::new_from_slice(key.as_ref())?;
let nonce = crate::md5_hex(nonce.as_ref(), AsciiCase::Lower);
let nonce = Nonce::from_slice(&nonce.as_bytes()[0..12]).to_owned();
Ok((cipher, nonce))
}
#[must_use]
pub(crate) fn aes_256_cbc_no_iv_base64_encrypt<T, E>(key: T, data: E) -> String
where
T: AsRef<[u8]>,
E: AsRef<[u8]>,
{
type Aes256CbcEnc = Encryptor<Aes256>;
let ciphertext = Aes256CbcEnc::new(key.as_ref().into(), &[0; 16].into())
.encrypt_padded_vec_mut::<Pkcs7>(data.as_ref());
base64_simd::STANDARD.encode_to_string(&ciphertext)
}
pub(crate) fn aes_256_cbc_no_iv_base64_decrypt<T, E>(key: T, data: E) -> Result<Vec<u8>, Error>
where
T: AsRef<[u8]>,
E: AsRef<[u8]>,
{
type Aes256CbcDec = Decryptor<Aes256>;
let ciphertext = base64_simd::STANDARD.decode_to_vec(data.as_ref())?;
let result = Aes256CbcDec::new(key.as_ref().into(), &[0; 16].into())
.decrypt_padded_vec_mut::<Pkcs7>(&ciphertext)?;
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn aes_256_gcm_base64() -> Result<(), Error> {
let dir = tempfile::tempdir()?;
let file = dir.path().join("aes.txt");
let data = String::from("Hello World");
encrypt_and_save_to_file(data, &file, "password", "nonce")?;
assert!(file.is_file());
let decrypted = decrypt_from_file(file, "password", "nonce")?;
assert_eq!(decrypted, "Hello World");
Ok(())
}
}