use aes_gcm::{ aead::{Aead, KeyInit, OsRng}, Aes256Gcm, Nonce, }; use argon2::{ password_hash::{self, SaltString}, Argon2, PasswordHasher, }; use derive_more::Display; use rand::Rng; use sha2::{Digest, Sha256}; use thiserror::Error; #[derive(Debug, Display, Error)] pub enum CrypterError { EncryptError(String), DecryptError(String), DecodeError(String), HashError(String), } pub struct Crypter { pub key: Vec, } impl Crypter { pub fn new(key: &[u8]) -> Self { Self { key: key.to_vec() } } pub fn encrypt(&self, data: &[u8]) -> Result, CrypterError> { let mut hasher = Sha256::new(); hasher.update(&self.key); let cipher = Aes256Gcm::new(&hasher.finalize()); let rbs = rand::thread_rng().gen::<[u8; 12]>(); let iv = Nonce::from_slice(&rbs); let crypt = cipher .encrypt(&iv, data) .map_err(|e| CrypterError::EncryptError(e.to_string()))?; let mut msg = iv.to_vec(); msg.extend_from_slice(&crypt); Ok(msg) } pub fn decrypt(&self, data: &[u8]) -> Result, CrypterError> { let mut hasher = Sha256::new(); hasher.update(&self.key); let cipher = Aes256Gcm::new(&hasher.finalize()); let iv = Nonce::from_slice(&data[0..=11]); let data = &data[12..]; Ok(cipher .decrypt(&iv, data) .map_err(|e| CrypterError::DecryptError(e.to_string()))?) } pub fn hash(&self, data: &[u8]) -> Result { let salt = SaltString::generate(&mut OsRng); let hasher = Argon2::default(); Ok(hasher .hash_password(data, &salt) .map_err(|e| CrypterError::HashError(e.to_string()))? .to_string()) } } #[cfg(test)] mod test { use super::*; #[test] fn encrypt_data_test() { let crypter = Crypter { key: "testkey".to_string().into_bytes(), }; let plaintext = "This is a secret."; let enc = crypter.encrypt(&plaintext.as_bytes()).unwrap(); let res = crypter.decrypt(&enc).unwrap(); assert_eq!(plaintext, std::str::from_utf8(&res).unwrap()); } }