use base64::{engine::general_purpose, Engine};
#[cfg(feature = "bincode")]
use bincode::{Decode, Encode};
use fernet::Fernet;
use pbkdf2::pbkdf2_hmac;
use rand::{thread_rng, Rng};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use sha2::Sha512;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "bincode", derive(Encode, Decode))]
pub struct Encrypted {
salt: [u8; 16],
data: String,
}
pub struct BytesEncrypter;
pub trait Encryptable<T> {
fn encrypt(data: &T, password: &str) -> Result<Encrypted, Box<dyn std::error::Error>>;
fn decrypt(data: &Encrypted, password: &str) -> Result<T, Box<dyn std::error::Error>>;
}
impl Encryptable<Vec<u8>> for BytesEncrypter {
fn encrypt(data: &Vec<u8>, password: &str) -> Result<Encrypted, Box<dyn std::error::Error>> {
let mut salt = [0u8; 16];
thread_rng().fill(&mut salt);
let mut kdf = [0u8; 32];
pbkdf2_hmac::<Sha512>(&password.as_bytes(), &salt, 480_000, &mut kdf);
let key = general_purpose::URL_SAFE.encode(&kdf);
let f = Fernet::new(&key.as_str()).unwrap();
Ok(Encrypted {
salt,
data: f.encrypt(&data),
})
}
fn decrypt(data: &Encrypted, password: &str) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let mut kdf = [0u8; 32];
pbkdf2_hmac::<Sha512>(&password.as_bytes(), &data.salt, 480_000, &mut kdf);
let key = general_purpose::URL_SAFE.encode(&kdf);
let f = Fernet::new(&key.as_str()).unwrap();
Ok(f.decrypt(&data.data)?)
}
}
#[cfg(test)]
mod tests {
use crate::{BytesEncrypter, Encryptable};
#[test]
fn encryption() {
const CORRECT_PASSWORD: &str = "password";
const INCORRECT_PASSWORD: &str = "incorrect password";
const TEST_DATA: &[u8] = b"test";
let encrypted = BytesEncrypter::encrypt(&TEST_DATA.to_vec(), CORRECT_PASSWORD).unwrap();
let d1 = BytesEncrypter::decrypt(&encrypted, CORRECT_PASSWORD);
let d2 = BytesEncrypter::decrypt(&encrypted, INCORRECT_PASSWORD);
assert!(&d1.is_ok());
assert!(&d2.is_err());
assert_eq!(&d1.unwrap(), TEST_DATA);
}
}