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