use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit, Nonce};
use rand::{rngs::OsRng, RngCore};
use sha2::{Digest, Sha256};
use std::io::{self, Read, Write};
pub const IV_LEN: usize = 96 / 8;
pub const TAG_LEN: usize = 128 / 8;
pub const ENC_CHUNK_LEN: usize = 128000;
pub const DEC_CHUNK_LEN: usize = IV_LEN + ENC_CHUNK_LEN + TAG_LEN;
#[derive(Debug)]
pub enum ErrorKind {
AesError(aes_gcm::Error),
Io(io::Error),
}
#[derive(Clone)]
pub struct Crypto {
cipher: Aes256Gcm,
}
impl Crypto {
pub fn new<K>(key: K) -> Result<Crypto, ErrorKind>
where
K: AsRef<[u8]>,
{
let key = hash_key(key.as_ref());
Ok(Crypto {
cipher: Aes256Gcm::new_from_slice(&key).unwrap(),
})
}
pub fn encrypt_with_iv(&self, plaintext: &[u8], iv: &[u8]) -> Result<Vec<u8>, ErrorKind> {
Ok(self
.cipher
.encrypt(Nonce::from_slice(iv), plaintext)
.map_err(|e| ErrorKind::AesError(e))?)
}
pub fn decrypt_with_iv(&self, ciphertext: &[u8], iv: &[u8]) -> Result<Vec<u8>, ErrorKind> {
Ok(self
.cipher
.decrypt(Nonce::from_slice(iv), ciphertext)
.map_err(|e| ErrorKind::AesError(e))?)
}
pub fn encrypt<P>(&self, plain: P) -> Result<Vec<u8>, ErrorKind>
where
P: AsRef<[u8]>,
{
let iv = random_iv();
Ok([&iv, self.encrypt_with_iv(plain.as_ref(), &iv)?.as_slice()].concat())
}
pub fn decrypt<E>(&self, enc: E) -> Result<Vec<u8>, ErrorKind>
where
E: AsRef<[u8]>,
{
let (iv, ciphertext) = enc.as_ref().split_at(IV_LEN);
self.decrypt_with_iv(ciphertext, iv)
}
pub fn encrypt_io(
&self,
source: &mut impl Read,
dest: &mut impl Write,
) -> Result<(), ErrorKind> {
let mut buffer = [0u8; ENC_CHUNK_LEN];
loop {
let read_len = source.read(&mut buffer).map_err(|e| ErrorKind::Io(e))?;
dest.write_all(&self.encrypt(&buffer[..read_len])?)
.map_err(|e| ErrorKind::Io(e))?;
if read_len != ENC_CHUNK_LEN {
break;
}
}
Ok(())
}
pub fn decrypt_io(
&self,
source: &mut impl Read,
dest: &mut impl Write,
) -> Result<(), ErrorKind> {
let mut buffer = [0u8; DEC_CHUNK_LEN];
loop {
let read_len = source.read(&mut buffer).map_err(|e| ErrorKind::Io(e))?;
dest.write_all(&self.decrypt(&buffer[..read_len])?)
.map_err(|e| ErrorKind::Io(e))?;
if read_len != DEC_CHUNK_LEN {
break;
}
}
Ok(())
}
}
fn hash_key<K>(key: K) -> Vec<u8>
where
K: AsRef<[u8]>,
{
let mut hasher = Sha256::new();
hasher.update(key.as_ref());
hasher.finalize().to_vec()
}
fn random_iv() -> [u8; IV_LEN] {
let mut iv = [0; IV_LEN];
OsRng.fill_bytes(&mut iv);
iv
}