use chacha20poly1305::aead::Aead;
use chacha20poly1305::{ChaCha20Poly1305, KeyInit, Nonce};
use rand::{rngs::OsRng, RngCore};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use crate::error::CryptoError;
use crate::keyspace::KeySpace;
const NONCE_SIZE: usize = 12;
pub fn generate_symmetric_key() -> [u8; 32] {
let mut key = [0u8; 32];
OsRng.fill_bytes(&mut key);
key
}
pub fn derive_key_from_password(password: &str) -> [u8; 32] {
let mut hasher = Sha256::default();
hasher.update(password.as_bytes());
let result = hasher.finalize();
let mut key = [0u8; 32];
key.copy_from_slice(&result);
key
}
pub fn encrypt_symmetric(key: &[u8], message: &[u8]) -> Result<Vec<u8>, CryptoError> {
let cipher =
ChaCha20Poly1305::new_from_slice(key).map_err(|_| CryptoError::InvalidKeyLength)?;
let mut nonce_bytes = [0u8; NONCE_SIZE];
OsRng.fill_bytes(&mut nonce_bytes);
let nonce = Nonce::from_slice(&nonce_bytes);
let ciphertext = cipher
.encrypt(nonce, message)
.map_err(|e| CryptoError::EncryptionFailed(e.to_string()))?;
let mut result = ciphertext;
result.extend_from_slice(&nonce_bytes);
Ok(result)
}
pub fn decrypt_symmetric(key: &[u8], ciphertext_with_nonce: &[u8]) -> Result<Vec<u8>, CryptoError> {
if ciphertext_with_nonce.len() <= NONCE_SIZE {
return Err(CryptoError::DecryptionFailed(
"Ciphertext too short".to_string(),
));
}
let ciphertext_len = ciphertext_with_nonce.len() - NONCE_SIZE;
let ciphertext = &ciphertext_with_nonce[0..ciphertext_len];
let nonce_bytes = &ciphertext_with_nonce[ciphertext_len..];
let cipher =
ChaCha20Poly1305::new_from_slice(key).map_err(|_| CryptoError::InvalidKeyLength)?;
let nonce = Nonce::from_slice(nonce_bytes);
cipher
.decrypt(nonce, ciphertext)
.map_err(|e| CryptoError::DecryptionFailed(e.to_string()))
}
pub fn encrypt_with_key(key: &[u8], message: &[u8]) -> Result<Vec<u8>, CryptoError> {
encrypt_symmetric(key, message)
}
pub fn decrypt_with_key(key: &[u8], ciphertext_with_nonce: &[u8]) -> Result<Vec<u8>, CryptoError> {
decrypt_symmetric(key, ciphertext_with_nonce)
}
#[derive(Serialize, Deserialize, Debug)]
pub struct EncryptedKeySpaceMetadata {
pub name: String,
pub created_at: u64,
pub last_accessed: u64,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct EncryptedKeySpace {
pub metadata: EncryptedKeySpaceMetadata,
pub encrypted_data: Vec<u8>,
}
pub fn encrypt_key_space(
space: &KeySpace,
password: &str,
) -> Result<EncryptedKeySpace, CryptoError> {
let serialized = match serde_json::to_vec(space) {
Ok(data) => data,
Err(e) => {
log::error!("Serialization error during encryption: {}", e);
return Err(CryptoError::SerializationError(e.to_string()));
}
};
let key = derive_key_from_password(password);
let encrypted_data = encrypt_symmetric(&key, &serialized)?;
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64;
let metadata = EncryptedKeySpaceMetadata {
name: space.name.clone(),
created_at: now,
last_accessed: now,
};
Ok(EncryptedKeySpace {
metadata,
encrypted_data,
})
}
pub fn decrypt_key_space(
encrypted_space: &EncryptedKeySpace,
password: &str,
) -> Result<KeySpace, CryptoError> {
let key = derive_key_from_password(password);
let decrypted_data = decrypt_symmetric(&key, &encrypted_space.encrypted_data)?;
let space: KeySpace = match serde_json::from_slice(&decrypted_data) {
Ok(space) => space,
Err(e) => {
log::error!("Deserialization error: {}", e);
return Err(CryptoError::SerializationError(e.to_string()));
}
};
Ok(space)
}
pub fn serialize_encrypted_space(
encrypted_space: &EncryptedKeySpace,
) -> Result<String, CryptoError> {
serde_json::to_string(encrypted_space)
.map_err(|e| CryptoError::SerializationError(e.to_string()))
}
pub fn deserialize_encrypted_space(serialized: &str) -> Result<EncryptedKeySpace, CryptoError> {
match serde_json::from_str(serialized) {
Ok(space) => Ok(space),
Err(e) => {
log::error!("Error deserializing encrypted space: {}", e);
Err(CryptoError::SerializationError(e.to_string()))
}
}
}