oxidite_security/
crypto.rs1use 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
11pub struct AesKey {
13 cipher: Aes256Gcm,
14}
15
16impl AesKey {
17 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 #[must_use]
31 pub fn generate() -> Self {
32 let cipher = Aes256Gcm::new(&Aes256Gcm::generate_key(&mut OsRng));
33 Self { cipher }
34 }
35
36 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 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 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
69pub 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
76pub 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]; 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}