1pub mod error;
4
5use rand::Rng;
6use std::{
7 fs::{read, write, File},
8 io::{BufRead, BufReader, Read},
9 path::PathBuf,
10 vec,
11};
12
13use crate::error::Error;
14
15use argon2::{self, Config};
16use chacha20poly1305::{
17 aead::{Aead, KeyInit},
18 ChaCha20Poly1305,
19};
20
21const HASH_SALT_LEN: usize = 16;
22const NONCE: &[u8; 12] = &[0; 12];
23
24pub fn encrypt_to_file(
37 pwd: PathBuf,
38 mut file_path: PathBuf,
39 output_path: Option<PathBuf>,
40) -> Result<bool, Error> {
41 let file_data = read(&file_path)?;
42 let pwd = read(pwd)?;
43 let data = encrypt(&pwd, &file_data)?;
44 if let Some(opath) = output_path {
45 file_path = opath
46 }
47 write(file_path, data)?;
48 Ok(true)
49}
50
51pub fn decrypt_from_file(
64 pwd: PathBuf,
65 mut file_path: PathBuf,
66 output_path: Option<PathBuf>,
67) -> Result<bool, Error> {
68 let file = File::open(&file_path)?;
69 let pwd = read(pwd)?;
70 let mut reader = BufReader::new(file);
71 let data = decrypt(&pwd, &mut reader)?;
72 if let Some(opath) = output_path {
73 file_path = opath
74 }
75 write(file_path, data)?;
76 Ok(true)
77}
78
79pub fn encrypt(pwd: &Vec<u8>, data: &Vec<u8>) -> Result<Vec<u8>, Error> {
83 let config = Config::default();
84 let rand_pwd = gen_bytes(32);
85 let mut salt = gen_bytes(HASH_SALT_LEN);
86
87 let (file_hash, mut file_cipher) = get_cipher(&rand_pwd, &salt, &config, data)?;
88 let (_, mut key_cipher) = get_cipher(&pwd, &salt, &config, &file_hash)?;
89
90 let mut final_data: Vec<u8> = vec![];
91 final_data.append(&mut salt);
92 final_data.push(b'\n');
93 final_data.append(&mut key_cipher);
94 final_data.push(b'\n');
95 final_data.append(&mut file_cipher);
96
97 Ok(final_data)
98}
99
100pub fn decrypt<BufReaderType>(
104 pwd: &Vec<u8>,
105 reader: &mut BufReader<BufReaderType>,
106) -> Result<Vec<u8>, Error>
107where
108 BufReaderType: Read,
109{
110 let config = Config::default();
111
112 let mut salt = vec![];
113 reader.read_until(b'\n', &mut salt)?;
114 salt.pop();
115
116 let mut key_cipcher = vec![];
117 reader.read_until(b'\n', &mut key_cipcher)?;
118 key_cipcher.pop();
119
120 let key_hash = get_hash(&pwd, &salt, &config)?;
121 let file_hash = get_data(&key_hash, &key_cipcher)?;
122
123 let mut ciphered_data = vec![];
124 reader.read_to_end(&mut ciphered_data)?;
125
126 Ok(get_data(&file_hash, &ciphered_data)?)
127}
128
129fn get_cipher(
130 pwd: &Vec<u8>,
131 salt: &Vec<u8>,
132 config: &Config,
133 data: &Vec<u8>,
134) -> Result<(Vec<u8>, Vec<u8>), Error> {
135 let hash = get_hash(pwd, salt, config)?;
136 let cipher = ChaCha20Poly1305::new((&hash[..]).into());
137 let ciphered_data = cipher.encrypt(NONCE.into(), &data[..])?;
138 Ok((hash, ciphered_data))
139}
140
141fn get_hash(pwd: &Vec<u8>, salt: &Vec<u8>, config: &Config) -> Result<Vec<u8>, Error> {
142 Ok(argon2::hash_raw(&pwd[..], salt, config)?)
143}
144
145fn get_data(hash: &Vec<u8>, cipher_data: &Vec<u8>) -> Result<Vec<u8>, Error> {
146 let cipher = ChaCha20Poly1305::new((&hash[..]).into());
147 Ok(cipher.decrypt(NONCE.into(), &cipher_data[..])?)
148}
149
150fn gen_bytes(len: usize) -> Vec<u8> {
151 const CHARSET: &[u8] =
152 b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789)(*&^%$#@!~";
153 let mut rng = rand::thread_rng();
154 let password = (0..len)
155 .map(|_| {
156 let idx = rng.gen_range(0..CHARSET.len());
157 CHARSET[idx]
158 })
159 .collect::<Vec<u8>>();
160 password
161}