zephyrfs/zephyrfs-node / 585cb7b

Browse files

Add Phase 2 crypto foundation: AES-256-GCM encryption, scrypt key derivation, hierarchical keys, secure memory, content addressing with Blake3/SHA-256

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
585cb7bd0208db3cd0f0f8aa07d7a42057bb1eaf
Parents
4588949
Tree
fa6e0d5

8 changed files

StatusFile+-
M Cargo.toml 11 0
A src/crypto/content_addressing.rs 489 0
A src/crypto/encryption.rs 360 0
A src/crypto/key_derivation.rs 457 0
A src/crypto/mod.rs 213 0
A src/crypto/secure_memory.rs 461 0
A src/lib.rs 20 0
M src/main.rs 7 2
Cargo.tomlmodified
@@ -37,6 +37,17 @@ sha2 = "0.10"
37
 rand = "0.8"
37
 rand = "0.8"
38
 hex = "0.4"
38
 hex = "0.4"
39
 
39
 
40
+# Phase 2: Advanced cryptography
41
+ring = "0.17"              # AES-256-GCM, SHA-256, secure crypto primitives
42
+scrypt = "0.11"            # Key derivation from passwords
43
+zeroize = "1.7"            # Secure memory clearing
44
+constant_time_eq = "0.3"   # Timing-safe comparisons
45
+rand_core = "0.6"          # Cryptographic randomness
46
+
47
+# TLS and networking security
48
+rustls = "0.23"            # TLS 1.3 implementation
49
+rustls-pemfile = "2.0"     # Certificate management
50
+
40
 # Protocol buffers
51
 # Protocol buffers
41
 prost = "0.13"
52
 prost = "0.13"
42
 prost-types = "0.13"
53
 prost-types = "0.13"
