cloud_disk_sync/encryption/
mod.rs

1pub mod types;
2
3use crate::config::EncryptionConfig;
4use crate::encryption::types::EncryptionAlgorithm;
5use crate::error::EncryptionError;
6use aes_gcm::aead::{Aead, Nonce};
7use aes_gcm::{Aes256Gcm, KeyInit};
8use std::collections::HashMap;
9use std::path::{Path, PathBuf};
10
11pub struct EncryptionManager {
12    key_store: HashMap<String, Vec<u8>>,
13}
14type Hmac = Vec<u8>;
15
16pub struct EncryptionMetadata {
17    pub algorithm: EncryptionAlgorithm,
18    pub key_id: String,
19    pub nonce: Vec<u8>,
20    pub hmac: Hmac,
21}
22
23impl EncryptionManager {
24    pub fn new() -> Self {
25        Self {
26            key_store: HashMap::new(),
27        }
28    }
29
30    fn get_key(&self, key_id: &str) -> Result<Vec<u8>, EncryptionError> {
31        self.key_store
32            .get(key_id)
33            .cloned()
34            .ok_or_else(|| EncryptionError::KeyNotFound(key_id.to_string()))
35    }
36
37    fn create_temp_path(&self) -> Result<PathBuf, EncryptionError> {
38        let name = format!("enc_{}.tmp", uuid::Uuid::new_v4());
39        let path = std::env::temp_dir().join(name);
40        Ok(path)
41    }
42
43    pub async fn encrypt_file(
44        &self,
45        path: &Path,
46        config: &EncryptionConfig,
47    ) -> Result<(Option<PathBuf>, Option<EncryptionMetadata>), EncryptionError> {
48        let key = self.get_key(&config.key_id)?;
49        let cipher = Aes256Gcm::new(aes_gcm::Key::<Aes256Gcm>::from_slice(&key));
50
51        // 生成随机nonce
52        let mut nonce_bytes = [0u8; 12];
53        for b in nonce_bytes.iter_mut() {
54            *b = rand::random();
55        }
56        let nonce = aes_gcm::Nonce::from_slice(&nonce_bytes);
57
58        // 读取文件
59        let data = tokio::fs::read(path)
60            .await
61            .map_err(|e| EncryptionError::InvalidData)?;
62
63        // 加密数据
64        let ciphertext = cipher
65            .encrypt(nonce, data.as_ref())
66            .map_err(|e| EncryptionError::EncryptionFailed(e.to_string()))?;
67
68        // 创建临时加密文件
69        let temp_path = self.create_temp_path()?;
70        let mut file = tokio::fs::File::create(&temp_path)
71            .await
72            .map_err(|_| EncryptionError::InvalidData)?;
73
74        // 写入nonce和密文
75        use tokio::io::AsyncWriteExt;
76        file.write_all(&nonce_bytes)
77            .await
78            .map_err(|_| EncryptionError::InvalidData)?;
79        file.write_all(&ciphertext)
80            .await
81            .map_err(|_| EncryptionError::InvalidData)?;
82
83        let metadata = EncryptionMetadata {
84            algorithm: config.algorithm.clone(),
85            key_id: config.key_id.clone(),
86            nonce: nonce_bytes.to_vec(),
87            hmac: self.calculate_hmac(&ciphertext),
88        };
89
90        Ok((Some(temp_path), Some(metadata)))
91    }
92
93    pub async fn decrypt_file(
94        &self,
95        encrypted_path: &Path,
96        metadata: &EncryptionMetadata,
97    ) -> Result<PathBuf, EncryptionError> {
98        let key = self.get_key(&metadata.key_id)?;
99        let cipher = Aes256Gcm::new(aes_gcm::Key::<Aes256Gcm>::from_slice(&key));
100
101        // 读取加密文件
102        let data = tokio::fs::read(encrypted_path)
103            .await
104            .map_err(|_| EncryptionError::InvalidData)?;
105
106        if data.len() < 12 {
107            return Err(EncryptionError::InvalidData);
108        }
109
110        let (nonce_bytes, ciphertext) = data.split_at(12);
111        let nonce = aes_gcm::Nonce::from_slice(nonce_bytes);
112
113        // 解密数据
114        let plaintext = cipher
115            .decrypt(nonce, ciphertext)
116            .map_err(|e| EncryptionError::DecryptionFailed(e.to_string()))?;
117
118        // 验证HMAC
119        if metadata.hmac != self.calculate_hmac(ciphertext) {
120            return Err(EncryptionError::IntegrityCheckFailed);
121        }
122
123        // 写入解密文件
124        let temp_path = self.create_temp_path()?;
125        tokio::fs::write(&temp_path, &plaintext)
126            .await
127            .map_err(|_| EncryptionError::InvalidData)?;
128
129        Ok(temp_path)
130    }
131
132    fn calculate_hmac(&self, ciphertext: &[u8]) -> Hmac {
133        use sha2::{Digest, Sha256};
134        let mut hasher = Sha256::new();
135        hasher.update(ciphertext);
136        hasher.finalize().to_vec()
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143    use crate::config::EncryptionConfig;
144    use crate::encryption::types::{EncryptionAlgorithm, IvMode};
145
146    #[tokio::test]
147    async fn test_hmac_and_encrypt_decrypt() {
148        let mut mgr = EncryptionManager::new();
149        mgr.key_store.insert("test".to_string(), vec![0u8; 32]);
150        let tmp = std::env::temp_dir().join("enc_test.bin");
151        tokio::fs::write(&tmp, b"hello").await.unwrap();
152        let cfg = EncryptionConfig {
153            algorithm: EncryptionAlgorithm::Aes256Gcm,
154            key_id: "test".to_string(),
155            iv_mode: IvMode::Random,
156        };
157        let (enc_path_opt, metadata_opt) = mgr.encrypt_file(&tmp, &cfg).await.unwrap();
158        assert!(enc_path_opt.is_some());
159        assert!(metadata_opt.is_some());
160        let enc_path = enc_path_opt.unwrap();
161        let metadata = metadata_opt.unwrap();
162        let dec_path = mgr.decrypt_file(&enc_path, &metadata).await.unwrap();
163        let dec = tokio::fs::read(&dec_path).await.unwrap();
164        assert_eq!(dec, b"hello");
165    }
166}