use aes_gcm_stream::{Aes256GcmStreamDecryptor, Aes256GcmStreamEncryptor}; use chacha20_poly1305_stream::{ChaCha20Poly1305StreamDecryptor, ChaCha20Poly1305StreamEncryptor}; use rust_util::{opt_result, simple_error, XResult}; use zeroize::Zeroize; use crate::{consts, util_env}; pub struct KeyNonce<'a, 'b> { pub k: &'a [u8], pub n: &'b [u8], } #[derive(Debug, Copy, Clone)] pub enum Cryptor { Aes256Gcm, ChaCha20Poly1305, } impl Cryptor { pub fn from(algorithm: &str) -> XResult { match algorithm { "aes256-gcm" | consts::TINY_ENC_AES_GCM => Ok(Cryptor::Aes256Gcm), "chacha20-poly1305" | consts::TINY_ENC_CHACHA20_POLY1305 => Ok(Cryptor::ChaCha20Poly1305), _ => simple_error!("Unknown algorithm: {}",algorithm), } } pub fn get_name(&self) -> String { let name = match self { Cryptor::Aes256Gcm => consts::TINY_ENC_AES_GCM, Cryptor::ChaCha20Poly1305 => consts::TINY_ENC_CHACHA20_POLY1305, }; name.to_string() } pub fn encryptor(self, key_nonce: &KeyNonce) -> XResult> { get_encryptor(self, key_nonce) } pub fn decryptor(self, key_nonce: &KeyNonce) -> XResult> { get_decryptor(self, key_nonce) } } pub trait Encryptor { fn update(&mut self, message: &[u8]) -> Vec; fn finalize(&mut self) -> (Vec, Vec); fn encrypt(&mut self, message: &[u8]) -> Vec { let mut cipher_text = self.update(message); let (last_block, tag) = self.finalize(); cipher_text.extend_from_slice(&last_block); cipher_text.extend_from_slice(&tag); cipher_text } } pub trait Decryptor { fn update(&mut self, message: &[u8]) -> Vec; fn finalize(&mut self) -> XResult>; fn decrypt(&mut self, message: &[u8]) -> XResult> { let mut plaintext = self.update(message); let last_block = self.finalize()?; plaintext.extend_from_slice(&last_block); Ok(plaintext) } } fn get_encryptor(crypto: Cryptor, key_nonce: &KeyNonce) -> XResult> { match crypto { Cryptor::Aes256Gcm => { let mut key: [u8; 32] = opt_result!(key_nonce.k.try_into(), "Bad AES 256 key: {}"); let aes256_gcm_stream_encryptor = Aes256GcmStreamEncryptor::new( key, key_nonce.n); key.zeroize(); Ok(Box::new(Aes256GcmEncryptor { aes256_gcm_stream_encryptor, })) } Cryptor::ChaCha20Poly1305 => Ok(Box::new(ChaCha20Poly1305Encryptor { chacha20_poly1305_stream_encryptor: ChaCha20Poly1305StreamEncryptor::new( key_nonce.k, key_nonce.n)?, })) } } fn get_decryptor(crypto: Cryptor, key_nonce: &KeyNonce) -> XResult> { match crypto { Cryptor::Aes256Gcm => { let mut key: [u8; 32] = opt_result!(key_nonce.k.try_into(), "Bad AES 256 key: {}"); let aes256_gcm_stream_decryptor = Aes256GcmStreamDecryptor::new( key, key_nonce.n); key.zeroize(); Ok(Box::new(Aes256GcmDecryptor { aes256_gcm_stream_decryptor, })) } Cryptor::ChaCha20Poly1305 => Ok(Box::new(ChaCha20Poly1305Decryptor { chacha20_poly1305_stream_decryptor: ChaCha20Poly1305StreamDecryptor::new( key_nonce.k, key_nonce.n)?, })) } } pub struct Aes256GcmEncryptor { aes256_gcm_stream_encryptor: Aes256GcmStreamEncryptor, } impl Encryptor for Aes256GcmEncryptor { fn update(&mut self, message: &[u8]) -> Vec { self.aes256_gcm_stream_encryptor.update(message) } fn finalize(&mut self) -> (Vec, Vec) { self.aes256_gcm_stream_encryptor.finalize() } } pub struct ChaCha20Poly1305Encryptor { chacha20_poly1305_stream_encryptor: ChaCha20Poly1305StreamEncryptor, } impl Encryptor for ChaCha20Poly1305Encryptor { fn update(&mut self, message: &[u8]) -> Vec { self.chacha20_poly1305_stream_encryptor.update(message) } fn finalize(&mut self) -> (Vec, Vec) { self.chacha20_poly1305_stream_encryptor.finalize() } } pub struct Aes256GcmDecryptor { aes256_gcm_stream_decryptor: Aes256GcmStreamDecryptor, } impl Decryptor for Aes256GcmDecryptor { fn update(&mut self, message: &[u8]) -> Vec { self.aes256_gcm_stream_decryptor.update(message) } fn finalize(&mut self) -> XResult> { Ok(self.aes256_gcm_stream_decryptor.finalize()?) } } pub struct ChaCha20Poly1305Decryptor { chacha20_poly1305_stream_decryptor: ChaCha20Poly1305StreamDecryptor, } impl Decryptor for ChaCha20Poly1305Decryptor { fn update(&mut self, message: &[u8]) -> Vec { self.chacha20_poly1305_stream_decryptor.update(message) } fn finalize(&mut self) -> XResult> { Ok(self.chacha20_poly1305_stream_decryptor.finalize()?) } } #[allow(clippy::redundant_closure)] pub fn get_cryptor_by_encryption_algorithm(encryption_algorithm: &Option) -> XResult { let encryption_algorithm = encryption_algorithm.as_deref() .or_else(|| util_env::get_default_encryption_algorithm()) .unwrap_or(consts::TINY_ENC_AES_GCM) .to_lowercase(); let cryptor = match encryption_algorithm.as_str() { "aes" | "aes/gcm" => Cryptor::Aes256Gcm, "chacha20" | "chacha20/poly1305" => Cryptor::ChaCha20Poly1305, _ => return simple_error!("Unknown encryption algorithm: {}, should be AES or CHACHA20", encryption_algorithm), }; Ok(cryptor) } #[test] fn test_cryptor() { let key = [0u8; 32]; let nonce = [0u8; 12]; let key_nonce = KeyNonce { k: &key, n: &nonce }; let ciphertext = Cryptor::Aes256Gcm.encryptor(&key_nonce).unwrap() .encrypt(b"hello world"); let plaintext = Cryptor::Aes256Gcm.decryptor(&key_nonce).unwrap() .decrypt(&ciphertext).unwrap(); assert_eq!(b"hello world", plaintext.as_slice()); }