1use crate::dht::{DHT, DhtKey};
20use crate::identity::enhanced::EnhancedIdentity;
21use saorsa_pqc::{ChaCha20Poly1305Cipher, SymmetricKey};
22use serde::{Deserialize, Serialize};
23use sha2::{Digest, Sha256};
24use std::time::{Duration, SystemTime};
25use thiserror::Error;
26
27#[derive(Debug, Error)]
29pub enum StorageError {
30 #[error("DHT error: {0}")]
31 DhtError(String),
32
33 #[error("Encryption error: {0}")]
34 EncryptionError(String),
35
36 #[error("Serialization error: {0}")]
37 SerializationError(#[from] bincode::Error),
38
39 #[error("Key not found: {0}")]
40 KeyNotFound(String),
41
42 #[error("Invalid data format")]
43 InvalidFormat,
44}
45
46type Result<T> = std::result::Result<T, StorageError>;
47
48pub mod keys {
50 pub fn profile(user_id: &str) -> String {
52 format!("profile:{user_id}")
53 }
54
55 pub fn devices(user_id: &str) -> String {
57 format!("devices:{user_id}")
58 }
59
60 pub fn chat_channel(channel_id: &str) -> String {
62 format!("chat:channel:{channel_id}")
63 }
64
65 pub fn chat_message(channel_id: &str, msg_id: &str) -> String {
67 format!("chat:msg:{channel_id}:{msg_id}")
68 }
69
70 pub fn chat_index(channel_id: &str, timestamp: u64) -> String {
72 format!("chat:idx:{channel_id}:{timestamp}")
73 }
74
75 pub fn discuss_topic(topic_id: &str) -> String {
77 format!("discuss:topic:{topic_id}")
78 }
79
80 pub fn discuss_reply(topic_id: &str, reply_id: &str) -> String {
82 format!("discuss:reply:{topic_id}:{reply_id}")
83 }
84
85 pub fn project(project_id: &str) -> String {
87 format!("project:{project_id}")
88 }
89
90 pub fn document_meta(doc_id: &str) -> String {
92 format!("doc:meta:{doc_id}")
93 }
94
95 pub fn file_chunk(file_id: &str, chunk_num: u32) -> String {
97 format!("file:chunk:{file_id}:{chunk_num:08}")
98 }
99
100 pub fn organization(org_id: &str) -> String {
102 format!("org:{org_id}")
103 }
104
105 pub fn public_channel_list() -> String {
107 "public:channels".to_string()
108 }
109
110 pub fn user_channels(user_id: &str) -> String {
112 format!("user:channels:{user_id}")
113 }
114}
115
116pub mod ttl {
118 use std::time::Duration;
119
120 pub const PROFILE: Duration = Duration::from_secs(365 * 24 * 60 * 60); pub const MESSAGE: Duration = Duration::from_secs(90 * 24 * 60 * 60); pub const FILE_CHUNK: Duration = Duration::from_secs(365 * 24 * 60 * 60); pub const TEMP: Duration = Duration::from_secs(24 * 60 * 60); pub const PRESENCE: Duration = Duration::from_secs(5 * 60); }
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct EncryptedData {
139 pub ciphertext: Vec<u8>,
141
142 pub nonce: Vec<u8>,
144
145 pub key_id: String,
147
148 pub timestamp: SystemTime,
150
151 pub metadata: Option<serde_json::Value>,
153}
154
155pub struct StorageManager {
157 dht: DHT,
159
160 master_key: [u8; 32],
162}
163
164impl StorageManager {
165 pub fn new(dht: DHT, identity: &EnhancedIdentity) -> Result<Self> {
167 let mut hasher = Sha256::new();
169 hasher.update(identity.base_identity.user_id.as_bytes()); let master_key: [u8; 32] = hasher.finalize().into();
171
172 Ok(Self { dht, master_key })
173 }
174
175 pub async fn store_encrypted<T: Serialize>(
177 &mut self,
178 key: &str,
179 data: &T,
180 _ttl: Duration,
181 metadata: Option<serde_json::Value>,
182 ) -> Result<()> {
183 let plaintext = bincode::serialize(data)?;
185
186 let encrypted = self.encrypt(&plaintext)?;
188
189 let wrapper = EncryptedData {
191 ciphertext: encrypted.0,
192 nonce: encrypted.1,
193 key_id: "v1".to_string(),
194 timestamp: SystemTime::now(),
195 metadata,
196 };
197
198 let wrapper_bytes = bincode::serialize(&wrapper)?;
200
201 let hash = blake3::hash(key.as_bytes());
203 let dht_key = *hash.as_bytes();
204
205 self.dht
206 .store(&DhtKey::from_bytes(dht_key), wrapper_bytes)
207 .await
208 .map_err(|e| StorageError::DhtError(e.to_string()))?;
209
210 Ok(())
211 }
212
213 pub async fn get_encrypted<T: for<'de> Deserialize<'de>>(&self, key: &str) -> Result<T> {
215 let hash = blake3::hash(key.as_bytes());
217 let dht_key = *hash.as_bytes();
218 let value = self
219 .dht
220 .retrieve(&DhtKey::from_bytes(dht_key))
221 .await
222 .map_err(|e| StorageError::DhtError(e.to_string()))?
223 .ok_or_else(|| StorageError::KeyNotFound(key.to_string()))?;
224
225 let wrapper: EncryptedData = bincode::deserialize(&value)?;
227
228 let plaintext = self.decrypt(&wrapper.ciphertext, &wrapper.nonce)?;
230
231 let data = bincode::deserialize(&plaintext)?;
233
234 Ok(data)
235 }
236
237 pub async fn store_public<T: Serialize>(
239 &mut self,
240 key: &str,
241 data: &T,
242 _ttl: Duration,
243 ) -> Result<()> {
244 let value = bincode::serialize(data)?;
245
246 let hash = blake3::hash(key.as_bytes());
247 let dht_key = *hash.as_bytes();
248
249 self.dht
250 .store(&DhtKey::from_bytes(dht_key), value)
251 .await
252 .map_err(|e| StorageError::DhtError(e.to_string()))?;
253
254 Ok(())
255 }
256
257 pub async fn get_public<T: for<'de> Deserialize<'de>>(&self, key: &str) -> Result<T> {
259 let hash = blake3::hash(key.as_bytes());
260 let dht_key = *hash.as_bytes();
261 let value = self
262 .dht
263 .retrieve(&DhtKey::from_bytes(dht_key))
264 .await
265 .map_err(|e| StorageError::DhtError(e.to_string()))?
266 .ok_or_else(|| StorageError::KeyNotFound(key.to_string()))?;
267
268 let data = bincode::deserialize(&value)?;
269 Ok(data)
270 }
271
272 pub async fn delete(&mut self, key: &str) -> Result<()> {
274 let hash = blake3::hash(key.as_bytes());
276 let dht_key = *hash.as_bytes();
277 self.dht
278 .store(&DhtKey::from_bytes(dht_key), vec![])
279 .await
280 .map_err(|e| StorageError::DhtError(e.to_string()))?;
281 Ok(())
282 }
283
284 pub async fn list_keys(&self, _prefix: &str) -> Result<Vec<String>> {
286 Ok(vec![])
289 }
290
291 fn encrypt(&self, plaintext: &[u8]) -> Result<(Vec<u8>, Vec<u8>)> {
293 let sk = SymmetricKey::from_bytes(self.master_key);
294 let cipher = ChaCha20Poly1305Cipher::new(&sk);
295 let (ciphertext, nonce) = cipher
296 .encrypt(plaintext, None)
297 .map_err(|e| StorageError::EncryptionError(format!("{e}")))?;
298 Ok((ciphertext, nonce.to_vec()))
299 }
300
301 fn decrypt(&self, ciphertext: &[u8], nonce: &[u8]) -> Result<Vec<u8>> {
303 let sk = SymmetricKey::from_bytes(self.master_key);
304 let cipher = ChaCha20Poly1305Cipher::new(&sk);
305 let mut nonce_array = [0u8; 12];
306 nonce_array.copy_from_slice(&nonce[..12]);
307 cipher
308 .decrypt(ciphertext, &nonce_array, None)
309 .map_err(|e| StorageError::EncryptionError(format!("{e}")))
310 }
311}
312
313pub struct FileChunker {
391 chunk_size: usize,
392}
393
394impl FileChunker {
395 pub fn new(chunk_size: usize) -> Self {
397 Self { chunk_size }
398 }
399
400 pub fn chunk_file(&self, data: &[u8]) -> Vec<Vec<u8>> {
402 data.chunks(self.chunk_size)
403 .map(|chunk| chunk.to_vec())
404 .collect()
405 }
406
407 pub async fn store_file(
409 &self,
410 storage: &mut StorageManager,
411 file_id: &str,
412 data: &[u8],
413 metadata: FileMetadata,
414 ) -> Result<()> {
415 let chunks = self.chunk_file(data);
416 let total_chunks = chunks.len() as u32;
417
418 let meta_with_chunks = FileMetadata {
420 total_chunks,
421 ..metadata
422 };
423
424 let meta_key = keys::document_meta(file_id);
425 storage
426 .store_encrypted(&meta_key, &meta_with_chunks, ttl::FILE_CHUNK, None)
427 .await?;
428
429 for (i, chunk) in chunks.iter().enumerate() {
431 let chunk_key = keys::file_chunk(file_id, i as u32);
432 storage
433 .store_encrypted(&chunk_key, chunk, ttl::FILE_CHUNK, None)
434 .await?;
435 }
436
437 Ok(())
438 }
439
440 pub async fn get_file(&self, storage: &StorageManager, file_id: &str) -> Result<Vec<u8>> {
442 let meta_key = keys::document_meta(file_id);
444 let metadata: FileMetadata = storage.get_encrypted(&meta_key).await?;
445
446 let mut data = Vec::new();
448 for i in 0..metadata.total_chunks {
449 let chunk_key = keys::file_chunk(file_id, i);
450 let chunk: Vec<u8> = storage.get_encrypted(&chunk_key).await?;
451 data.extend(chunk);
452 }
453
454 Ok(data)
455 }
456}
457
458#[derive(Debug, Clone, Serialize, Deserialize)]
460pub struct FileMetadata {
461 pub file_id: String,
462 pub name: String,
463 pub size: u64,
464 pub mime_type: String,
465 pub hash: Vec<u8>,
466 pub total_chunks: u32,
467 pub created_at: SystemTime,
468 pub created_by: String,
469}