Skip to main content

oxidite_security/
crypto.rs

1//! Cryptographic utilities
2
3use crate::{Result, SecurityError};
4use aes_gcm::{
5    aead::{Aead, KeyInit, OsRng},
6    Aes256Gcm, Nonce,
7};
8use aes_gcm::aead::rand_core::RngCore;
9use base64::{Engine as _, engine::general_purpose::STANDARD};
10
11/// AES-256-GCM encryption key
12pub struct AesKey {
13    cipher: Aes256Gcm,
14}
15
16impl AesKey {
17    /// Create a new key from bytes (must be 32 bytes)
18    pub fn from_bytes(key: &[u8]) -> Result<Self> {
19        if key.len() != 32 {
20            return Err(SecurityError::InvalidKeyLength);
21        }
22        
23        let cipher = Aes256Gcm::new_from_slice(key)
24            .map_err(|e| SecurityError::EncryptionError(e.to_string()))?;
25        
26        Ok(Self { cipher })
27    }
28
29    /// Generate a new random key
30    #[must_use]
31    pub fn generate() -> Self {
32        let cipher = Aes256Gcm::new(&Aes256Gcm::generate_key(&mut OsRng));
33        Self { cipher }
34    }
35
36    /// Encrypt data
37    pub fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>> {
38        let mut nonce_bytes = [0u8; 12];
39        OsRng.fill_bytes(&mut nonce_bytes);
40        let nonce = Nonce::from_slice(&nonce_bytes);
41
42        let ciphertext = self.cipher
43            .encrypt(nonce, plaintext)
44            .map_err(|e| SecurityError::EncryptionError(e.to_string()))?;
45
46        // Prepend nonce to ciphertext
47        let mut result = Vec::with_capacity(12 + ciphertext.len());
48        result.extend_from_slice(&nonce_bytes);
49        result.extend(ciphertext);
50        
51        Ok(result)
52    }
53
54    /// Decrypt data
55    pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
56        if ciphertext.len() < 12 {
57            return Err(SecurityError::InvalidFormat);
58        }
59
60        let (nonce_bytes, encrypted) = ciphertext.split_at(12);
61        let nonce = Nonce::from_slice(nonce_bytes);
62
63        self.cipher
64            .decrypt(nonce, encrypted)
65            .map_err(|e| SecurityError::DecryptionError(e.to_string()))
66    }
67}
68
69/// Encrypt data with a key (returns base64-encoded result)
70pub fn encrypt(key: &[u8], plaintext: &[u8]) -> Result<String> {
71    let aes_key = AesKey::from_bytes(key)?;
72    let encrypted = aes_key.encrypt(plaintext)?;
73    Ok(STANDARD.encode(&encrypted))
74}
75
76/// Decrypt base64-encoded data with a key
77pub fn decrypt(key: &[u8], ciphertext: &str) -> Result<Vec<u8>> {
78    let aes_key = AesKey::from_bytes(key)?;
79    let encrypted = STANDARD.decode(ciphertext)
80        .map_err(|_| SecurityError::InvalidFormat)?;
81    aes_key.decrypt(&encrypted)
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_encrypt_decrypt() {
90        let key = AesKey::generate();
91        let plaintext = b"Hello, World!";
92        
93        let encrypted = key.encrypt(plaintext).unwrap();
94        let decrypted = key.decrypt(&encrypted).unwrap();
95        
96        assert_eq!(decrypted, plaintext);
97    }
98
99    #[test]
100    fn test_convenience_functions() {
101        let key = [0u8; 32]; // 32-byte key
102        let plaintext = b"Secret message";
103        
104        let encrypted = encrypt(&key, plaintext).unwrap();
105        let decrypted = decrypt(&key, &encrypted).unwrap();
106        
107        assert_eq!(decrypted, plaintext);
108    }
109}