file_encrypt/encryption_algos/
aes256_gcm.rs

1use aes_gcm::{
2    aead::{Aead, AeadCore, KeyInit},
3    Aes256Gcm, Key, Nonce,
4};
5use anyhow::{anyhow, Result};
6use argon2::PasswordHash;
7use base64::prelude::*;
8use rand_core::OsRng;
9use std::{ffi::OsString, fs, path::PathBuf};
10
11use crate::hash_algos::argon_hash;
12
13pub fn encrypt_file(
14    file_path: String,
15    passphrase: String,
16    output_file: String,
17) -> Result<(OsString, String)> {
18    // Hash passphrase
19    let argon_pch: String = argon_hash::hash_passphrase(passphrase);
20    let hash = PasswordHash::new(&argon_pch).unwrap();
21    let hash = match hash.hash {
22        Some(hash) => hash.to_string(),
23        None => {
24            eprintln!("[-] Error with argon2 hash");
25            return Err(anyhow!("Unable to get hash from Argon2 PCH"));
26        }
27    };
28
29    // Argon2 hash is encoded in base64
30    // Must decode to extract 32-byte key needed
31    let hash = BASE64_STANDARD_NO_PAD.decode(hash)?;
32
33    // Generate iv, cipher, and key for encryption
34    let key = Key::<Aes256Gcm>::from_slice(&hash);
35    let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
36    let cipher = Aes256Gcm::new(&key);
37
38    // Read in file data and encrpyt
39    let file_data = fs::read(file_path.clone())?;
40    let mut enc_data = match cipher.encrypt(&nonce, file_data.as_ref()) {
41        Ok(enc_data) => enc_data,
42        Err(e) => {
43            let e = e.to_string();
44            return Err(anyhow!("Unable to encrypt data: {e}"));
45        }
46    };
47
48    // Attach nonce to encrypted data
49    // Needed for decryption
50    let mut nonce_and_data = nonce.to_vec();
51    nonce_and_data.append(&mut enc_data);
52
53    // Write out encrypted data to new file path
54    let new_file = PathBuf::from(output_file);
55    fs::write(new_file.clone(), nonce_and_data)?;
56
57    Ok((new_file.into(), argon_pch))
58}
59
60pub fn decrypt_file(
61    file_path: String,
62    passphrase: String,
63    expected_pch: String,
64    output_file: String,
65) -> Result<()> {
66    // Verify provided passphrase
67    let (res, hash_opt) = argon_hash::check_hash(passphrase, expected_pch);
68    if res == false {
69        eprintln!("[-] Invalid passphrase provided");
70        return Err(anyhow!("Invalid passphrase"));
71    }
72
73    // Convet hash to argon2 PCH object
74    let hash = hash_opt.unwrap();
75    let hash = PasswordHash::new(&hash).unwrap();
76    let hash = hash.hash.unwrap().to_string();
77
78    let hash = BASE64_STANDARD_NO_PAD.decode(hash)?;
79
80    // Generate iv, cipher, and key for encryption
81    let key = Key::<Aes256Gcm>::from_slice(&hash);
82    let cipher = Aes256Gcm::new(&key);
83
84    // Read in encrpyted data and get nonce
85    let mut nonce_and_data = fs::read(file_path.clone())?;
86    let data = nonce_and_data.split_off(12);
87    let nonce = nonce_and_data;
88    let nonce = Nonce::from_slice(&nonce);
89
90    let plain_data = match cipher.decrypt(nonce, data.as_ref()) {
91        Ok(data) => data,
92        Err(e) => {
93            let e = e.to_string();
94            return Err(anyhow!("Unable to decrypt file: {e}"));
95        }
96    };
97
98    let new_file = PathBuf::from(output_file);
99    fs::write(new_file, plain_data)?;
100
101    Ok(())
102}