simple_encrypt/
lib.rs

1use aes_gcm::{
2    aead::{Aead, KeyInit},
3    Aes256Gcm, Nonce,
4};
5use anyhow::{anyhow, bail, Result};
6use rand::{rngs::OsRng, TryRngCore};
7use std::fs;
8
9pub fn encrypt_bytes(plaintext: &[u8], key: &[u8]) -> Result<Vec<u8>> {
10    if key.len() != 32 {
11        bail!("Key must be exactly 32 bytes");
12    }
13
14    let cipher = Aes256Gcm::new(key.into());
15
16    // Generate a random 96-bit nonce (12 bytes)
17    let mut nonce_bytes = [0u8; 12];
18    OsRng.try_fill_bytes(&mut nonce_bytes)?;
19    let nonce = Nonce::from_slice(&nonce_bytes);
20
21    // Encrypt the plaintext
22    let ciphertext = cipher
23        .encrypt(nonce, plaintext)
24        .map_err(|e| anyhow!("Encryption failed: {}", e))?;
25
26    // Combine nonce and ciphertext for output
27    let mut output = nonce_bytes.to_vec();
28    output.extend_from_slice(&ciphertext);
29    Ok(output)
30}
31
32pub fn encrypt_string(plaintext: &str, key: &[u8]) -> Result<Vec<u8>> {
33    encrypt_bytes(plaintext.as_bytes(), key)
34}
35
36pub fn decrypt_bytes(encrypted: &[u8], key: &[u8]) -> Result<Vec<u8>> {
37    if key.len() != 32 {
38        bail!("Key must be exactly 32 bytes");
39    }
40    if encrypted.len() < 12 {
41        bail!("Encrypted data too short");
42    }
43
44    let cipher = Aes256Gcm::new(key.into());
45
46    // Split the input into nonce and ciphertext
47    let nonce = Nonce::from_slice(&encrypted[..12]);
48    let ciphertext = &encrypted[12..];
49
50    // Decrypt the ciphertext
51    cipher
52        .decrypt(nonce, ciphertext)
53        .map_err(|e| anyhow!("Decryption failed: {}", e))
54}
55
56pub fn decrypt_string(encrypted: &[u8], key: &[u8]) -> Result<String> {
57    let plaintext_bytes = decrypt_bytes(encrypted, key)?;
58    Ok(String::from_utf8(plaintext_bytes)?)
59}
60
61pub fn decrypt_file(filename: &str, password_b64: &str) -> Result<Vec<u8>> {
62    let data = fs::read(filename)?;
63    let key = base64::decode(password_b64)?;
64    decrypt_bytes(&data, &key)
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test() -> Result<()> {
73        let mut key = [0u8; 32];
74        OsRng.try_fill_bytes(&mut key)?;
75
76        let message = "Hello, world!";
77        let encrypted = encrypt_string(message, &key).expect("Encryption should succeed");
78
79        // Encrypted data should be longer than original due to nonce
80        assert!(encrypted.len() > message.len());
81        // First 12 bytes should be the nonce
82        assert_eq!(encrypted.len(), message.len() + 12 + 16); // +16 for GCM tag
83
84        let decrypted = decrypt_string(&encrypted, &key).expect("Decryption should succeed");
85        assert_eq!(message, decrypted);
86
87        // Test decryption fails with wrong key
88        let mut wrong_key = [0u8; 32];
89        OsRng.try_fill_bytes(&mut wrong_key)?;
90        assert!(decrypt_string(&encrypted, &wrong_key).is_err());
91        Ok(())
92    }
93}