pub mod error;
use rand::Rng;
use std::{
fs::{read, write, File},
io::{BufRead, BufReader, Read},
path::PathBuf,
vec,
};
use crate::error::Error;
use argon2::{self, Config};
use chacha20poly1305::{
aead::{Aead, KeyInit},
ChaCha20Poly1305,
};
const HASH_SALT_LEN: usize = 16;
const NONCE: &[u8; 12] = &[0; 12];
pub fn encrypt_to_file(
pwd: PathBuf,
mut file_path: PathBuf,
output_path: Option<PathBuf>,
) -> Result<bool, Error> {
let file_data = read(&file_path)?;
let pwd = read(pwd)?;
let data = encrypt(&pwd, &file_data)?;
if let Some(opath) = output_path {
file_path = opath
}
write(file_path, data)?;
Ok(true)
}
pub fn decrypt_from_file(
pwd: PathBuf,
mut file_path: PathBuf,
output_path: Option<PathBuf>,
) -> Result<bool, Error> {
let file = File::open(&file_path)?;
let pwd = read(pwd)?;
let mut reader = BufReader::new(file);
let data = decrypt(&pwd, &mut reader)?;
if let Some(opath) = output_path {
file_path = opath
}
write(file_path, data)?;
Ok(true)
}
pub fn encrypt(pwd: &Vec<u8>, data: &Vec<u8>) -> Result<Vec<u8>, Error> {
let config = Config::default();
let rand_pwd = gen_bytes(32);
let mut salt = gen_bytes(HASH_SALT_LEN);
let (file_hash, mut file_cipher) = get_cipher(&rand_pwd, &salt, &config, data)?;
let (_, mut key_cipher) = get_cipher(&pwd, &salt, &config, &file_hash)?;
let mut final_data: Vec<u8> = vec![];
final_data.append(&mut salt);
final_data.push(b'\n');
final_data.append(&mut key_cipher);
final_data.push(b'\n');
final_data.append(&mut file_cipher);
Ok(final_data)
}
pub fn decrypt<BufReaderType>(
pwd: &Vec<u8>,
reader: &mut BufReader<BufReaderType>,
) -> Result<Vec<u8>, Error>
where
BufReaderType: Read,
{
let config = Config::default();
let mut salt = vec![];
reader.read_until(b'\n', &mut salt)?;
salt.pop();
let mut key_cipcher = vec![];
reader.read_until(b'\n', &mut key_cipcher)?;
key_cipcher.pop();
let key_hash = get_hash(&pwd, &salt, &config)?;
let file_hash = get_data(&key_hash, &key_cipcher)?;
let mut ciphered_data = vec![];
reader.read_to_end(&mut ciphered_data)?;
Ok(get_data(&file_hash, &ciphered_data)?)
}
fn get_cipher(
pwd: &Vec<u8>,
salt: &Vec<u8>,
config: &Config,
data: &Vec<u8>,
) -> Result<(Vec<u8>, Vec<u8>), Error> {
let hash = get_hash(pwd, salt, config)?;
let cipher = ChaCha20Poly1305::new((&hash[..]).into());
let ciphered_data = cipher.encrypt(NONCE.into(), &data[..])?;
Ok((hash, ciphered_data))
}
fn get_hash(pwd: &Vec<u8>, salt: &Vec<u8>, config: &Config) -> Result<Vec<u8>, Error> {
Ok(argon2::hash_raw(&pwd[..], salt, config)?)
}
fn get_data(hash: &Vec<u8>, cipher_data: &Vec<u8>) -> Result<Vec<u8>, Error> {
let cipher = ChaCha20Poly1305::new((&hash[..]).into());
Ok(cipher.decrypt(NONCE.into(), &cipher_data[..])?)
}
fn gen_bytes(len: usize) -> Vec<u8> {
const CHARSET: &[u8] =
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789)(*&^%$#@!~";
let mut rng = rand::thread_rng();
let password = (0..len)
.map(|_| {
let idx = rng.gen_range(0..CHARSET.len());
CHARSET[idx]
})
.collect::<Vec<u8>>();
password
}