Rust · 15599 bytes Raw Blame History
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 }