use aes_gcm::{
aead::{Aead, KeyInit},
Aes256Gcm, Key, Nonce,
};
use hmac::digest::{generic_array::GenericArray, typenum};
use rand::RngCore;
use std::fs::File;
use std::io::{self, Write};
const SALT_LEN: usize = 16;
const NONCE_LEN: usize = 12;
fn generate_salt() -> [u8; SALT_LEN] {
let mut salt = [0u8; SALT_LEN];
rand::thread_rng().fill_bytes(&mut salt);
salt
}
fn generate_nonce() -> Nonce<typenum::U12> {
let mut nonce = [0u8; NONCE_LEN];
rand::thread_rng().fill_bytes(&mut nonce);
*Nonce::from_slice(&nonce)
}
fn derive_key_from_password_argon2(password: &str, salt: &[u8]) -> [u8; 32] {
use argon2::{self, password_hash::SaltString, Argon2, PasswordHasher};
let argon2 = Argon2::default();
let salt = SaltString::encode_b64(salt).unwrap();
let password_hash = argon2.hash_password(password.as_bytes(), &salt).unwrap();
let key = password_hash.hash.unwrap();
let mut key_bytes = [0u8; 32];
key_bytes.copy_from_slice(key.as_bytes());
key_bytes
}
pub fn encrypt_file(
input_file: &str,
output_file: &str,
password: &str,
) -> io::Result<()> {
let salt = generate_salt();
let key = derive_key_from_password_argon2(password, &salt);
let cipher = Aes256Gcm::new(Key::<Aes256Gcm>::from_slice(&key));
let nonce = generate_nonce();
let file_content = std::fs::read(input_file)?;
let ciphertext = cipher
.encrypt(&nonce, file_content.as_ref())
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Encryption failure"))?;
let mut output = File::create(output_file)?;
output.write_all(&salt)?; output.write_all(nonce.as_slice())?; output.write_all(&ciphertext)?;
Ok(())
}
pub fn decrypt_file(
input_file: &str,
output_file: &str,
password: &str,
) -> io::Result<()> {
let file_content = std::fs::read(input_file)?;
let (salt_and_nonce, ciphertext) =
file_content.split_at(SALT_LEN + NONCE_LEN); let (salt, nonce) = salt_and_nonce.split_at(SALT_LEN);
let key = derive_key_from_password_argon2(password, salt);
let cipher = Aes256Gcm::new(Key::<Aes256Gcm>::from_slice(&key));
let nonce = GenericArray::from_slice(nonce);
let plaintext = match cipher.decrypt(nonce, ciphertext) {
Ok(pt) => pt,
Err(_) => {
return Err(io::Error::new(io::ErrorKind::Other, "Decryption failure"))
}
};
let mut output = File::create(output_file)?;
output.write_all(&plaintext)?;
Ok(())
}