sal_vault/symmetric/
implementation.rs1use chacha20poly1305::aead::Aead;
4use chacha20poly1305::{ChaCha20Poly1305, KeyInit, Nonce};
5use rand::{rngs::OsRng, RngCore};
6use serde::{Deserialize, Serialize};
7use sha2::{Digest, Sha256};
8
9use crate::error::CryptoError;
10use crate::keyspace::KeySpace;
11
12const NONCE_SIZE: usize = 12;
14
15pub fn generate_symmetric_key() -> [u8; 32] {
21 let mut key = [0u8; 32];
22 OsRng.fill_bytes(&mut key);
23 key
24}
25
26pub fn derive_key_from_password(password: &str) -> [u8; 32] {
36 let mut hasher = Sha256::default();
37 hasher.update(password.as_bytes());
38 let result = hasher.finalize();
39
40 let mut key = [0u8; 32];
41 key.copy_from_slice(&result);
42 key
43}
44
45pub fn encrypt_symmetric(key: &[u8], message: &[u8]) -> Result<Vec<u8>, CryptoError> {
60 let cipher =
62 ChaCha20Poly1305::new_from_slice(key).map_err(|_| CryptoError::InvalidKeyLength)?;
63
64 let mut nonce_bytes = [0u8; NONCE_SIZE];
66 OsRng.fill_bytes(&mut nonce_bytes);
67 let nonce = Nonce::from_slice(&nonce_bytes);
68
69 let ciphertext = cipher
71 .encrypt(nonce, message)
72 .map_err(|e| CryptoError::EncryptionFailed(e.to_string()))?;
73
74 let mut result = ciphertext;
76 result.extend_from_slice(&nonce_bytes);
77
78 Ok(result)
79}
80
81pub fn decrypt_symmetric(key: &[u8], ciphertext_with_nonce: &[u8]) -> Result<Vec<u8>, CryptoError> {
94 if ciphertext_with_nonce.len() <= NONCE_SIZE {
96 return Err(CryptoError::DecryptionFailed(
97 "Ciphertext too short".to_string(),
98 ));
99 }
100
101 let ciphertext_len = ciphertext_with_nonce.len() - NONCE_SIZE;
103 let ciphertext = &ciphertext_with_nonce[0..ciphertext_len];
104 let nonce_bytes = &ciphertext_with_nonce[ciphertext_len..];
105
106 let cipher =
108 ChaCha20Poly1305::new_from_slice(key).map_err(|_| CryptoError::InvalidKeyLength)?;
109
110 let nonce = Nonce::from_slice(nonce_bytes);
111
112 cipher
114 .decrypt(nonce, ciphertext)
115 .map_err(|e| CryptoError::DecryptionFailed(e.to_string()))
116}
117
118pub fn encrypt_with_key(key: &[u8], message: &[u8]) -> Result<Vec<u8>, CryptoError> {
130 encrypt_symmetric(key, message)
131}
132
133pub fn decrypt_with_key(key: &[u8], ciphertext_with_nonce: &[u8]) -> Result<Vec<u8>, CryptoError> {
145 decrypt_symmetric(key, ciphertext_with_nonce)
146}
147
148#[derive(Serialize, Deserialize, Debug)]
150pub struct EncryptedKeySpaceMetadata {
151 pub name: String,
152 pub created_at: u64,
153 pub last_accessed: u64,
154}
155
156#[derive(Serialize, Deserialize, Debug)]
158pub struct EncryptedKeySpace {
159 pub metadata: EncryptedKeySpaceMetadata,
160 pub encrypted_data: Vec<u8>,
161}
162
163pub fn encrypt_key_space(
175 space: &KeySpace,
176 password: &str,
177) -> Result<EncryptedKeySpace, CryptoError> {
178 let serialized = match serde_json::to_vec(space) {
180 Ok(data) => data,
181 Err(e) => {
182 log::error!("Serialization error during encryption: {}", e);
183 return Err(CryptoError::SerializationError(e.to_string()));
184 }
185 };
186
187 let key = derive_key_from_password(password);
189
190 let encrypted_data = encrypt_symmetric(&key, &serialized)?;
192
193 let now = std::time::SystemTime::now()
195 .duration_since(std::time::UNIX_EPOCH)
196 .unwrap_or_default()
197 .as_millis() as u64;
198 let metadata = EncryptedKeySpaceMetadata {
199 name: space.name.clone(),
200 created_at: now,
201 last_accessed: now,
202 };
203
204 Ok(EncryptedKeySpace {
205 metadata,
206 encrypted_data,
207 })
208}
209
210pub fn decrypt_key_space(
222 encrypted_space: &EncryptedKeySpace,
223 password: &str,
224) -> Result<KeySpace, CryptoError> {
225 let key = derive_key_from_password(password);
227
228 let decrypted_data = decrypt_symmetric(&key, &encrypted_space.encrypted_data)?;
230
231 let space: KeySpace = match serde_json::from_slice(&decrypted_data) {
233 Ok(space) => space,
234 Err(e) => {
235 log::error!("Deserialization error: {}", e);
236 return Err(CryptoError::SerializationError(e.to_string()));
237 }
238 };
239
240 Ok(space)
241}
242
243pub fn serialize_encrypted_space(
254 encrypted_space: &EncryptedKeySpace,
255) -> Result<String, CryptoError> {
256 serde_json::to_string(encrypted_space)
257 .map_err(|e| CryptoError::SerializationError(e.to_string()))
258}
259
260pub fn deserialize_encrypted_space(serialized: &str) -> Result<EncryptedKeySpace, CryptoError> {
271 match serde_json::from_str(serialized) {
272 Ok(space) => Ok(space),
273 Err(e) => {
274 log::error!("Error deserializing encrypted space: {}", e);
275 Err(CryptoError::SerializationError(e.to_string()))
276 }
277 }
278}