src/crypto/content_addressing.rsadded
@@ -0,0 +1,489 @@
1
+//! Content addressing system for ZephyrFS
2
+//!
3
+//! Provides cryptographic content identifiers and verification using Blake3 and SHA-256.
4
+//! Content IDs are used for integrity verification and deduplication.
5
+
6
+use crate::crypto::{ContentHasher, VerificationHasher};
7
+use blake3::Hasher as Blake3Hasher;
8
+use serde::{Deserialize, Serialize};
9
+use sha2::{Digest, Sha256};
10
+use std::fmt;
11
+
12
+/// Content identifier - cryptographic hash of data
13
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
14
+pub struct ContentId {
15
+    /// Hash algorithm used
16
+    pub algorithm: HashAlgorithm,
17
+    /// Hash bytes
18
+    pub hash: Vec<u8>,
19
+}
20
+
21
+/// Supported hash algorithms
22
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
23
+pub enum HashAlgorithm {
24
+    Blake3,
25
+    Sha256,
26
+}
27
+
28
+impl ContentId {
29
+    /// Generate content ID from data
30
+    pub fn generate(data: &[u8], hasher: &ContentHasher) -> Self {
31
+        match hasher {
32
+            ContentHasher::Blake3 => {
33
+                let mut hasher = Blake3Hasher::new();
34
+                hasher.update(data);
35
+                let hash = hasher.finalize();
36
+                
37
+                Self {
38
+                    algorithm: HashAlgorithm::Blake3,
39
+                    hash: hash.as_bytes().to_vec(),
40
+                }
41
+            }
42
+            ContentHasher::Sha256 => {
43
+                let mut hasher = Sha256::new();
44
+                hasher.update(data);
45
+                let hash = hasher.finalize();
46
+                
47
+                Self {
48
+                    algorithm: HashAlgorithm::Sha256,
49
+                    hash: hash.to_vec(),
50
+                }
51
+            }
52
+        }
53
+    }
54
+
55
+    /// Create from existing hash bytes and algorithm
56
+    pub fn from_hash(algorithm: HashAlgorithm, hash: Vec<u8>) -> Self {
57
+        Self { algorithm, hash }
58
+    }
59
+
60
+    /// Get hash bytes
61
+    pub fn hash_bytes(&self) -> &[u8] {
62
+        &self.hash
63
+    }
64
+
65
+    /// Get algorithm used
66
+    pub fn algorithm(&self) -> &HashAlgorithm {
67
+        &self.algorithm
68
+    }
69
+
70
+    /// Convert to hex string representation
71
+    pub fn to_hex(&self) -> String {
72
+        hex::encode(&self.hash)
73
+    }
74
+
75
+    /// Create from hex string
76
+    pub fn from_hex(algorithm: HashAlgorithm, hex_str: &str) -> Result<Self, hex::FromHexError> {
77
+        let hash = hex::decode(hex_str)?;
78
+        Ok(Self { algorithm, hash })
79
+    }
80
+
81
+    /// Get expected hash length for algorithm
82
+    pub fn expected_length(&self) -> usize {
83
+        match self.algorithm {
84
+            HashAlgorithm::Blake3 => 32,  // Blake3 output is 32 bytes
85
+            HashAlgorithm::Sha256 => 32,  // SHA-256 output is 32 bytes
86
+        }
87
+    }
88
+
89
+    /// Validate hash length matches algorithm
90
+    pub fn is_valid(&self) -> bool {
91
+        self.hash.len() == self.expected_length()
92
+    }
93
+
94
+    /// Create a multihash-style prefix for the content ID
95
+    pub fn multihash_prefix(&self) -> Vec<u8> {
96
+        let mut prefix = Vec::new();
97
+        
98
+        // Add algorithm identifier
99
+        match self.algorithm {
100
+            HashAlgorithm::Blake3 => {
101
+                prefix.push(0x1e); // Blake3 multicodec
102
+                prefix.push(32);   // Hash length
103
+            }
104
+            HashAlgorithm::Sha256 => {
105
+                prefix.push(0x12); // SHA-256 multicodec
106
+                prefix.push(32);   // Hash length
107
+            }
108
+        }
109
+        
110
+        prefix.extend_from_slice(&self.hash);
111
+        prefix
112
+    }
113
+
114
+    /// Parse from multihash format
115
+    pub fn from_multihash(data: &[u8]) -> Result<Self, String> {
116
+        if data.len() < 2 {
117
+            return Err("Multihash too short".to_string());
118
+        }
119
+
120
+        let algorithm = match data[0] {
121
+            0x1e => HashAlgorithm::Blake3,
122
+            0x12 => HashAlgorithm::Sha256,
123
+            _ => return Err(format!("Unsupported hash algorithm: {}", data[0])),
124
+        };
125
+
126
+        let expected_len = data[1] as usize;
127
+        if data.len() != expected_len + 2 {
128
+            return Err(format!(
129
+                "Invalid multihash length: expected {}, got {}",
130
+                expected_len + 2,
131
+                data.len()
132
+            ));
133
+        }
134
+
135
+        let hash = data[2..].to_vec();
136
+        let content_id = Self { algorithm, hash };
137
+
138
+        if !content_id.is_valid() {
139
+            return Err("Invalid hash length for algorithm".to_string());
140
+        }
141
+
142
+        Ok(content_id)
143
+    }
144
+}
145
+
146
+impl fmt::Display for ContentId {
147
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148
+        write!(f, "{}:{}", 
149
+            match self.algorithm {
150
+                HashAlgorithm::Blake3 => "blake3",
151
+                HashAlgorithm::Sha256 => "sha256",
152
+            },
153
+            self.to_hex()
154
+        )
155
+    }
156
+}
157
+
158
+/// Content verification utilities
159
+pub struct ContentVerifier;
160
+
161
+impl ContentVerifier {
162
+    /// Verify data matches expected content ID
163
+    pub fn verify(data: &[u8], expected_id: &ContentId, hasher: &VerificationHasher) -> bool {
164
+        let computed_id = match hasher {
165
+            VerificationHasher::Blake3 => ContentId::generate(data, &ContentHasher::Blake3),
166
+            VerificationHasher::Sha256 => ContentId::generate(data, &ContentHasher::Sha256),
167
+        };
168
+
169
+        // Must use same algorithm and hash must match
170
+        computed_id.algorithm == expected_id.algorithm && 
171
+        computed_id.hash == expected_id.hash
172
+    }
173
+
174
+    /// Verify data with multiple hash algorithms (for maximum security)
175
+    pub fn verify_multi(data: &[u8], expected_ids: &[ContentId]) -> bool {
176
+        for expected_id in expected_ids {
177
+            let hasher = match expected_id.algorithm {
178
+                HashAlgorithm::Blake3 => VerificationHasher::Blake3,
179
+                HashAlgorithm::Sha256 => VerificationHasher::Sha256,
180
+            };
181
+
182
+            if !Self::verify(data, expected_id, &hasher) {
183
+                return false;
184
+            }
185
+        }
186
+        true
187
+    }
188
+
189
+    /// Compute multiple hashes for data (Blake3 + SHA-256 for maximum security)
190
+    pub fn compute_multi_hash(data: &[u8]) -> Vec<ContentId> {
191
+        vec![
192
+            ContentId::generate(data, &ContentHasher::Blake3),
193
+            ContentId::generate(data, &ContentHasher::Sha256),
194
+        ]
195
+    }
196
+
197
+    /// Create integrity proof for data chunk
198
+    pub fn create_integrity_proof(data: &[u8]) -> IntegrityProof {
199
+        IntegrityProof {
200
+            blake3_hash: ContentId::generate(data, &ContentHasher::Blake3),
201
+            sha256_hash: ContentId::generate(data, &ContentHasher::Sha256),
202
+            data_length: data.len() as u64,
203
+        }
204
+    }
205
+
206
+    /// Verify data against integrity proof
207
+    pub fn verify_integrity_proof(data: &[u8], proof: &IntegrityProof) -> bool {
208
+        if data.len() as u64 != proof.data_length {
209
+            return false;
210
+        }
211
+
212
+        let blake3_ok = Self::verify(data, &proof.blake3_hash, &VerificationHasher::Blake3);
213
+        let sha256_ok = Self::verify(data, &proof.sha256_hash, &VerificationHasher::Sha256);
214
+
215
+        blake3_ok && sha256_ok
216
+    }
217
+}
218
+
219
+/// Integrity proof containing multiple hashes for maximum security
220
+#[derive(Debug, Clone, Serialize, Deserialize)]
221
+pub struct IntegrityProof {
222
+    pub blake3_hash: ContentId,
223
+    pub sha256_hash: ContentId,
224
+    pub data_length: u64,
225
+}
226
+
227
+impl IntegrityProof {
228
+    /// Get the primary content ID (Blake3 for performance)
229
+    pub fn primary_id(&self) -> &ContentId {
230
+        &self.blake3_hash
231
+    }
232
+
233
+    /// Get the secondary content ID (SHA-256 for compatibility)
234
+    pub fn secondary_id(&self) -> &ContentId {
235
+        &self.sha256_hash
236
+    }
237
+
238
+    /// Convert to multihash format (primary hash only)
239
+    pub fn to_multihash(&self) -> Vec<u8> {
240
+        self.blake3_hash.multihash_prefix()
241
+    }
242
+}
243
+
244
+/// Content addressing utilities for file chunks
245
+pub struct ChunkAddressing;
246
+
247
+impl ChunkAddressing {
248
+    /// Generate content ID for a file chunk with metadata
249
+    pub fn chunk_id(chunk_data: &[u8], chunk_index: u64, file_id: &[u8]) -> ContentId {
250
+        let mut hasher = Blake3Hasher::new();
251
+        
252
+        // Include chunk metadata in hash for uniqueness
253
+        hasher.update(b"ZephyrFS-chunk-v1:");
254
+        hasher.update(&chunk_index.to_be_bytes());
255
+        hasher.update(b":");
256
+        hasher.update(file_id);
257
+        hasher.update(b":");
258
+        hasher.update(chunk_data);
259
+        
260
+        let hash = hasher.finalize();
261
+        ContentId {
262
+            algorithm: HashAlgorithm::Blake3,
263
+            hash: hash.as_bytes().to_vec(),
264
+        }
265
+    }
266
+
267
+    /// Generate file-level content ID from chunk IDs
268
+    pub fn file_id_from_chunks(chunk_ids: &[ContentId]) -> ContentId {
269
+        let mut hasher = Blake3Hasher::new();
270
+        hasher.update(b"ZephyrFS-file-v1:");
271
+        
272
+        for chunk_id in chunk_ids {
273
+            hasher.update(&chunk_id.hash);
274
+        }
275
+        
276
+        let hash = hasher.finalize();
277
+        ContentId {
278
+            algorithm: HashAlgorithm::Blake3,
279
+            hash: hash.as_bytes().to_vec(),
280
+        }
281
+    }
282
+
283
+    /// Create Merkle tree root from chunk hashes
284
+    pub fn merkle_root(chunk_ids: &[ContentId]) -> ContentId {
285
+        if chunk_ids.is_empty() {
286
+            // Empty file hash
287
+            return ContentId::generate(b"", &ContentHasher::Blake3);
288
+        }
289
+
290
+        if chunk_ids.len() == 1 {
291
+            return chunk_ids[0].clone();
292
+        }
293
+
294
+        // Build Merkle tree bottom-up
295
+        let mut level: Vec<ContentId> = chunk_ids.to_vec();
296
+        
297
+        while level.len() > 1 {
298
+            let mut next_level = Vec::new();
299
+            
300
+            for pair in level.chunks(2) {
301
+                let mut hasher = Blake3Hasher::new();
302
+                hasher.update(b"ZephyrFS-merkle-v1:");
303
+                hasher.update(&pair[0].hash);
304
+                
305
+                if pair.len() == 2 {
306
+                    hasher.update(&pair[1].hash);
307
+                } else {
308
+                    // Odd number of nodes - hash with itself
309
+                    hasher.update(&pair[0].hash);
310
+                }
311
+                
312
+                let hash = hasher.finalize();
313
+                next_level.push(ContentId {
314
+                    algorithm: HashAlgorithm::Blake3,
315
+                    hash: hash.as_bytes().to_vec(),
316
+                });
317
+            }
318
+            
319
+            level = next_level;
320
+        }
321
+        
322
+        level.into_iter().next().unwrap()
323
+    }
324
+}
325
+
326
+#[cfg(test)]
327
+mod tests {
328
+    use super::*;
329
+
330
+    #[test]
331
+    fn test_content_id_generation() {
332
+        let data = b"Hello, ZephyrFS!";
333
+        
334
+        let blake3_id = ContentId::generate(data, &ContentHasher::Blake3);
335
+        let sha256_id = ContentId::generate(data, &ContentHasher::Sha256);
336
+
337
+        assert_eq!(blake3_id.algorithm, HashAlgorithm::Blake3);
338
+        assert_eq!(sha256_id.algorithm, HashAlgorithm::Sha256);
339
+        assert_eq!(blake3_id.hash.len(), 32);
340
+        assert_eq!(sha256_id.hash.len(), 32);
341
+        assert_ne!(blake3_id.hash, sha256_id.hash);
342
+    }
343
+
344
+    #[test]
345
+    fn test_content_verification() {
346
+        let data = b"Test data for verification";
347
+        let content_id = ContentId::generate(data, &ContentHasher::Blake3);
348
+
349
+        // Correct data should verify
350
+        assert!(ContentVerifier::verify(data, &content_id, &VerificationHasher::Blake3));
351
+
352
+        // Wrong data should not verify
353
+        let wrong_data = b"Wrong data";
354
+        assert!(!ContentVerifier::verify(wrong_data, &content_id, &VerificationHasher::Blake3));
355
+    }
356
+
357
+    #[test]
358
+    fn test_content_id_hex_serialization() {
359
+        let data = b"Serialization test";
360
+        let original_id = ContentId::generate(data, &ContentHasher::Blake3);
361
+        
362
+        let hex_str = original_id.to_hex();
363
+        let restored_id = ContentId::from_hex(HashAlgorithm::Blake3, &hex_str).unwrap();
364
+        
365
+        assert_eq!(original_id, restored_id);
366
+    }
367
+
368
+    #[test]
369
+    fn test_multihash_format() {
370
+        let data = b"Multihash test data";
371
+        let content_id = ContentId::generate(data, &ContentHasher::Blake3);
372
+        
373
+        let multihash = content_id.multihash_prefix();
374
+        let restored_id = ContentId::from_multihash(&multihash).unwrap();
375
+        
376
+        assert_eq!(content_id, restored_id);
377
+        
378
+        // Test SHA-256 as well
379
+        let sha256_id = ContentId::generate(data, &ContentHasher::Sha256);
380
+        let sha256_multihash = sha256_id.multihash_prefix();
381
+        let restored_sha256 = ContentId::from_multihash(&sha256_multihash).unwrap();
382
+        
383
+        assert_eq!(sha256_id, restored_sha256);
384
+    }
385
+
386
+    #[test]
387
+    fn test_integrity_proof() {
388
+        let data = b"Integrity proof test data";
389
+        let proof = ContentVerifier::create_integrity_proof(data);
390
+        
391
+        assert_eq!(proof.data_length, data.len() as u64);
392
+        assert_eq!(proof.blake3_hash.algorithm, HashAlgorithm::Blake3);
393
+        assert_eq!(proof.sha256_hash.algorithm, HashAlgorithm::Sha256);
394
+        
395
+        // Correct data should verify
396
+        assert!(ContentVerifier::verify_integrity_proof(data, &proof));
397
+        
398
+        // Wrong data should not verify
399
+        let wrong_data = b"Wrong data";
400
+        assert!(!ContentVerifier::verify_integrity_proof(wrong_data, &proof));
401
+    }
402
+
403
+    #[test]
404
+    fn test_chunk_addressing() {
405
+        let chunk_data = b"Chunk data for addressing test";
406
+        let file_id = b"test_file_12345";
407
+        
408
+        let chunk_id = ChunkAddressing::chunk_id(chunk_data, 0, file_id);
409
+        assert_eq!(chunk_id.algorithm, HashAlgorithm::Blake3);
410
+        assert_eq!(chunk_id.hash.len(), 32);
411
+        
412
+        // Different chunk index should produce different ID
413
+        let chunk_id2 = ChunkAddressing::chunk_id(chunk_data, 1, file_id);
414
+        assert_ne!(chunk_id, chunk_id2);
415
+        
416
+        // Different file ID should produce different ID
417
+        let chunk_id3 = ChunkAddressing::chunk_id(chunk_data, 0, b"different_file");
418
+        assert_ne!(chunk_id, chunk_id3);
419
+    }
420
+
421
+    #[test]
422
+    fn test_file_id_from_chunks() {
423
+        let chunk1 = ContentId::generate(b"chunk 1", &ContentHasher::Blake3);
424
+        let chunk2 = ContentId::generate(b"chunk 2", &ContentHasher::Blake3);
425
+        let chunk3 = ContentId::generate(b"chunk 3", &ContentHasher::Blake3);
426
+        
427
+        let file_id = ChunkAddressing::file_id_from_chunks(&[chunk1.clone(), chunk2.clone(), chunk3.clone()]);
428
+        assert_eq!(file_id.algorithm, HashAlgorithm::Blake3);
429
+        
430
+        // Different order should produce different file ID
431
+        let file_id2 = ChunkAddressing::file_id_from_chunks(&[chunk3, chunk1, chunk2]);
432
+        assert_ne!(file_id, file_id2);
433
+    }
434
+
435
+    #[test]
436
+    fn test_merkle_root() {
437
+        let chunk1 = ContentId::generate(b"chunk 1", &ContentHasher::Blake3);
438
+        let chunk2 = ContentId::generate(b"chunk 2", &ContentHasher::Blake3);
439
+        let chunk3 = ContentId::generate(b"chunk 3", &ContentHasher::Blake3);
440
+        let chunk4 = ContentId::generate(b"chunk 4", &ContentHasher::Blake3);
441
+        
442
+        // Single chunk
443
+        let root1 = ChunkAddressing::merkle_root(&[chunk1.clone()]);
444
+        assert_eq!(root1, chunk1);
445
+        
446
+        // Multiple chunks
447
+        let root2 = ChunkAddressing::merkle_root(&[chunk1.clone(), chunk2.clone()]);
448
+        let root4 = ChunkAddressing::merkle_root(&[chunk1, chunk2, chunk3, chunk4]);
449
+        
450
+        assert_ne!(root2, root4);
451
+        assert_eq!(root2.algorithm, HashAlgorithm::Blake3);
452
+        assert_eq!(root4.algorithm, HashAlgorithm::Blake3);
453
+        
454
+        // Empty chunks
455
+        let empty_root = ChunkAddressing::merkle_root(&[]);
456
+        assert_eq!(empty_root.algorithm, HashAlgorithm::Blake3);
457
+    }
458
+
459
+    #[test]
460
+    fn test_content_id_display() {
461
+        let content_id = ContentId::generate(b"display test", &ContentHasher::Blake3);
462
+        let display_str = format!("{}", content_id);
463
+        
464
+        assert!(display_str.starts_with("blake3:"));
465
+        assert_eq!(display_str.len(), 71); // "blake3:" (7) + hex hash (64)
466
+        
467
+        let sha256_id = ContentId::generate(b"display test", &ContentHasher::Sha256);
468
+        let sha256_str = format!("{}", sha256_id);
469
+        
470
+        assert!(sha256_str.starts_with("sha256:"));
471
+        assert_eq!(sha256_str.len(), 71); // "sha256:" (7) + hex hash (64)
472
+    }
473
+
474
+    #[test]
475
+    fn test_multi_hash_verification() {
476
+        let data = b"Multi hash verification test";
477
+        let blake3_id = ContentId::generate(data, &ContentHasher::Blake3);
478
+        let sha256_id = ContentId::generate(data, &ContentHasher::Sha256);
479
+        
480
+        let ids = vec![blake3_id, sha256_id];
481
+        
482
+        // Correct data should verify against all hashes
483
+        assert!(ContentVerifier::verify_multi(data, &ids));
484
+        
485
+        // Wrong data should fail verification
486
+        let wrong_data = b"Wrong data";
487
+        assert!(!ContentVerifier::verify_multi(wrong_data, &ids));
488
+    }
489
+}
src/crypto/encryption.rsadded
@@ -0,0 +1,360 @@
1
+//! AES-256-GCM encryption implementation for ZephyrFS
2
+//! 
3
+//! Provides segment-level encryption using AES-256 in GCM mode for authenticated encryption.
4
+//! Each file segment is encrypted with its own derived key for maximum security isolation.
5
+
6
+use crate::crypto::{AesParams, KeyHierarchy, SecureBytes};
7
+use anyhow::{Context, Result};
8
+use ring::aead::{Aad, BoundKey, Nonce, NonceSequence, OpeningKey, SealingKey, UnboundKey, AES_256_GCM};
9
+use ring::error::Unspecified;
10
+use ring::rand::{SecureRandom, SystemRandom};
11
+use serde::{Deserialize, Serialize};
12
+use zeroize::{Zeroize, ZeroizeOnDrop};
13
+
14
+/// Default segment size for file encryption (1MB)
15
+pub const DEFAULT_SEGMENT_SIZE: usize = 1024 * 1024; // 1MB
16
+
17
+/// Encrypted data container with all necessary metadata for decryption
18
+#[derive(Debug, Clone, Serialize, Deserialize)]
19
+pub struct EncryptedData {
20
+    /// Segment index in the original file
21
+    pub segment_index: u64,
22
+    /// Encrypted data payload
23
+    pub ciphertext: Vec<u8>,
24
+    /// Nonce used for this encryption
25
+    pub nonce: [u8; 12],
26
+    /// Additional authenticated data (AAD) - segment metadata
27
+    pub aad: Vec<u8>,
28
+    /// Key derivation path for this segment
29
+    pub key_path: Vec<u32>,
30
+}
31
+
32
+/// File-level encryption operations
33
+pub struct FileEncryption {
34
+    params: AesParams,
35
+    segment_size: usize,
36
+    rng: SystemRandom,
37
+}
38
+
39
+impl FileEncryption {
40
+    pub fn new(params: &AesParams) -> Self {
41
+        Self {
42
+            params: params.clone(),
43
+            segment_size: DEFAULT_SEGMENT_SIZE,
44
+            rng: SystemRandom::new(),
45
+        }
46
+    }
47
+
48
+    pub fn with_segment_size(params: &AesParams, segment_size: usize) -> Self {
49
+        Self {
50
+            params: params.clone(),
51
+            segment_size,
52
+            rng: SystemRandom::new(),
53
+        }
54
+    }
55
+
56
+    /// Encrypt file data into multiple encrypted segments
57
+    pub fn encrypt_to_segments(
58
+        &self,
59
+        file_data: &[u8],
60
+        key_hierarchy: &KeyHierarchy,
61
+    ) -> Result<Vec<EncryptedData>> {
62
+        let mut encrypted_segments = Vec::new();
63
+        let total_segments = (file_data.len() + self.segment_size - 1) / self.segment_size;
64
+
65
+        for (segment_index, chunk) in file_data.chunks(self.segment_size).enumerate() {
66
+            let segment_encryption = SegmentEncryption::new(&self.params, &self.rng);
67
+            let key_path = vec![segment_index as u32]; // Simple path for now
68
+            
69
+            let encrypted_data = segment_encryption.encrypt_segment(
70
+                chunk,
71
+                segment_index as u64,
72
+                key_hierarchy,
73
+                &key_path,
74
+            )?;
75
+
76
+            encrypted_segments.push(encrypted_data);
77
+        }
78
+
79
+        Ok(encrypted_segments)
80
+    }
81
+
82
+    /// Decrypt segments back to original file data
83
+    pub fn decrypt_from_segments(
84
+        &self,
85
+        encrypted_segments: &[EncryptedData],
86
+        key_hierarchy: &KeyHierarchy,
87
+    ) -> Result<Vec<u8>> {
88
+        // Sort segments by index to ensure correct order
89
+        let mut sorted_segments = encrypted_segments.to_vec();
90
+        sorted_segments.sort_by_key(|s| s.segment_index);
91
+
92
+        let mut decrypted_data = Vec::new();
93
+        
94
+        for encrypted_segment in sorted_segments {
95
+            let segment_encryption = SegmentEncryption::new(&self.params, &self.rng);
96
+            let decrypted_chunk = segment_encryption.decrypt_segment(
97
+                &encrypted_segment,
98
+                key_hierarchy,
99
+            )?;
100
+            
101
+            decrypted_data.extend_from_slice(&decrypted_chunk);
102
+        }
103
+
104
+        Ok(decrypted_data)
105
+    }
106
+}
107
+
108
+/// Segment-level encryption operations
109
+pub struct SegmentEncryption {
110
+    params: AesParams,
111
+    rng: SystemRandom,
112
+}
113
+
114
+impl SegmentEncryption {
115
+    pub fn new(params: &AesParams, rng: &SystemRandom) -> Self {
116
+        Self {
117
+            params: params.clone(),
118
+            rng: SystemRandom::new(), // Create new instance for thread safety
119
+        }
120
+    }
121
+
122
+    /// Encrypt a single file segment
123
+    pub fn encrypt_segment(
124
+        &self,
125
+        segment_data: &[u8],
126
+        segment_index: u64,
127
+        key_hierarchy: &KeyHierarchy,
128
+        key_path: &[u32],
129
+    ) -> Result<EncryptedData> {
130
+        // Derive segment-specific key
131
+        let segment_key = key_hierarchy.derive_segment_key(key_path)
132
+            .context("Failed to derive segment key")?;
133
+
134
+        // Generate random nonce
135
+        let mut nonce_bytes = [0u8; 12];
136
+        self.rng.fill(&mut nonce_bytes)
137
+            .map_err(|_| anyhow::anyhow!("Failed to generate random nonce"))?;
138
+
139
+        let nonce = Nonce::assume_unique_for_key(nonce_bytes);
140
+
141
+        // Prepare AAD (Additional Authenticated Data) with segment metadata
142
+        let aad_data = self.prepare_aad(segment_index, key_path)?;
143
+        let aad = Aad::from(&aad_data);
144
+
145
+        // Create sealing key
146
+        let unbound_key = UnboundKey::new(&AES_256_GCM, segment_key.as_bytes())
147
+            .map_err(|_| anyhow::anyhow!("Failed to create encryption key"))?;
148
+        
149
+        let mut sealing_key = SealingKey::new(unbound_key, NonceCounter::new());
150
+
151
+        // Encrypt the data
152
+        let mut ciphertext = segment_data.to_vec();
153
+        sealing_key.seal_in_place_append_tag(aad, &mut ciphertext)
154
+            .map_err(|_| anyhow::anyhow!("Failed to encrypt segment"))?;
155
+
156
+        Ok(EncryptedData {
157
+            segment_index,
158
+            ciphertext,
159
+            nonce: nonce_bytes,
160
+            aad: aad_data,
161
+            key_path: key_path.to_vec(),
162
+        })
163
+    }
164
+
165
+    /// Decrypt a single encrypted segment
166
+    pub fn decrypt_segment(
167
+        &self,
168
+        encrypted_data: &EncryptedData,
169
+        key_hierarchy: &KeyHierarchy,
170
+    ) -> Result<Vec<u8>> {
171
+        // Derive the same segment key used for encryption
172
+        let segment_key = key_hierarchy.derive_segment_key(&encrypted_data.key_path)
173
+            .context("Failed to derive segment key for decryption")?;
174
+
175
+        // Recreate nonce
176
+        let nonce = Nonce::assume_unique_for_key(encrypted_data.nonce);
177
+        let aad = Aad::from(&encrypted_data.aad);
178
+
179
+        // Create opening key  
180
+        let unbound_key = UnboundKey::new(&AES_256_GCM, segment_key.as_bytes())
181
+            .map_err(|_| anyhow::anyhow!("Failed to create decryption key"))?;
182
+            
183
+        let mut opening_key = OpeningKey::new(unbound_key, NonceCounter::new());
184
+
185
+        // Decrypt the data
186
+        let mut ciphertext = encrypted_data.ciphertext.clone();
187
+        let plaintext = opening_key.open_in_place(aad, &mut ciphertext)
188
+            .map_err(|_| anyhow::anyhow!("Failed to decrypt segment - authentication failed"))?;
189
+
190
+        Ok(plaintext.to_vec())
191
+    }
192
+
193
+    /// Prepare Additional Authenticated Data (AAD) for GCM
194
+    fn prepare_aad(&self, segment_index: u64, key_path: &[u32]) -> Result<Vec<u8>> {
195
+        let mut aad = Vec::new();
196
+        
197
+        // Add segment index to AAD
198
+        aad.extend_from_slice(&segment_index.to_le_bytes());
199
+        
200
+        // Add key path to AAD for additional security
201
+        for path_element in key_path {
202
+            aad.extend_from_slice(&path_element.to_le_bytes());
203
+        }
204
+
205
+        // Add version identifier for future compatibility
206
+        aad.extend_from_slice(b"ZephyrFS-v1");
207
+
208
+        Ok(aad)
209
+    }
210
+}
211
+
212
+/// Nonce counter for GCM operations - ensures nonces are never reused
213
+#[derive(ZeroizeOnDrop)]
214
+struct NonceCounter {
215
+    counter: u64,
216
+}
217
+
218
+impl NonceCounter {
219
+    fn new() -> Self {
220
+        Self { counter: 0 }
221
+    }
222
+}
223
+
224
+impl NonceSequence for NonceCounter {
225
+    fn advance(&mut self) -> Result<Nonce, Unspecified> {
226
+        self.counter += 1;
227
+        
228
+        if self.counter == u64::MAX {
229
+            return Err(Unspecified);
230
+        }
231
+
232
+        let mut nonce_bytes = [0u8; 12];
233
+        nonce_bytes[4..].copy_from_slice(&self.counter.to_be_bytes());
234
+        
235
+        Ok(Nonce::assume_unique_for_key(nonce_bytes))
236
+    }
237
+}
238
+
239
+impl Zeroize for NonceCounter {
240
+    fn zeroize(&mut self) {
241
+        self.counter = 0;
242
+    }
243
+}
244
+
245
+#[cfg(test)]
246
+mod tests {
247
+    use super::*;
248
+    use crate::crypto::key_derivation::{KeyDerivation, DerivedKey};
249
+    use crate::crypto::CryptoParams;
250
+
251
+    fn create_test_hierarchy() -> Result<KeyHierarchy> {
252
+        let params = CryptoParams::default();
253
+        let key_derivation = KeyDerivation::new(&params.scrypt_params);
254
+        let derived_key = key_derivation.derive_from_password("test_password_123")?;
255
+        KeyHierarchy::new(derived_key)
256
+    }
257
+
258
+    #[test]
259
+    fn test_segment_encryption_roundtrip() -> Result<()> {
260
+        let params = CryptoParams::default().aes_params;
261
+        let hierarchy = create_test_hierarchy()?;
262
+        let rng = SystemRandom::new();
263
+        
264
+        let segment_encryption = SegmentEncryption::new(&params, &rng);
265
+        let test_data = b"Hello, ZephyrFS! This is test segment data for encryption.";
266
+        let key_path = vec![42];
267
+
268
+        // Encrypt
269
+        let encrypted = segment_encryption.encrypt_segment(
270
+            test_data,
271
+            1,
272
+            &hierarchy,
273
+            &key_path,
274
+        )?;
275
+
276
+        // Decrypt
277
+        let decrypted = segment_encryption.decrypt_segment(&encrypted, &hierarchy)?;
278
+
279
+        assert_eq!(test_data, decrypted.as_slice());
280
+        assert_eq!(encrypted.segment_index, 1);
281
+        assert_eq!(encrypted.key_path, vec![42]);
282
+        
283
+        Ok(())
284
+    }
285
+
286
+    #[test]
287
+    fn test_file_encryption_multiple_segments() -> Result<()> {
288
+        let params = CryptoParams::default().aes_params;
289
+        let hierarchy = create_test_hierarchy()?;
290
+        
291
+        let file_encryption = FileEncryption::with_segment_size(&params, 100); // Small segments for testing
292
+        let test_data = b"This is a longer test file that should be split into multiple segments for encryption testing. Each segment will be encrypted independently with its own derived key.";
293
+
294
+        // Encrypt file
295
+        let encrypted_segments = file_encryption.encrypt_to_segments(test_data, &hierarchy)?;
296
+        
297
+        // Should have multiple segments
298
+        assert!(encrypted_segments.len() > 1);
299
+
300
+        // Decrypt file
301
+        let decrypted_data = file_encryption.decrypt_from_segments(&encrypted_segments, &hierarchy)?;
302
+
303
+        assert_eq!(test_data, decrypted_data.as_slice());
304
+        
305
+        Ok(())
306
+    }
307
+
308
+    #[test]
309
+    fn test_encryption_authentication_failure() -> Result<()> {
310
+        let params = CryptoParams::default().aes_params;
311
+        let hierarchy = create_test_hierarchy()?;
312
+        let rng = SystemRandom::new();
313
+        
314
+        let segment_encryption = SegmentEncryption::new(&params, &rng);
315
+        let test_data = b"Test data for authentication failure";
316
+        let key_path = vec![1];
317
+
318
+        // Encrypt
319
+        let mut encrypted = segment_encryption.encrypt_segment(
320
+            test_data,
321
+            0,
322
+            &hierarchy,
323
+            &key_path,
324
+        )?;
325
+
326
+        // Tamper with ciphertext
327
+        encrypted.ciphertext[0] ^= 1;
328
+
329
+        // Decrypt should fail due to authentication failure
330
+        let result = segment_encryption.decrypt_segment(&encrypted, &hierarchy);
331
+        assert!(result.is_err());
332
+        
333
+        Ok(())
334
+    }
335
+
336
+    #[test]
337
+    fn test_different_segments_different_keys() -> Result<()> {
338
+        let params = CryptoParams::default().aes_params;
339
+        let hierarchy = create_test_hierarchy()?;
340
+        let rng = SystemRandom::new();
341
+        
342
+        let segment_encryption = SegmentEncryption::new(&params, &rng);
343
+        let test_data = b"Same data, different keys";
344
+
345
+        // Encrypt with different key paths
346
+        let encrypted1 = segment_encryption.encrypt_segment(
347
+            test_data, 0, &hierarchy, &[1]
348
+        )?;
349
+        
350
+        let encrypted2 = segment_encryption.encrypt_segment(
351
+            test_data, 0, &hierarchy, &[2]
352
+        )?;
353
+
354
+        // Ciphertexts should be different even with same plaintext
355
+        assert_ne!(encrypted1.ciphertext, encrypted2.ciphertext);
356
+        assert_ne!(encrypted1.key_path, encrypted2.key_path);
357
+        
358
+        Ok(())
359
+    }
360
+}
src/crypto/key_derivation.rsadded
@@ -0,0 +1,457 @@
1
+//! Key derivation system for ZephyrFS
2
+//!
3
+//! Implements hierarchical deterministic key derivation following Tahoe-LAFS security model.
4
+//! Uses scrypt for password-based key derivation and HKDF for key hierarchy expansion.
5
+
6
+use crate::crypto::{ScryptParams, SecureBytes};
7
+use anyhow::{Context, Result};
8
+use ring::hkdf::{Salt, Prk, HKDF_SHA256};
9
+use ring::rand::{SecureRandom, SystemRandom};
10
+use scrypt::{scrypt, Params};
11
+use zeroize::{Zeroize, ZeroizeOnDrop};
12
+use std::collections::HashMap;
13
+
14
+/// Master key derived from user password using scrypt
15
+#[derive(ZeroizeOnDrop)]
16
+pub struct DerivedKey {
17
+    /// Master key material (32 bytes)
18
+    master_key: SecureBytes,
19
+    /// Salt used for derivation (32 bytes)  
20
+    salt: [u8; 32],
21
+    /// Verification hash for password checking (32 bytes)
22
+    verification_hash: [u8; 32],
23
+}
24
+
25
+impl DerivedKey {
26
+    /// Create DerivedKey from raw bytes (64 bytes total)
27
+    pub fn from_bytes(bytes: SecureBytes) -> Result<Self> {
28
+        if bytes.len() != 64 {
29
+            anyhow::bail!("DerivedKey requires exactly 64 bytes (32 master + 32 salt, verification derived)");
30
+        }
31
+
32
+        let mut master_key = SecureBytes::new(32);
33
+        let mut salt = [0u8; 32];
34
+
35
+        master_key.copy_from_slice(&bytes[0..32]);
36
+        salt.copy_from_slice(&bytes[32..64]);
37
+
38
+        // Derive verification hash using HKDF from master key and salt
39
+        let salt_obj = Salt::new(HKDF_SHA256, &salt);
40
+        let prk = salt_obj.extract(master_key.as_bytes());
41
+        let mut verification_hash = [0u8; 32];
42
+        prk.expand(&[b"ZephyrFS-verification-hash-v1"], HKDF_SHA256)
43
+            .map_err(|_| anyhow::anyhow!("HKDF expansion failed"))?
44
+            .fill(&mut verification_hash)
45
+            .map_err(|_| anyhow::anyhow!("HKDF fill failed"))?;
46
+
47
+        Ok(Self {
48
+            master_key,
49
+            salt,
50
+            verification_hash,
51
+        })
52
+    }
53
+
54
+    /// Export key material as bytes (for secure storage/transmission)
55
+    pub fn to_bytes(&self) -> SecureBytes {
56
+        let mut bytes = SecureBytes::new(64);
57
+        bytes[0..32].copy_from_slice(self.master_key.as_bytes());
58
+        bytes[32..64].copy_from_slice(&self.salt);
59
+        // Note: verification_hash is derived, not stored
60
+        bytes
61
+    }
62
+
63
+    /// Get master key bytes for HKDF
64
+    pub fn master_key(&self) -> &SecureBytes {
65
+        &self.master_key
66
+    }
67
+
68
+    /// Get salt used for derivation
69
+    pub fn salt(&self) -> &[u8; 32] {
70
+        &self.salt
71
+    }
72
+
73
+    /// Verify password against stored verification hash
74
+    pub fn verify_password(&self, password: &str, params: &ScryptParams) -> Result<bool> {
75
+        let mut derived = vec![0u8; params.output_len];
76
+        let scrypt_params = Params::new(
77
+            params.log_n, 
78
+            params.r, 
79
+            params.p, 
80
+            params.output_len
81
+        ).context("Invalid scrypt parameters")?;
82
+
83
+        scrypt(password.as_bytes(), &self.salt, &scrypt_params, &mut derived)
84
+            .context("Scrypt key derivation failed")?;
85
+
86
+        // Extract master key from derived output
87
+        let derived_master_key = &derived[0..32];
88
+
89
+        // Derive verification hash using HKDF (same as during creation)
90
+        let salt_obj = Salt::new(HKDF_SHA256, &self.salt);
91
+        let prk = salt_obj.extract(derived_master_key);
92
+        let mut computed_verification = [0u8; 32];
93
+        prk.expand(&[b"ZephyrFS-verification-hash-v1"], HKDF_SHA256)
94
+            .map_err(|_| anyhow::anyhow!("HKDF expansion failed"))?
95
+            .fill(&mut computed_verification)
96
+            .map_err(|_| anyhow::anyhow!("HKDF fill failed"))?;
97
+
98
+        // Check verification hash matches
99
+        let verification_ok = constant_time_eq::constant_time_eq(
100
+            &computed_verification, 
101
+            &self.verification_hash
102
+        );
103
+
104
+        // Clear derived key material
105
+        derived.zeroize();
106
+        computed_verification.zeroize();
107
+
108
+        Ok(verification_ok)
109
+    }
110
+}
111
+
112
+/// Password-based key derivation using scrypt
113
+pub struct KeyDerivation {
114
+    params: ScryptParams,
115
+    rng: SystemRandom,
116
+}
117
+
118
+impl KeyDerivation {
119
+    pub fn new(params: &ScryptParams) -> Self {
120
+        Self {
121
+            params: params.clone(),
122
+            rng: SystemRandom::new(),
123
+        }
124
+    }
125
+
126
+    /// Derive key from password with random salt
127
+    pub fn derive_from_password(&self, password: &str) -> Result<DerivedKey> {
128
+        // Generate random salt
129
+        let mut salt = [0u8; 32];
130
+        self.rng.fill(&mut salt)
131
+            .map_err(|_| anyhow::anyhow!("Failed to generate random salt"))?;
132
+
133
+        self.derive_from_password_with_salt(password, &salt)
134
+    }
135
+
136
+    /// Derive key from password with specific salt (for key recovery)
137
+    pub fn derive_from_password_with_salt(&self, password: &str, salt: &[u8; 32]) -> Result<DerivedKey> {
138
+        let scrypt_params = Params::new(
139
+            self.params.log_n,
140
+            self.params.r, 
141
+            self.params.p,
142
+            self.params.output_len
143
+        ).context("Invalid scrypt parameters")?;
144
+
145
+        let mut derived = vec![0u8; self.params.output_len];
146
+        scrypt(password.as_bytes(), salt, &scrypt_params, &mut derived)
147
+            .context("Scrypt key derivation failed")?;
148
+
149
+        // Split derived output: 32 bytes master key (64 bytes total now)
150
+        let mut master_key = SecureBytes::new(32);
151
+        master_key.copy_from_slice(&derived[0..32]);
152
+
153
+        // Derive verification hash using HKDF from master key and salt
154
+        let salt_obj = Salt::new(HKDF_SHA256, salt);
155
+        let prk = salt_obj.extract(master_key.as_bytes());
156
+        let mut verification_hash = [0u8; 32];
157
+        prk.expand(&[b"ZephyrFS-verification-hash-v1"], HKDF_SHA256)
158
+            .map_err(|_| anyhow::anyhow!("HKDF expansion failed"))?
159
+            .fill(&mut verification_hash)
160
+            .map_err(|_| anyhow::anyhow!("HKDF fill failed"))?;
161
+
162
+        // Clear the derived buffer
163
+        derived.zeroize();
164
+
165
+        Ok(DerivedKey {
166
+            master_key,
167
+            salt: *salt,
168
+            verification_hash,
169
+        })
170
+    }
171
+}
172
+
173
+/// Hierarchical key system for deriving segment-specific keys
174
+pub struct KeyHierarchy {
175
+    master_prk: Prk,
176
+    key_cache: HashMap<Vec<u32>, SecureBytes>,
177
+}
178
+
179
+impl KeyHierarchy {
180
+    /// Create new key hierarchy from derived master key
181
+    pub fn new(derived_key: DerivedKey) -> Result<Self> {
182
+        // Use HKDF to create pseudorandom key from master key
183
+        let salt = Salt::new(HKDF_SHA256, &derived_key.salt);
184
+        let prk = salt.extract(derived_key.master_key.as_bytes());
185
+
186
+        Ok(Self {
187
+            master_prk: prk,
188
+            key_cache: HashMap::new(),
189
+        })
190
+    }
191
+
192
+    /// Derive segment-specific encryption key
193
+    pub fn derive_segment_key(&self, key_path: &[u32]) -> Result<SecureBytes> {
194
+        // Check cache first
195
+        if let Some(cached_key) = self.key_cache.get(key_path) {
196
+            return Ok(cached_key.clone());
197
+        }
198
+
199
+        // Create info string from key path
200
+        let mut info = Vec::new();
201
+        info.extend_from_slice(b"ZephyrFS-segment-key-v1:");
202
+        for &path_element in key_path {
203
+            info.extend_from_slice(&path_element.to_be_bytes());
204
+        }
205
+
206
+        // Derive key using HKDF-Expand
207
+        let mut segment_key = SecureBytes::new(32); // AES-256 key size
208
+        self.master_prk.expand(&[&info], ring::hkdf::HKDF_SHA256)
209
+            .map_err(|_| anyhow::anyhow!("HKDF expansion failed"))?
210
+            .fill(segment_key.as_mut())?;
211
+
212
+        Ok(segment_key)
213
+    }
214
+
215
+    /// Derive file-level key (for file metadata encryption)
216
+    pub fn derive_file_key(&self, file_id: &[u8]) -> Result<SecureBytes> {
217
+        let mut info = Vec::new();
218
+        info.extend_from_slice(b"ZephyrFS-file-key-v1:");
219
+        info.extend_from_slice(file_id);
220
+
221
+        let mut file_key = SecureBytes::new(32);
222
+        self.master_prk.expand(&[&info], ring::hkdf::HKDF_SHA256)
223
+            .map_err(|_| anyhow::anyhow!("HKDF expansion failed"))?
224
+            .fill(file_key.as_mut())?;
225
+
226
+        Ok(file_key)
227
+    }
228
+
229
+    /// Derive capability key for zero-knowledge proofs
230
+    pub fn derive_capability_key(&self, capability_id: &[u8]) -> Result<SecureBytes> {
231
+        let mut info = Vec::new();
232
+        info.extend_from_slice(b"ZephyrFS-capability-key-v1:");
233
+        info.extend_from_slice(capability_id);
234
+
235
+        let mut capability_key = SecureBytes::new(32);
236
+        self.master_prk.expand(&[&info], ring::hkdf::HKDF_SHA256)
237
+            .map_err(|_| anyhow::anyhow!("HKDF expansion failed"))?
238
+            .fill(capability_key.as_mut())?;
239
+
240
+        Ok(capability_key)
241
+    }
242
+
243
+    /// Get public capability for sharing (non-sensitive key material)
244
+    pub fn get_public_capability(&self) -> Result<Vec<u8>> {
245
+        // Derive a public capability that can be shared without compromising security
246
+        let mut info = Vec::new();
247
+        info.extend_from_slice(b"ZephyrFS-public-capability-v1");
248
+
249
+        let mut public_cap = vec![0u8; 32];
250
+        self.master_prk.expand(&[&info], ring::hkdf::HKDF_SHA256)
251
+            .map_err(|_| anyhow::anyhow!("HKDF expansion failed"))?
252
+            .fill(&mut public_cap)?;
253
+
254
+        Ok(public_cap)
255
+    }
256
+
257
+    /// Clear key cache (for security)
258
+    pub fn clear_cache(&mut self) {
259
+        for (_, mut key) in self.key_cache.drain() {
260
+            key.zeroize();
261
+        }
262
+    }
263
+}
264
+
265
+impl Drop for KeyHierarchy {
266
+    fn drop(&mut self) {
267
+        self.clear_cache();
268
+    }
269
+}
270
+
271
+impl Zeroize for KeyHierarchy {
272
+    fn zeroize(&mut self) {
273
+        self.clear_cache();
274
+        // Note: Cannot zeroize master_prk as it's not under our control
275
+    }
276
+}
277
+
278
+#[cfg(test)]
279
+mod tests {
280
+    use super::*;
281
+
282
+    #[test]
283
+    fn test_scrypt_params_validation() -> Result<()> {
284
+        // Test different scrypt parameters to find valid range
285
+        println!("Testing various scrypt parameters...");
286
+        
287
+        // Test with lower log_n
288
+        let result15 = scrypt::Params::new(15, 8, 1, 96);
289
+        println!("Scrypt params (15, 8, 1, 96) result: {:?}", result15);
290
+        
291
+        let result16 = scrypt::Params::new(16, 8, 1, 96);
292
+        println!("Scrypt params (16, 8, 1, 96) result: {:?}", result16);
293
+        
294
+        let result17 = scrypt::Params::new(17, 8, 1, 96);
295
+        println!("Scrypt params (17, 8, 1, 96) result: {:?}", result17);
296
+        
297
+        // Test with smaller output length
298
+        let result17_32 = scrypt::Params::new(17, 8, 1, 32);
299
+        println!("Scrypt params (17, 8, 1, 32) result: {:?}", result17_32);
300
+        
301
+        let result17_64 = scrypt::Params::new(17, 8, 1, 64);
302
+        println!("Scrypt params (17, 8, 1, 64) result: {:?}", result17_64);
303
+        
304
+        // 64-byte versions should work, 96-byte should not
305
+        assert!(result17_32.is_ok(), "17,8,1,32 should be valid");
306
+        assert!(result17_64.is_ok(), "17,8,1,64 should be valid");
307
+        assert!(!result17.is_ok(), "17,8,1,96 should be invalid");
308
+        Ok(())
309
+    }
310
+
311
+    #[test]
312
+    fn test_key_derivation_from_password() -> Result<()> {
313
+        let params = ScryptParams {
314
+            log_n: 15,  // Lower for testing (faster)
315
+            r: 8,
316
+            p: 1,
317
+            output_len: 64,
318
+        };
319
+
320
+        let key_derivation = KeyDerivation::new(&params);
321
+        let derived_key = key_derivation.derive_from_password("test_password_123")?;
322
+
323
+        assert_eq!(derived_key.master_key.len(), 32);
324
+        assert_eq!(derived_key.salt.len(), 32);
325
+        assert_eq!(derived_key.verification_hash.len(), 32);
326
+
327
+        Ok(())
328
+    }
329
+
330
+    #[test]
331
+    fn test_password_verification() -> Result<()> {
332
+        let params = ScryptParams {
333
+            log_n: 15,
334
+            r: 8, 
335
+            p: 1,
336
+            output_len: 64,
337
+        };
338
+
339
+        let key_derivation = KeyDerivation::new(&params);
340
+        let derived_key = key_derivation.derive_from_password("correct_password")?;
341
+
342
+        // Correct password should verify
343
+        assert!(derived_key.verify_password("correct_password", &params)?);
344
+
345
+        // Wrong password should not verify  
346
+        assert!(!derived_key.verify_password("wrong_password", &params)?);
347
+
348
+        Ok(())
349
+    }
350
+
351
+    #[test]
352
+    fn test_key_hierarchy_segment_keys() -> Result<()> {
353
+        let params = ScryptParams {
354
+            log_n: 15,
355
+            r: 8,
356
+            p: 1,
357
+            output_len: 96,
358
+        };
359
+
360
+        let key_derivation = KeyDerivation::new(&params);
361
+        let derived_key = key_derivation.derive_from_password("test_password")?;
362
+        let hierarchy = KeyHierarchy::new(derived_key)?;
363
+
364
+        // Derive keys for different segments
365
+        let key1 = hierarchy.derive_segment_key(&[0])?;
366
+        let key2 = hierarchy.derive_segment_key(&[1])?;
367
+        let key3 = hierarchy.derive_segment_key(&[0, 1])?;
368
+
369
+        // Keys should be different
370
+        assert_ne!(key1.as_bytes(), key2.as_bytes());
371
+        assert_ne!(key1.as_bytes(), key3.as_bytes());
372
+        assert_ne!(key2.as_bytes(), key3.as_bytes());
373
+
374
+        // Same path should produce same key
375
+        let key1_again = hierarchy.derive_segment_key(&[0])?;
376
+        assert_eq!(key1.as_bytes(), key1_again.as_bytes());
377
+
378
+        Ok(())
379
+    }
380
+
381
+    #[test]
382
+    fn test_derived_key_serialization() -> Result<()> {
383
+        let params = ScryptParams {
384
+            log_n: 15,
385
+            r: 8,
386
+            p: 1, 
387
+            output_len: 96,
388
+        };
389
+
390
+        let key_derivation = KeyDerivation::new(&params);
391
+        let original_key = key_derivation.derive_from_password("serialization_test")?;
392
+
393
+        // Serialize and deserialize
394
+        let serialized = original_key.to_bytes();
395
+        let restored_key = DerivedKey::from_bytes(serialized)?;
396
+
397
+        // Should be able to verify with restored key
398
+        assert!(restored_key.verify_password("serialization_test", &params)?);
399
+        assert!(!restored_key.verify_password("wrong_password", &params)?);
400
+
401
+        Ok(())
402
+    }
403
+
404
+    #[test]
405
+    fn test_deterministic_key_derivation() -> Result<()> {
406
+        let params = ScryptParams {
407
+            log_n: 15,
408
+            r: 8,
409
+            p: 1,
410
+            output_len: 96,
411
+        };
412
+
413
+        let key_derivation = KeyDerivation::new(&params);
414
+        let password = "deterministic_test";
415
+        let salt = [42u8; 32]; // Fixed salt
416
+
417
+        // Derive key twice with same password and salt
418
+        let key1 = key_derivation.derive_from_password_with_salt(password, &salt)?;
419
+        let key2 = key_derivation.derive_from_password_with_salt(password, &salt)?;
420
+
421
+        // Should produce identical keys
422
+        assert_eq!(key1.master_key.as_bytes(), key2.master_key.as_bytes());
423
+        assert_eq!(key1.salt, key2.salt);
424
+        assert_eq!(key1.verification_hash, key2.verification_hash);
425
+
426
+        Ok(())
427
+    }
428
+
429
+    #[test]
430
+    fn test_capability_derivation() -> Result<()> {
431
+        let params = ScryptParams {
432
+            log_n: 15,
433
+            r: 8,
434
+            p: 1,
435
+            output_len: 96,
436
+        };
437
+
438
+        let key_derivation = KeyDerivation::new(&params);
439
+        let derived_key = key_derivation.derive_from_password("capability_test")?;
440
+        let hierarchy = KeyHierarchy::new(derived_key)?;
441
+
442
+        let file_key = hierarchy.derive_file_key(b"test_file_id")?;
443
+        let cap_key = hierarchy.derive_capability_key(b"test_capability_id")?;
444
+        let public_cap = hierarchy.get_public_capability()?;
445
+
446
+        // All should be different
447
+        assert_ne!(file_key.as_bytes(), cap_key.as_bytes());
448
+        assert_ne!(file_key.as_bytes(), &public_cap);
449
+        assert_ne!(cap_key.as_bytes(), &public_cap);
450
+
451
+        // Should be deterministic
452
+        let file_key2 = hierarchy.derive_file_key(b"test_file_id")?;
453
+        assert_eq!(file_key.as_bytes(), file_key2.as_bytes());
454
+
455
+        Ok(())
456
+    }
457
+}
src/crypto/mod.rsadded
@@ -0,0 +1,213 @@
1
+//! # ZephyrFS Cryptography Module
2
+//! 
3
+//! Provides zero-knowledge client-side encryption for ZephyrFS distributed storage.
4
+//! Implements Tahoe-LAFS inspired capability-based security with mandatory encryption.
5
+//!
6
+//! ## Architecture
7
+//! - AES-256-GCM encryption for file segments
8
+//! - Scrypt key derivation from user passwords  
9
+//! - Hierarchical deterministic keys for per-segment encryption
10
+//! - Secure memory handling with automatic cleanup
11
+//! - Content addressing with cryptographic verification
12
+
13
+pub mod encryption;
14
+pub mod key_derivation;
15
+pub mod secure_memory;
16
+pub mod content_addressing;
17
+
18
+pub use encryption::{FileEncryption, SegmentEncryption, EncryptedData};
19
+pub use key_derivation::{KeyDerivation, DerivedKey, KeyHierarchy};
20
+pub use secure_memory::{SecureBytes, SecureString};
21
+pub use content_addressing::{ContentId, ContentVerifier, HashAlgorithm};
22
+
23
+use anyhow::Result;
24
+use zeroize::Zeroize;
25
+
26
+/// Core cryptographic parameters following Tahoe-LAFS security model
27
+pub struct CryptoParams {
28
+    /// Scrypt parameters: N=2^17, r=8, p=1, output=96 bytes
29
+    pub scrypt_params: ScryptParams,
30
+    /// AES-256-GCM parameters
31
+    pub aes_params: AesParams,
32
+    /// Content addressing parameters
33
+    pub hash_params: HashParams,
34
+}
35
+
36
+#[derive(Debug, Clone)]
37
+pub struct ScryptParams {
38
+    pub log_n: u8,      // 17 (N = 2^17 = 131072)
39
+    pub r: u32,         // 8
40
+    pub p: u32,         // 1  
41
+    pub output_len: usize, // 96 bytes (master key + salt + verification)
42
+}
43
+
44
+#[derive(Debug, Clone)]
45
+pub struct AesParams {
46
+    pub key_len: usize,    // 32 bytes (AES-256)
47
+    pub nonce_len: usize,  // 12 bytes (GCM standard)
48
+    pub tag_len: usize,    // 16 bytes (GCM authentication tag)
49
+}
50
+
51
+#[derive(Debug, Clone)] 
52
+pub struct HashParams {
53
+    pub content_hasher: ContentHasher,
54
+    pub verification_hasher: VerificationHasher,
55
+}
56
+
57
+#[derive(Debug, Clone)]
58
+pub enum ContentHasher {
59
+    Blake3,     // Fast hashing for content IDs
60
+    Sha256,     // SHA-256 for compatibility
61
+}
62
+
63
+#[derive(Debug, Clone)]
64
+pub enum VerificationHasher {
65
+    Blake3,     // Fast verification
66
+    Sha256,     // Standard verification  
67
+}
68
+
69
+impl Default for CryptoParams {
70
+    fn default() -> Self {
71
+        Self {
72
+            scrypt_params: ScryptParams {
73
+                log_n: 17,          // 2^17 iterations (strong security)
74
+                r: 8,               // Memory cost parameter
75
+                p: 1,               // Parallelization parameter  
76
+                output_len: 64,     // 64 bytes (max supported by scrypt crate)
77
+            },
78
+            aes_params: AesParams {
79
+                key_len: 32,        // AES-256
80
+                nonce_len: 12,      // GCM standard nonce size
81
+                tag_len: 16,        // GCM authentication tag size
82
+            },
83
+            hash_params: HashParams {
84
+                content_hasher: ContentHasher::Blake3,
85
+                verification_hasher: VerificationHasher::Blake3,
86
+            },
87
+        }
88
+    }
89
+}
90
+
91
+/// High-level cryptographic operations for ZephyrFS
92
+pub struct ZephyrCrypto {
93
+    params: CryptoParams,
94
+    key_hierarchy: Option<KeyHierarchy>,
95
+}
96
+
97
+impl ZephyrCrypto {
98
+    /// Create new crypto instance with default parameters
99
+    pub fn new() -> Self {
100
+        Self {
101
+            params: CryptoParams::default(),
102
+            key_hierarchy: None,
103
+        }
104
+    }
105
+
106
+    /// Create crypto instance with custom parameters
107
+    pub fn with_params(params: CryptoParams) -> Self {
108
+        Self {
109
+            params,
110
+            key_hierarchy: None,
111
+        }
112
+    }
113
+
114
+    /// Initialize key hierarchy from password
115
+    pub fn init_from_password(&mut self, password: &str) -> Result<()> {
116
+        let key_derivation = KeyDerivation::new(&self.params.scrypt_params);
117
+        let derived_key = key_derivation.derive_from_password(password)?;
118
+        self.key_hierarchy = Some(KeyHierarchy::new(derived_key)?);
119
+        Ok(())
120
+    }
121
+
122
+    /// Initialize key hierarchy from existing key material  
123
+    pub fn init_from_key(&mut self, key_material: SecureBytes) -> Result<()> {
124
+        let derived_key = DerivedKey::from_bytes(key_material)?;
125
+        self.key_hierarchy = Some(KeyHierarchy::new(derived_key)?);
126
+        Ok(())
127
+    }
128
+
129
+    /// Encrypt file data into segments
130
+    pub fn encrypt_file(&self, file_data: &[u8]) -> Result<Vec<EncryptedData>> {
131
+        let hierarchy = self.key_hierarchy.as_ref()
132
+            .ok_or_else(|| anyhow::anyhow!("Key hierarchy not initialized"))?;
133
+
134
+        let file_encryption = FileEncryption::new(&self.params.aes_params);
135
+        file_encryption.encrypt_to_segments(file_data, hierarchy)
136
+    }
137
+
138
+    /// Decrypt file segments back to original data
139
+    pub fn decrypt_file(&self, encrypted_segments: &[EncryptedData]) -> Result<Vec<u8>> {
140
+        let hierarchy = self.key_hierarchy.as_ref()
141
+            .ok_or_else(|| anyhow::anyhow!("Key hierarchy not initialized"))?;
142
+
143
+        let file_encryption = FileEncryption::new(&self.params.aes_params);
144
+        file_encryption.decrypt_from_segments(encrypted_segments, hierarchy)
145
+    }
146
+
147
+    /// Generate content ID for data
148
+    pub fn content_id(&self, data: &[u8]) -> ContentId {
149
+        ContentId::generate(data, &self.params.hash_params.content_hasher)
150
+    }
151
+
152
+    /// Verify content integrity
153
+    pub fn verify_content(&self, data: &[u8], expected_id: &ContentId) -> bool {
154
+        ContentVerifier::verify(data, expected_id, &self.params.hash_params.verification_hasher)
155
+    }
156
+
157
+    /// Get public key material for capability sharing (zero-knowledge proof)
158
+    pub fn get_capability(&self) -> Result<Vec<u8>> {
159
+        let hierarchy = self.key_hierarchy.as_ref()
160
+            .ok_or_else(|| anyhow::anyhow!("Key hierarchy not initialized"))?;
161
+        
162
+        // Return only public/shareable key material, never private keys
163
+        Ok(hierarchy.get_public_capability()?)
164
+    }
165
+}
166
+
167
+impl Drop for ZephyrCrypto {
168
+    fn drop(&mut self) {
169
+        // Ensure all sensitive data is cleared when dropping
170
+        if let Some(ref mut hierarchy) = self.key_hierarchy {
171
+            hierarchy.zeroize();
172
+        }
173
+    }
174
+}
175
+
176
+impl Zeroize for ZephyrCrypto {
177
+    fn zeroize(&mut self) {
178
+        if let Some(ref mut hierarchy) = self.key_hierarchy {
179
+            hierarchy.zeroize();
180
+        }
181
+        self.key_hierarchy = None;
182
+    }
183
+}
184
+
185
+#[cfg(test)]
186
+mod tests {
187
+    use super::*;
188
+
189
+    #[test]
190
+    fn test_crypto_params_default() {
191
+        let params = CryptoParams::default();
192
+        assert_eq!(params.scrypt_params.log_n, 17);
193
+        assert_eq!(params.scrypt_params.r, 8);
194
+        assert_eq!(params.scrypt_params.p, 1);
195
+        assert_eq!(params.aes_params.key_len, 32);
196
+        assert_eq!(params.aes_params.nonce_len, 12);
197
+        assert_eq!(params.aes_params.tag_len, 16);
198
+    }
199
+
200
+    #[test]
201
+    fn test_zephyr_crypto_creation() {
202
+        let crypto = ZephyrCrypto::new();
203
+        assert!(crypto.key_hierarchy.is_none());
204
+    }
205
+
206
+    #[test] 
207
+    fn test_crypto_init_from_password() {
208
+        let mut crypto = ZephyrCrypto::new();
209
+        let result = crypto.init_from_password("test_password_123");
210
+        assert!(result.is_ok());
211
+        assert!(crypto.key_hierarchy.is_some());
212
+    }
213
+}
src/crypto/secure_memory.rsadded
@@ -0,0 +1,461 @@
1
+//! Secure memory handling for ZephyrFS cryptographic operations
2
+//!
3
+//! Provides zeroizing containers for sensitive data like keys, passwords, and plaintext.
4
+//! Ensures memory is securely cleared when data is no longer needed.
5
+
6
+use zeroize::{Zeroize, ZeroizeOnDrop};
7
+use std::ops::{Deref, DerefMut};
8
+use std::fmt;
9
+
10
+/// Secure byte buffer that automatically zeros memory on drop
11
+#[derive(Clone, ZeroizeOnDrop)]
12
+pub struct SecureBytes {
13
+    data: Vec<u8>,
14
+}
15
+
16
+impl SecureBytes {
17
+    /// Create new secure buffer with specified size
18
+    pub fn new(size: usize) -> Self {
19
+        Self {
20
+            data: vec![0u8; size],
21
+        }
22
+    }
23
+
24
+    /// Create from existing data (data will be zeroized in source)
25
+    pub fn from_vec(mut data: Vec<u8>) -> Self {
26
+        let secure = Self { data: data.clone() };
27
+        data.zeroize(); // Clear original
28
+        secure
29
+    }
30
+
31
+    /// Create from slice (slice data will be copied)
32
+    pub fn from_slice(data: &[u8]) -> Self {
33
+        Self {
34
+            data: data.to_vec(),
35
+        }
36
+    }
37
+
38
+    /// Get length of secure buffer
39
+    pub fn len(&self) -> usize {
40
+        self.data.len()
41
+    }
42
+
43
+    /// Check if buffer is empty
44
+    pub fn is_empty(&self) -> bool {
45
+        self.data.is_empty()
46
+    }
47
+
48
+    /// Get immutable reference to bytes
49
+    pub fn as_bytes(&self) -> &[u8] {
50
+        &self.data
51
+    }
52
+
53
+    /// Get mutable reference to bytes
54
+    pub fn as_mut(&mut self) -> &mut [u8] {
55
+        &mut self.data
56
+    }
57
+
58
+    /// Copy data from slice into this buffer
59
+    pub fn copy_from_slice(&mut self, src: &[u8]) {
60
+        if src.len() != self.data.len() {
61
+            panic!("Source length {} doesn't match buffer length {}", src.len(), self.data.len());
62
+        }
63
+        self.data.copy_from_slice(src);
64
+    }
65
+
66
+    /// Resize buffer (new space will be zero-filled)
67
+    pub fn resize(&mut self, new_size: usize) {
68
+        self.data.resize(new_size, 0);
69
+    }
70
+
71
+    /// Clear buffer contents (set all bytes to zero)
72
+    pub fn clear(&mut self) {
73
+        self.data.zeroize();
74
+    }
75
+
76
+    /// Split buffer at index, returning (left, right)
77
+    pub fn split_at(mut self, mid: usize) -> (SecureBytes, SecureBytes) {
78
+        let right = self.data.split_off(mid);
79
+        let left_data = std::mem::take(&mut self.data);
80
+        let left = SecureBytes { data: left_data };
81
+        let right = SecureBytes { data: right };
82
+        self.data.zeroize(); // Clear original (now empty)
83
+        (left, right)
84
+    }
85
+
86
+    /// Extend buffer with data from slice
87
+    pub fn extend_from_slice(&mut self, data: &[u8]) {
88
+        self.data.extend_from_slice(data);
89
+    }
90
+
91
+    /// Convert to Vec<u8> (consumes self and zeros original)
92
+    pub fn into_vec(mut self) -> Vec<u8> {
93
+        let data = std::mem::take(&mut self.data);
94
+        self.data.zeroize();
95
+        data
96
+    }
97
+}
98
+
99
+impl Deref for SecureBytes {
100
+    type Target = [u8];
101
+
102
+    fn deref(&self) -> &Self::Target {
103
+        &self.data
104
+    }
105
+}
106
+
107
+impl DerefMut for SecureBytes {
108
+    fn deref_mut(&mut self) -> &mut Self::Target {
109
+        &mut self.data
110
+    }
111
+}
112
+
113
+impl AsRef<[u8]> for SecureBytes {
114
+    fn as_ref(&self) -> &[u8] {
115
+        &self.data
116
+    }
117
+}
118
+
119
+impl AsMut<[u8]> for SecureBytes {
120
+    fn as_mut(&mut self) -> &mut [u8] {
121
+        &mut self.data
122
+    }
123
+}
124
+
125
+// Implement Debug but don't show actual data
126
+impl fmt::Debug for SecureBytes {
127
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128
+        f.debug_struct("SecureBytes")
129
+            .field("len", &self.data.len())
130
+            .field("data", &"[REDACTED]")
131
+            .finish()
132
+    }
133
+}
134
+
135
+// Implement PartialEq for testing, but use constant-time comparison
136
+impl PartialEq for SecureBytes {
137
+    fn eq(&self, other: &Self) -> bool {
138
+        if self.data.len() != other.data.len() {
139
+            return false;
140
+        }
141
+        constant_time_eq::constant_time_eq(&self.data, &other.data)
142
+    }
143
+}
144
+
145
+impl Eq for SecureBytes {}
146
+
147
+impl Zeroize for SecureBytes {
148
+    fn zeroize(&mut self) {
149
+        self.data.zeroize();
150
+    }
151
+}
152
+
153
+/// Secure string that automatically zeros memory on drop
154
+#[derive(Clone, ZeroizeOnDrop)]
155
+pub struct SecureString {
156
+    data: String,
157
+}
158
+
159
+impl SecureString {
160
+    /// Create new empty secure string
161
+    pub fn new() -> Self {
162
+        Self {
163
+            data: String::new(),
164
+        }
165
+    }
166
+
167
+    /// Create from existing string (string will be zeroized in source)
168
+    pub fn from_string(mut data: String) -> Self {
169
+        let secure = Self { data: data.clone() };
170
+        data.zeroize();
171
+        secure
172
+    }
173
+
174
+    /// Create from str (data will be copied)
175
+    pub fn from_str(data: &str) -> Self {
176
+        Self {
177
+            data: data.to_string(),
178
+        }
179
+    }
180
+
181
+    /// Get length of string
182
+    pub fn len(&self) -> usize {
183
+        self.data.len()
184
+    }
185
+
186
+    /// Check if string is empty
187
+    pub fn is_empty(&self) -> bool {
188
+        self.data.is_empty()
189
+    }
190
+
191
+    /// Get str reference
192
+    pub fn as_str(&self) -> &str {
193
+        &self.data
194
+    }
195
+
196
+    /// Push character to string
197
+    pub fn push(&mut self, ch: char) {
198
+        self.data.push(ch);
199
+    }
200
+
201
+    /// Push str to string
202
+    pub fn push_str(&mut self, string: &str) {
203
+        self.data.push_str(string);
204
+    }
205
+
206
+    /// Clear string contents
207
+    pub fn clear(&mut self) {
208
+        self.data.zeroize();
209
+        self.data.clear();
210
+    }
211
+
212
+    /// Convert to String (consumes self and zeros original)
213
+    pub fn into_string(mut self) -> String {
214
+        let data = std::mem::take(&mut self.data);
215
+        self.data.zeroize();
216
+        data
217
+    }
218
+
219
+    /// Get bytes of the UTF-8 string
220
+    pub fn as_bytes(&self) -> &[u8] {
221
+        self.data.as_bytes()
222
+    }
223
+}
224
+
225
+impl Default for SecureString {
226
+    fn default() -> Self {
227
+        Self::new()
228
+    }
229
+}
230
+
231
+impl Deref for SecureString {
232
+    type Target = str;
233
+
234
+    fn deref(&self) -> &Self::Target {
235
+        &self.data
236
+    }
237
+}
238
+
239
+impl AsRef<str> for SecureString {
240
+    fn as_ref(&self) -> &str {
241
+        &self.data
242
+    }
243
+}
244
+
245
+impl fmt::Debug for SecureString {
246
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247
+        f.debug_struct("SecureString")
248
+            .field("len", &self.data.len())
249
+            .field("data", &"[REDACTED]")
250
+            .finish()
251
+    }
252
+}
253
+
254
+impl PartialEq for SecureString {
255
+    fn eq(&self, other: &Self) -> bool {
256
+        constant_time_eq::constant_time_eq(self.data.as_bytes(), other.data.as_bytes())
257
+    }
258
+}
259
+
260
+impl Eq for SecureString {}
261
+
262
+impl Zeroize for SecureString {
263
+    fn zeroize(&mut self) {
264
+        self.data.zeroize();
265
+    }
266
+}
267
+
268
+/// Utility for securely reading passwords from console
269
+pub struct SecureInput;
270
+
271
+impl SecureInput {
272
+    /// Read password from stdin without echoing (requires rpassword crate)
273
+    #[cfg(feature = "password-input")]
274
+    pub fn read_password(prompt: &str) -> std::io::Result<SecureString> {
275
+        use rpassword::read_password_from_tty;
276
+        print!("{}", prompt);
277
+        std::io::flush(std::io::stdout())?;
278
+        let password = read_password_from_tty(None)?;
279
+        Ok(SecureString::from_string(password))
280
+    }
281
+
282
+    /// Read password from stdin (fallback - echoes input, for testing only)
283
+    #[cfg(not(feature = "password-input"))]
284
+    pub fn read_password(prompt: &str) -> std::io::Result<SecureString> {
285
+        use std::io::{self, Write};
286
+        print!("{}", prompt);
287
+        io::stdout().flush()?;
288
+        
289
+        let mut input = String::new();
290
+        io::stdin().read_line(&mut input)?;
291
+        
292
+        // Remove trailing newline
293
+        if input.ends_with('\n') {
294
+            input.pop();
295
+        }
296
+        
297
+        Ok(SecureString::from_string(input))
298
+    }
299
+}
300
+
301
+/// Helper trait for creating secure versions of sensitive data
302
+pub trait IntoSecure {
303
+    type Output;
304
+    fn into_secure(self) -> Self::Output;
305
+}
306
+
307
+impl IntoSecure for Vec<u8> {
308
+    type Output = SecureBytes;
309
+    
310
+    fn into_secure(self) -> Self::Output {
311
+        SecureBytes::from_vec(self)
312
+    }
313
+}
314
+
315
+impl IntoSecure for String {
316
+    type Output = SecureString;
317
+    
318
+    fn into_secure(self) -> Self::Output {
319
+        SecureString::from_string(self)
320
+    }
321
+}
322
+
323
+impl IntoSecure for &[u8] {
324
+    type Output = SecureBytes;
325
+    
326
+    fn into_secure(self) -> Self::Output {
327
+        SecureBytes::from_slice(self)
328
+    }
329
+}
330
+
331
+impl IntoSecure for &str {
332
+    type Output = SecureString;
333
+    
334
+    fn into_secure(self) -> Self::Output {
335
+        SecureString::from_str(self)
336
+    }
337
+}
338
+
339
+#[cfg(test)]
340
+mod tests {
341
+    use super::*;
342
+
343
+    #[test]
344
+    fn test_secure_bytes_basic_operations() {
345
+        let mut secure = SecureBytes::new(32);
346
+        assert_eq!(secure.len(), 32);
347
+        assert!(!secure.is_empty());
348
+
349
+        // Fill with test data
350
+        for (i, byte) in secure.as_mut().iter_mut().enumerate() {
351
+            *byte = (i % 256) as u8;
352
+        }
353
+
354
+        // Check data
355
+        for (i, &byte) in secure.as_bytes().iter().enumerate() {
356
+            assert_eq!(byte, (i % 256) as u8);
357
+        }
358
+
359
+        // Clear should zero all data
360
+        secure.clear();
361
+        for &byte in secure.as_bytes() {
362
+            assert_eq!(byte, 0);
363
+        }
364
+    }
365
+
366
+    #[test]
367
+    fn test_secure_bytes_from_slice() {
368
+        let data = b"Hello, ZephyrFS!";
369
+        let secure = SecureBytes::from_slice(data);
370
+        
371
+        assert_eq!(secure.len(), data.len());
372
+        assert_eq!(secure.as_bytes(), data);
373
+    }
374
+
375
+    #[test]
376
+    fn test_secure_bytes_split() {
377
+        let mut secure = SecureBytes::new(10);
378
+        for (i, byte) in secure.as_mut().iter_mut().enumerate() {
379
+            *byte = i as u8;
380
+        }
381
+
382
+        let (left, right) = secure.split_at(4);
383
+        assert_eq!(left.len(), 4);
384
+        assert_eq!(right.len(), 6);
385
+        assert_eq!(left.as_bytes(), &[0, 1, 2, 3]);
386
+        assert_eq!(right.as_bytes(), &[4, 5, 6, 7, 8, 9]);
387
+    }
388
+
389
+    #[test]
390
+    fn test_secure_string_operations() {
391
+        let mut secure = SecureString::from_str("Hello");
392
+        assert_eq!(secure.len(), 5);
393
+        assert_eq!(secure.as_str(), "Hello");
394
+
395
+        secure.push_str(", World!");
396
+        assert_eq!(secure.as_str(), "Hello, World!");
397
+
398
+        secure.clear();
399
+        assert!(secure.is_empty());
400
+    }
401
+
402
+    #[test]
403
+    fn test_secure_memory_equality() {
404
+        let secure1 = SecureBytes::from_slice(b"test data");
405
+        let secure2 = SecureBytes::from_slice(b"test data");
406
+        let secure3 = SecureBytes::from_slice(b"different");
407
+
408
+        assert_eq!(secure1, secure2);
409
+        assert_ne!(secure1, secure3);
410
+
411
+        let str1 = SecureString::from_str("password");
412
+        let str2 = SecureString::from_str("password");
413
+        let str3 = SecureString::from_str("different");
414
+
415
+        assert_eq!(str1, str2);
416
+        assert_ne!(str1, str3);
417
+    }
418
+
419
+    #[test] 
420
+    fn test_into_secure_trait() {
421
+        let vec = vec![1, 2, 3, 4, 5];
422
+        let secure_bytes = vec.into_secure();
423
+        assert_eq!(secure_bytes.as_bytes(), &[1, 2, 3, 4, 5]);
424
+
425
+        let string = "test string".to_string();
426
+        let secure_string = string.into_secure();
427
+        assert_eq!(secure_string.as_str(), "test string");
428
+
429
+        let slice: &[u8] = &[10, 20, 30];
430
+        let secure_bytes = slice.into_secure();
431
+        assert_eq!(secure_bytes.as_bytes(), &[10, 20, 30]);
432
+
433
+        let str_ref: &str = "reference string";
434
+        let secure_string = str_ref.into_secure();
435
+        assert_eq!(secure_string.as_str(), "reference string");
436
+    }
437
+
438
+    #[test]
439
+    fn test_debug_redaction() {
440
+        let secure_bytes = SecureBytes::from_slice(b"secret data");
441
+        let debug_str = format!("{:?}", secure_bytes);
442
+        assert!(debug_str.contains("[REDACTED]"));
443
+        assert!(!debug_str.contains("secret"));
444
+
445
+        let secure_string = SecureString::from_str("secret password");
446
+        let debug_str = format!("{:?}", secure_string);
447
+        assert!(debug_str.contains("[REDACTED]"));
448
+        assert!(!debug_str.contains("password"));
449
+    }
450
+
451
+    #[test]
452
+    fn test_zeroize_on_drop() {
453
+        let mut data = vec![1, 2, 3, 4, 5];
454
+        {
455
+            let _secure = SecureBytes::from_vec(data.clone());
456
+            // secure goes out of scope here and should zero its memory
457
+        }
458
+        // Original data should be zeroed
459
+        assert_eq!(data, vec![0, 0, 0, 0, 0]);
460
+    }
461
+}
src/lib.rsadded
@@ -0,0 +1,20 @@
1
+//! ZephyrFS Node Library
2
+//!
3
+//! Core library for ZephyrFS distributed P2P storage system.
4
+//! Provides cryptographic primitives, storage management, and network protocols.
5
+
6
+pub mod config;
7
+pub mod network;
8
+pub mod storage;
9
+pub mod protocol;
10
+pub mod node_manager;
11
+pub mod crypto;
12
+
13
+pub use crypto::{
14
+    ZephyrCrypto, CryptoParams, ScryptParams, AesParams, HashParams,
15
+    ContentHasher, VerificationHasher, EncryptedData, ContentId, HashAlgorithm
16
+};
17
+
18
+pub use config::Config;
19
+pub use node_manager::{NodeManager, DistributionStrategy};
20
+pub use storage::{StorageManager, StorageConfig};
src/main.rsmodified
@@ -8,6 +8,7 @@ mod network;
8
 mod storage;
