Skip to main content

i6_pack/encryptions/
aes256_gcm.rs

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