Skip to main content

i6_pack/encryptions/
cha_cha20_poly1305.rs

1use chacha20poly1305::{
2  aead::{Aead, AeadCore, KeyInit, OsRng},
3  Nonce,
4};
5
6use argon2::{self, password_hash::SaltString, Argon2, PasswordHasher};
7
8use hmac::digest::{generic_array::GenericArray, typenum};
9use rand::RngCore;
10use std::fs::File;
11use std::io::{self, Write};
12
13use super::encryption::{self, Encryption};
14
15const SALT_LEN: usize = 16;
16const NONCE_LEN: usize = 12;
17
18fn generate_salt() -> [u8; SALT_LEN] {
19  let mut salt = [0u8; SALT_LEN];
20  rand::thread_rng().fill_bytes(&mut salt);
21  salt
22}
23
24fn derive_key_from_password_argon2(password: &str, salt: &[u8]) -> [u8; 32] {
25  let argon2 = Argon2::default();
26  let salt = SaltString::encode_b64(salt).unwrap();
27  let password_hash = argon2.hash_password(password.as_bytes(), &salt).unwrap();
28  let key = password_hash.hash.unwrap();
29  let mut key_bytes = [0u8; 32];
30  key_bytes.copy_from_slice(key.as_bytes());
31  key_bytes
32}
33
34pub struct ChaCha20Poly1305;
35
36impl Encryption for ChaCha20Poly1305 {
37  fn encrypt_file(
38    input_file: &str,
39    output_file: &str,
40    password: &str,
41  ) -> io::Result<()> {
42    let salt = generate_salt();
43    let key = derive_key_from_password_argon2(password, &salt);
44    let cipher = chacha20poly1305::ChaCha20Poly1305::new((&key).into());
45    let nonce = chacha20poly1305::ChaCha20Poly1305::generate_nonce(&mut OsRng);
46
47    let file_content = std::fs::read(input_file)?;
48    let ciphertext =
49      cipher.encrypt(&nonce, file_content.as_ref()).map_err(|_| {
50        io::Error::new(io::ErrorKind::Other, "Encryption failure")
51      })?;
52
53    let mut output = File::create(output_file)?;
54    output.write_all(&salt)?; // Prepend salt
55    output.write_all(nonce.as_slice())?; // Prepend nonce
56    output.write_all(&ciphertext)?;
57
58    Ok(())
59  }
60
61  fn decrypt_file(
62    input_file: &str,
63    output_file: &str,
64    password: &str,
65  ) -> io::Result<()> {
66    let file_content = std::fs::read(input_file)?;
67    let (salt_and_nonce, ciphertext) =
68      file_content.split_at(SALT_LEN + NONCE_LEN); // Extract salt and nonce
69    let (salt, nonce) = salt_and_nonce.split_at(SALT_LEN); // Extract salt
70
71    let key = derive_key_from_password_argon2(password, salt);
72    let cipher = chacha20poly1305::ChaCha20Poly1305::new((&key).into());
73
74    let nonce = GenericArray::from_slice(nonce);
75    let plaintext = match cipher.decrypt(nonce, ciphertext) {
76      Ok(pt) => pt,
77      Err(_) => {
78        return Err(io::Error::new(io::ErrorKind::Other, "Decryption failure"))
79      }
80    };
81
82    let mut output = File::create(output_file)?;
83    output.write_all(&plaintext)?;
84    Ok(())
85  }
86}