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 pub fn generate() -> Self {
31 let cipher = Aes256Gcm::new(&Aes256Gcm::generate_key(&mut OsRng));
32 Self { cipher }
33 }
34
35 pub fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>> {
37 let mut nonce_bytes = [0u8; 12];
38 OsRng.fill_bytes(&mut nonce_bytes);
39 let nonce = Nonce::from_slice(&nonce_bytes);
40
41 let ciphertext = self.cipher
42 .encrypt(nonce, plaintext)
43 .map_err(|e| SecurityError::EncryptionError(e.to_string()))?;
44
45 let mut result = Vec::with_capacity(12 + ciphertext.len());
47 result.extend_from_slice(&nonce_bytes);
48 result.extend(ciphertext);
49
50 Ok(result)
51 }
52
53 pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
55 if ciphertext.len() < 12 {
56 return Err(SecurityError::InvalidFormat);
57 }
58
59 let (nonce_bytes, encrypted) = ciphertext.split_at(12);
60 let nonce = Nonce::from_slice(nonce_bytes);
61
62 self.cipher
63 .decrypt(nonce, encrypted)
64 .map_err(|e| SecurityError::DecryptionError(e.to_string()))
65 }
66}
67
68pub fn encrypt(key: &[u8], plaintext: &[u8]) -> Result<String> {
70 let aes_key = AesKey::from_bytes(key)?;
71 let encrypted = aes_key.encrypt(plaintext)?;
72 Ok(STANDARD.encode(&encrypted))
73}
74
75pub fn decrypt(key: &[u8], ciphertext: &str) -> Result<Vec<u8>> {
77 let aes_key = AesKey::from_bytes(key)?;
78 let encrypted = STANDARD.decode(ciphertext)
79 .map_err(|_| SecurityError::InvalidFormat)?;
80 aes_key.decrypt(&encrypted)
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn test_encrypt_decrypt() {
89 let key = AesKey::generate();
90 let plaintext = b"Hello, World!";
91
92 let encrypted = key.encrypt(plaintext).unwrap();
93 let decrypted = key.decrypt(&encrypted).unwrap();
94
95 assert_eq!(decrypted, plaintext);
96 }
97
98 #[test]
99 fn test_convenience_functions() {
100 let key = [0u8; 32]; let plaintext = b"Secret message";
102
103 let encrypted = encrypt(&key, plaintext).unwrap();
104 let decrypted = decrypt(&key, &encrypted).unwrap();
105
106 assert_eq!(decrypted, plaintext);
107 }
108}