i6_pack/encryptions/
aes256_gcm.rs1use 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)?; output.write_all(nonce.as_slice())?; 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); let (salt, nonce) = salt_and_nonce.split_at(SALT_LEN); 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}