chargo/
lib.rs

1//! Chargo library
2
3pub 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
24/// Encrypts provided file with the specified password
25/// `pwd` - path to a file with a password
26/// `file_path` - file to encrypt
27/// `output_path` - option file to write a result
28/// ```
29/// use chargo::encrypt_to_file;
30/// use std::path::PathBuf;
31///
32/// fn main() {
33///     encrypt_to_file(PathBuf::from("pwd.txt"), PathBuf::from("test.txt"), Some(PathBuf::from("test.chargo"))).unwrap();
34/// }
35/// ```
36pub 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
51/// Decrypts provided file with the specified password
52/// `pwd` - path to a file with a password
53/// `file_path` - file to decrypt
54/// `output_path` - option file to write a result
55/// ```
56/// use chargo::decrypt_from_file;
57/// use std::path::PathBuf;
58///
59/// fn main() {
60///     decrypt_from_file(PathBuf::from("pwd.txt"), PathBuf::from("test.chargo"), Some(PathBuf::from("test_dec.txt"))).unwrap();
61/// }
62/// ```
63pub 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
79/// Encrypts provided bytes with the specified password
80/// `pwd` - password
81/// `data` - vec with data bytes
82pub 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
100/// Decrypts data with the specified password
101/// `pwd` - password
102/// `reader` - buf reader
103pub 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}