8
 mod storage;
9
 mod protocol;
9
 mod protocol;
10
 mod node_manager;
10
 mod node_manager;
11
+mod crypto;
11
 
12
 
12
 #[cfg(test)]
13
 #[cfg(test)]
13
 mod integration_tests;
14
 mod integration_tests;
@@ -108,7 +109,7 @@ async fn main() -> Result<()> {
108
         .init();
109
         .init();
109
     
110
     
110
     match args.command {
111
     match args.command {
111
-        Some(Commands::HealthCheck) | None if std::env::args().any(|arg| arg == "--health-check") => {
112
+        Some(Commands::HealthCheck) => {
112
             // Simple health check for Docker/monitoring
113
             // Simple health check for Docker/monitoring
113
             info!("Health check passed");
114
             info!("Health check passed");
114
             Ok(())
115
             Ok(())
@@ -118,10 +119,14 @@ async fn main() -> Result<()> {
118
             init_node(storage_path, max_storage_gb, args.config.as_deref()).await
119
             init_node(storage_path, max_storage_gb, args.config.as_deref()).await
119
         }
120
         }
120
         
121
         
121
-        Some(Commands::Start { daemon }) | None => {
122
+        Some(Commands::Start { daemon }) => {
122
             start_node(daemon, args.config.as_deref()).await
123
             start_node(daemon, args.config.as_deref()).await
123
         }
124
         }
124
         
125
         
126
+        None => {
127
+            start_node(false, args.config.as_deref()).await
128
+        }
129
+        
125
         Some(Commands::Join { bootstrap_peer }) => {
130
         Some(Commands::Join { bootstrap_peer }) => {
126
             join_network(bootstrap_peer, args.config.as_deref()).await
131
             join_network(bootstrap_peer, args.config.as_deref()).await
127
         }
132
         }