celerix_store/engine/
vault.rs1use aes_gcm::{
2 aead::{Aead, AeadCore, KeyInit, OsRng},
3 Aes256Gcm, Nonce,
4};
5use crate::{Result, Error};
6
7pub fn encrypt(plaintext: &str, key: &[u8]) -> Result<String> {
8 if key.len() != 32 {
9 return Err(Error::Internal("Key must be 32 bytes".to_string()));
10 }
11 let cipher = Aes256Gcm::new_from_slice(key).map_err(|e| Error::Internal(e.to_string()))?;
12 let nonce = Aes256Gcm::generate_nonce(&mut OsRng); let ciphertext = cipher.encrypt(&nonce, plaintext.as_bytes()).map_err(|e| Error::Internal(e.to_string()))?;
14
15 let mut combined = nonce.to_vec();
16 combined.extend_from_slice(&ciphertext);
17 Ok(hex::encode(combined))
18}
19
20pub fn decrypt(cipher_hex: &str, key: &[u8]) -> Result<String> {
21 if key.len() != 32 {
22 return Err(Error::Internal("Key must be 32 bytes".to_string()));
23 }
24 let combined = hex::decode(cipher_hex).map_err(|e| Error::Internal(e.to_string()))?;
25 if combined.len() < 12 {
26 return Err(Error::Internal("Ciphertext too short".to_string()));
27 }
28
29 let cipher = Aes256Gcm::new_from_slice(key).map_err(|e| Error::Internal(e.to_string()))?;
30 let (nonce_bytes, ciphertext) = combined.split_at(12);
31 let nonce = Nonce::from_slice(nonce_bytes);
32
33 let plaintext_bytes = cipher.decrypt(nonce, ciphertext).map_err(|_| Error::Internal("decryption failed (wrong key or tampered data)".to_string()))?;
34 String::from_utf8(plaintext_bytes).map_err(|e| Error::Internal(e.to_string()))
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40
41 #[test]
42 fn test_encrypt_decrypt() {
43 let key = b"thisis32byteslongsecretkey123456";
44 let plaintext = "Hello, Celerix!";
45 let ciphertext = encrypt(plaintext, key).unwrap();
46 assert_ne!(ciphertext, plaintext);
47 let decrypted = decrypt(&ciphertext, key).unwrap();
48 assert_eq!(decrypted, plaintext);
49 }
50
51 #[test]
52 fn test_decrypt_with_wrong_key() {
53 let key1 = b"thisis32byteslongsecretkey123456";
54 let key2 = b"another32byteslongsecretkey65432";
55 let plaintext = "Secret message";
56 let ciphertext = encrypt(plaintext, key1).unwrap();
57 assert!(decrypt(&ciphertext, key2).is_err());
58 }
59}