rust_keyvault/
storage.rs

1//! Storage backend traits and implementations
2
3use crate::{crypto::{RuntimeAead, AEAD, RandomNonceGenerator, NonceGenerator}, key::VersionedKey, KeyId, KeyMetadata, KeyState, Result};
4use std::collections::HashMap;
5use std::sync::{Arc, RwLock};
6use std::path::{Path, PathBuf};
7use std::fs;
8use serde::{Deserialize, Serialize};
9use rand_chacha::ChaCha20Rng;
10use rand_core::SeedableRng;
11use std::time::SystemTime;
12use argon2::Argon2;
13use argon2::{Algorithm as Argon2Algorithm, Version, Params};
14
15const KEYVAULT_SALT: &[u8] = b"rust-keyvault-argon2-salt-v1-fixed-32b";
16
17/// Trait for key storage backends
18pub trait KeyStore: Send + Sync {
19    /// Store a versioned key
20    fn store(&mut self, key: VersionedKey) -> Result<()>;
21
22    /// Retrieve a key by ID
23    fn retrieve(&self, id: &KeyId) -> Result<VersionedKey>;
24
25    /// Delete a key
26    fn delete(&mut self, id: &KeyId) -> Result<()>;
27
28    /// List all kety IDs
29    fn list(&self) -> Result<Vec<KeyId>>;
30
31    /// Update key metadata
32    fn update_metadata(&mut self, id: &KeyId, metadata: KeyMetadata) -> Result<()>; 
33
34    /// Find keys by state
35    fn find_by_state(&self, state: KeyState) -> Result<Vec<KeyId>>;
36
37    /// Rotate a key to a new version
38    fn rotate_key(&mut self, id: &KeyId) -> Result<VersionedKey>;
39
40    /// Get all verions of a key (sorted by version number)
41    fn get_key_versions(&self, id: &KeyId) -> Result<Vec<VersionedKey>>;
42
43    /// Get the latest active version of a key
44    fn get_latest_key(&self, id: &KeyId) -> Result<VersionedKey>;
45}
46
47/// Extended trait for advanced key lifecycle management
48/// 
49/// Provides methods for managing key states and cleanup policies
50/// This exxtends `KeyStore` with operations for key deprecation and revocation
51pub trait KeyLifeCycle: KeyStore {
52    /// Mark a particular key as deprecated (key should be able to decrypt but not encrypt)
53    fn deprecate_key(&mut self, id: &KeyId) -> Result<()>;
54
55    /// Revoke a key (key should not be used for any operations)
56    fn revoke_key(&mut self, id: &KeyId) -> Result<()>;
57
58    /// Clean up old versions based on policy
59    fn cleanup_old_versions(&mut self, id: &KeyId, keep_versions: usize) -> Result<Vec<KeyId>>;
60}
61
62/// Trait for persistent storage backends
63pub trait PersistentStorage: KeyStore {
64    /// Flush any pending writes to persistent storage
65    fn flush(&mut self) -> Result<()>;
66
67    /// Load keys from persistent storage
68    fn load(&mut self) -> Result<()>;
69
70    /// Get the storage location/path
71    fn location(&self) -> &str;
72}
73
74/// Serializable wrapper for persisted keys (excludes secret material)
75/// 
76/// Contains key metadata and the encrypted key material for disk storage
77/// The actual secret ket bytes are encrypted when `StorageConfig.encrypted` is true
78#[derive(Debug, Clone, Serialize, Deserialize)]
79struct PersistedKey {
80    /// Key metadata
81    metadata: KeyMetadata,
82    /// Encrypted key material (as bytes)
83    encrypted_key: Vec<u8>,
84}
85
86/// Trait for encrypted storage backends
87/// 
88/// Provides password-based intialisation and re-keying capabilites
89/// for storage backends that support encryption at rest
90pub trait EncryptedStore: KeyStore {
91    /// Initiate the store with a master key
92    fn init_with_password(&mut self, password: &[u8]) -> Result<()>; 
93
94    /// Re-encrypt all keys with a new master key
95    fn rekey(&mut self, new_password: &[u8]) -> Result<()>;
96
97    /// Check if the store is unlocked
98    fn is_unlocked(&self) -> bool;
99}
100
101/// In-memory key store
102/// 
103/// Thread-safe storage backend for testing and high performamce scenerios
104/// where persistence is not required, please remember that all keys are lost when dropped
105pub struct MemoryStore {
106    keys: Arc<RwLock<HashMap<KeyId, VersionedKey>>>,
107}
108
109impl Default for MemoryStore {
110    fn default() -> Self {
111        Self::new()
112    }
113}
114
115impl MemoryStore {
116    /// Create a new in-memory store
117    pub fn new() -> Self {
118        Self {
119            keys: Arc::new(RwLock::new(HashMap::new())),
120        }
121    }
122
123    /// Generate new key material for the given algorithm
124    fn generate_new_key_material(algorithm: crate::Algorithm) -> Result<crate::key::SecretKey> {
125        use crate::crypto::{SimpleSymmetricKeyGenerator, KeyGenerator};
126        use rand_chacha::ChaCha20Rng;
127        use rand_core::SeedableRng;
128        
129        let mut rng = ChaCha20Rng::from_entropy();
130        let generator = SimpleSymmetricKeyGenerator;
131        let params = crate::crypto::KeyGenParams {
132            algorithm,
133            seed: None,
134            key_size: None,
135        };
136        
137        generator.generate_with_params(&mut rng, params)
138    }
139}
140
141impl KeyStore for MemoryStore {
142    fn store(&mut self, key: VersionedKey) -> Result<()> {
143        let key_id = key.metadata.id.clone();
144        let mut keys = self.keys.write().map_err(|_| crate::Error::storage("lock poisoned"))?;
145        keys.insert(key_id, key);
146        Ok(())
147    }
148
149    fn retrieve(&self, id: &KeyId) -> Result<VersionedKey> {
150        let keys = self.keys.read().map_err(|_| crate::Error::storage("lock poisoned"))?;
151        keys.get(id)
152            .cloned()
153            .ok_or_else(|| crate::Error::storage(format!("key not found: {id:?}")))
154    }
155
156    fn delete(&mut self, id: &KeyId) -> Result<()> {
157        let mut keys = self.keys.write().map_err(|_| crate::Error::storage("lock poisoned"))?;
158        keys.remove(id)
159            .ok_or_else(|| crate::Error::storage(format!("key not found: {id:?}")))?;
160        Ok(())
161    }
162
163    fn list(&self) -> Result<Vec<KeyId>> {
164        let keys = self.keys.read().map_err(|_| crate::Error::storage("lock poisoned"))?;
165        Ok(keys.keys().cloned().collect())
166    }
167
168    fn update_metadata(&mut self, id: &KeyId, metadata: KeyMetadata) -> Result<()> {
169        let mut keys = self.keys.write().map_err(|_| crate::Error::storage("lock poisoned"))?;
170        if let Some(versioned_key) = keys.get_mut(id) {
171            versioned_key.metadata = metadata;
172            Ok(())
173        } else {
174            Err(crate::Error::storage(format!("key not found: {id:?}")))
175        }
176    }
177
178    fn find_by_state(&self, state: KeyState) -> Result<Vec<KeyId>> {
179        let keys = self.keys.read().map_err(|_| crate::Error::storage("lock poisoned"))?;
180        Ok(keys
181            .iter()
182            .filter(|(_, versioned_key)| versioned_key.metadata.state == state)
183            .map(|(id, _)| id.clone())
184            .collect())
185    }
186
187    fn rotate_key(&mut self, id: &KeyId) -> Result<VersionedKey> {
188        let current_key = self.get_latest_key(id)?;
189        let mut depracated_metadata = current_key.metadata.clone();
190        depracated_metadata.state = KeyState::Deprecated;
191        self.update_metadata(id, depracated_metadata)?;
192
193        let new_version = current_key.metadata.version + 1;
194        let new_key_id = KeyId::generate_versioned(id, new_version)?;
195
196        let new_secret_key = Self::generate_new_key_material(current_key.metadata.algorithm)?;
197        let new_metadata = KeyMetadata {
198            id: new_key_id.clone(),
199            base_id: current_key.metadata.base_id.clone(),
200            algorithm: current_key.metadata.algorithm,
201            created_at: SystemTime::now(),
202            expires_at: current_key.metadata.expires_at,
203            state: KeyState::Active,
204            version: new_version,
205        };
206
207        let new_versioned_key = VersionedKey {
208            key: new_secret_key,
209            metadata: new_metadata,
210        };
211
212        // Store the new key
213        self.store(new_versioned_key.clone())?;
214
215        Ok(new_versioned_key)
216    }
217
218    fn get_key_versions(&self, id: &KeyId) -> Result<Vec<VersionedKey>> {
219        let keys = self.keys.read().map_err(|_| crate::Error::storage("lock poisoned"))?;
220
221        let mut versions = Vec::new();
222
223        // Look for all te keys with the same base ID but different versions
224        for (_store_id, key) in keys.iter() {
225            if &key.metadata.base_id == id {
226                versions.push(key.clone());
227            }
228        }
229
230        // Sort the IDs by version number
231        versions.sort_by_key(|k| k.metadata.version);
232
233        if versions.is_empty() {
234            return Err(crate::Error::storage(format!("no versions found for this key: {id:?}")))
235        }
236        Ok(versions)
237    }
238
239    fn get_latest_key(&self, id: &KeyId) -> Result<VersionedKey> {
240        let versions = self.get_key_versions(id)?;
241        
242        // Find the latest active or rotating key
243        versions
244            .into_iter()
245            .filter(|k| matches!(k.metadata.state, KeyState::Active | KeyState::Rotating))
246            .max_by_key(|k| k.metadata.version)
247            .ok_or_else(|| crate::Error::storage(format!("no active key found for: {id:?}")))
248    }
249}
250
251/// Configuration for file-based storage
252/// 
253/// Controls encryption, compression and caching behaviour for persistent storage.
254#[derive(Debug, Clone)]
255pub struct StorageConfig {
256    /// Path to storage location (file, directory, etc.)
257    pub path: Option<String>,
258    /// Enable encryption at rest
259    pub encrypted: bool,
260    /// Enable compresssion
261    pub compressed: bool,
262    /// Maximum number of keys to cache in memory
263    pub cache_size: usize,
264}
265
266impl Default for StorageConfig {
267    fn default() -> Self {
268        Self {
269            path: None,
270            encrypted: false,
271            compressed: false,
272            cache_size: 100,
273        }
274    }
275}
276
277/// File-based key store with optional encryption
278/// 
279/// Provides persistent storage of cryptographic keys with optional encryption at rest
280/// The key are cached in memory for performamce and automatically loaded from disk
281pub struct FileStore {
282    /// Directory path for key storage
283    path: PathBuf,
284    /// In-memory cache of loaded keys
285    keys: HashMap<KeyId, VersionedKey>,
286    /// Configuration
287    config: StorageConfig,
288    /// Master key for encryption (optional, i.e. if enabled)
289    master_key: Option<crate::key::SecretKey>,
290}
291
292impl FileStore {
293    /// Create a new FileStore at the given path
294    pub fn new<P: AsRef<Path>>(path: P, config: StorageConfig) -> Result<Self> {
295        let path = path.as_ref().to_path_buf();
296
297        // Create the directory if it does not exists
298        if !path.exists() {
299            fs::create_dir_all(&path)?;
300        }
301
302        let mut store = Self {
303            path,
304            keys: HashMap::new(),
305            config,
306            master_key: None,
307        };
308
309        // Load existing keys
310        store.load()?;
311
312        Ok(store)
313    }
314
315    /// Set master key for encryption
316    pub fn set_master_key(&mut self, key: crate::key::SecretKey) -> Result<()> {
317        if !self.config.encrypted {
318            return Err(crate::Error::storage("encryption not enabled in config"));
319        }
320        self.master_key = Some(key); 
321        Ok(())
322    }
323
324    /// Get the file path for a key ID
325    /// 
326    /// Returns the filesystem path where the key's own encrypted data is stored
327    fn key_path(&self, id: &KeyId) -> PathBuf {
328        let filename = format!("{id:?}.json");
329        self.path.join(filename)
330    }
331
332    /// Serialize the key and optionally encrypt a key
333    fn serialize_key(&self, key: &VersionedKey) -> Result<Vec<u8>> {
334        let key_bytes = key.key.expose_secret().to_vec();
335
336        let encrypted_key = if self.config.encrypted {
337            if let Some(master_key) = &self.master_key {
338                let aead = RuntimeAead;
339                let mut nonce_gen = RandomNonceGenerator::new(
340                    ChaCha20Rng::from_entropy(),
341                    RuntimeAead::NONCE_SIZE
342                );
343
344                let key_id_bytes = format!("{:?}", key.metadata.id);
345                let nonce = nonce_gen.generate_nonce(key_id_bytes.as_bytes())?;
346                let encrypted_bytes = aead.encrypt(
347                    master_key, 
348                    &nonce, 
349                    &key_bytes, 
350                    b"rust-keyvault-key-encryption",
351                )?;
352
353                let mut result = nonce;
354                result.extend_from_slice(&encrypted_bytes);
355                result
356            } else {
357                return Err(crate::Error::storage("encryption enabled but no master key set"));
358            }
359        } else {
360            key_bytes
361        };
362
363        let persisted = PersistedKey {
364            metadata: key.metadata.clone(),
365            encrypted_key,
366        };
367
368        serde_json::to_vec(&persisted)
369            .map_err(|e| crate::Error::storage(format!("serilization failed: {e}"))) 
370    }
371
372    /// Deserialize and optionally decrypt a key
373    fn deserialize_key(&self, data: &[u8]) -> Result<VersionedKey> {
374        let persisted: PersistedKey = serde_json::from_slice(data)
375            .map_err(|e| crate::Error::storage(format!("deserialization failed: {e}")))?;
376        
377        let key_bytes = if self.config.encrypted {
378            if let Some(master_key) = &self.master_key {
379                let aead = RuntimeAead;
380                
381                if persisted.encrypted_key.len() < RuntimeAead::NONCE_SIZE {
382                    return Err(crate::Error::storage("encrypted key too short - corrupted data"));
383                }
384                
385                let (nonce, ciphertext) = persisted.encrypted_key.split_at(RuntimeAead::NONCE_SIZE);
386                
387                aead.decrypt(
388                    master_key,
389                    nonce,
390                    ciphertext,
391                    b"rust-keyvault-key-encryption" 
392                )?
393            } else {
394                return Err(crate::Error::storage("encrypted key but no master key available"));
395            }
396        } else {
397            persisted.encrypted_key
398        };
399        
400        let secret_key = crate::key::SecretKey::from_bytes(key_bytes, persisted.metadata.algorithm)?;
401        
402        Ok(VersionedKey {
403            key: secret_key,
404            metadata: persisted.metadata,
405        })
406    }
407    
408    /// Initialize with password-derived master key (now using Argon2)
409    pub fn init_with_password(&mut self, password: &[u8]) -> Result<()> {
410        if !self.config.encrypted {
411            return Err(crate::Error::storage("encryption not enabled in config"));
412        }
413        
414        let salt = KEYVAULT_SALT;
415        let master_key = Self::derive_master_key(password, salt)?;
416        self.set_master_key(master_key)?;
417        
418        Ok(())
419    }
420
421    /// Derive a master key from a password using Argon2id
422    pub fn derive_master_key(password: &[u8], salt: &[u8]) -> Result<crate::key::SecretKey> {
423        let params = Params::new(
424            19456, 
425            2,
426            1,
427            Some(32),
428        ).map_err(|e| crate::Error::crypto(format!("invalid Argon2 params: {e}")))?;
429        
430        let argon2 = Argon2::new(Argon2Algorithm::Argon2id, Version::V0x13, params);
431
432        let mut key_bytes = [0u8; 32];
433        argon2.hash_password_into(password, salt, &mut key_bytes)
434            .map_err(|e| crate::Error::crypto(format!("Argon2 derivation failed: {e}")))?;
435
436        crate::key::SecretKey::from_bytes(key_bytes.to_vec(), crate::Algorithm::ChaCha20Poly1305)
437    }
438
439    /// Generate new key material for the given algorithm
440    fn generate_new_key_material(&self, algorithm: crate::Algorithm) -> Result<crate::key::SecretKey> {
441        use crate::crypto::{SimpleSymmetricKeyGenerator, KeyGenerator};
442        use rand_chacha::ChaCha20Rng;
443        use rand_core::SeedableRng;
444        
445        let mut rng = ChaCha20Rng::from_entropy();
446        let generator = SimpleSymmetricKeyGenerator;
447        let params = crate::crypto::KeyGenParams {
448            algorithm,
449            seed: None,
450            key_size: None,
451        };
452        
453        generator.generate_with_params(&mut rng, params)
454    }
455}
456
457impl EncryptedStore for FileStore {
458    /// Initialise with password-derived master key
459    fn init_with_password(&mut self, password: &[u8]) -> Result<()> {
460        if !self.config.encrypted {
461            return Err(crate::Error::storage("encryption not enabled in config"));
462        }
463
464        let salt = KEYVAULT_SALT;
465        let master_key = Self::derive_master_key(password, salt)?;
466        self.set_master_key(master_key)?;
467
468        Ok(())
469    }
470
471    fn rekey(&mut self, new_password: &[u8]) -> Result<()> {
472        if !self.config.encrypted {
473            return Err(crate::Error::storage("encryption not enabled in config"));
474        }
475
476        // Ensure we have a current master key
477        if self.master_key.is_none() {
478            return Err(crate::Error::storage("store is locked - cannot rekey"));
479        }
480
481        // Collect all keys that need re-encryption
482        let all_keys: Vec<_> = self.keys.values().cloned().collect();
483        
484        // Derive new master key
485        let salt = KEYVAULT_SALT;
486        let new_master_key = Self::derive_master_key(new_password, salt)?;
487        
488        // Set the new master key
489        self.master_key = Some(new_master_key);
490        
491        // Re-encrypt all keys with the new master key
492        for key in all_keys {
493            self.store(key)?;
494        }
495        
496        Ok(())
497    }
498
499    /// Check if the store is unlocked (has a master key available)
500    /// 
501    /// Returns `true` if a master key has been set and the store can decrypt keys
502    fn is_unlocked(&self) -> bool {
503        self.master_key.is_some()
504    }
505}
506
507impl KeyStore for FileStore {
508    fn store(&mut self, key: VersionedKey) -> Result<()> {
509        let key_id = key.metadata.id.clone();
510        let key_path = self.key_path(&key_id);
511        
512        // Serialize to bytes
513        let data = self.serialize_key(&key)?;
514        
515        // Write to file
516        fs::write(&key_path, data)?;
517        
518        // Update in-memory cache
519        self.keys.insert(key_id, key);
520        
521        Ok(())
522    }
523    
524    fn retrieve(&self, id: &KeyId) -> Result<VersionedKey> {
525        // Check cache first
526        if let Some(key) = self.keys.get(id) {
527            return Ok(key.clone());
528        }
529        
530        // Load from disk
531        let key_path = self.key_path(id);
532        if !key_path.exists() {
533            return Err(crate::Error::storage(format!("key file not found: {id:?}")));
534        }
535        
536        let data = fs::read(&key_path)?;
537        self.deserialize_key(&data)
538    }
539    
540    fn delete(&mut self, id: &KeyId) -> Result<()> {
541        let key_path = self.key_path(id);
542        
543        // Remove from disk
544        if key_path.exists() {
545            fs::remove_file(&key_path)?;
546        }
547        
548        // Remove from cache
549        self.keys.remove(id)
550            .ok_or_else(|| crate::Error::storage(format!("key not found: {id:?}")))?;
551        
552        Ok(())
553    }
554    
555    fn list(&self) -> Result<Vec<KeyId>> {
556        Ok(self.keys.keys().cloned().collect())
557    }
558    
559    fn update_metadata(&mut self, id: &KeyId, metadata: KeyMetadata) -> Result<()> {
560        if let Some(versioned_key) = self.keys.get_mut(id) {
561            versioned_key.metadata = metadata;
562            // Re-persist to disk
563            let key_copy = versioned_key.clone();
564            self.store(key_copy)?;
565            Ok(())
566        } else {
567            Err(crate::Error::storage(format!("key not found: {id:?}")))
568        }
569    }
570    
571    fn find_by_state(&self, state: KeyState) -> Result<Vec<KeyId>> {
572        Ok(self.keys
573            .iter()
574            .filter(|(_, key)| key.metadata.state == state)
575            .map(|(id, _)| id.clone())
576            .collect())
577    }
578
579    fn rotate_key(&mut self, id: &KeyId) -> Result<VersionedKey> {
580        let current_key = self.get_latest_key(id)?;
581        
582        let mut deprecated_metadata = current_key.metadata.clone();
583        deprecated_metadata.state = KeyState::Deprecated;
584        self.update_metadata(id, deprecated_metadata)?;
585        
586        let new_version = current_key.metadata.version + 1;
587        let new_key_id = KeyId::generate_versioned(id, new_version)?;
588
589        let new_secret_key = self.generate_new_key_material(current_key.key.algorithm())?;
590        let new_metadata = KeyMetadata {
591            id: new_key_id.clone(),
592            algorithm: current_key.metadata.algorithm,
593            created_at: SystemTime::now(),
594            expires_at: current_key.metadata.expires_at,
595            state: KeyState::Active,
596            version: new_version,
597            base_id: current_key.metadata.base_id.clone(),
598        };
599        
600        let new_versioned_key = VersionedKey {
601            key: new_secret_key,
602            metadata: new_metadata,
603        };
604        
605        // 5. Store the new key
606        self.store(new_versioned_key.clone())?;
607        
608        Ok(new_versioned_key)
609    }
610
611    fn get_key_versions(&self, id: &KeyId) -> Result<Vec<VersionedKey>> {
612        let mut versions = Vec::new();
613        
614        // Look for all keys with the same base ID but different versions
615        for key in self.keys.values() {
616            if &key.metadata.base_id == id {
617                versions.push(key.clone());
618            }
619        }
620        
621        // Sort by version number
622        versions.sort_by_key(|k| k.metadata.version);
623        
624        if versions.is_empty() {
625            return Err(crate::Error::storage(format!("no versions found for key: {id:?}")));
626        }
627        
628        Ok(versions)
629    }
630
631    fn get_latest_key(&self, id: &KeyId) -> Result<VersionedKey> {
632        let versions = self.get_key_versions(id)?;
633        
634        // Find the latest active or rotating key
635        versions
636            .into_iter()
637            .filter(|k| matches!(k.metadata.state, KeyState::Active | KeyState::Rotating))
638            .max_by_key(|k| k.metadata.version)
639            .ok_or_else(|| crate::Error::storage(format!("no active key found for: {id:?}")))
640    }
641}
642
643impl PersistentStorage for FileStore {
644    fn flush(&mut self) -> Result<()> {
645        // Re-persist all keys to ensure consistency
646        let keys: Vec<_> = self.keys.values().cloned().collect();
647        for key in keys {
648            self.store(key)?;
649        }
650        Ok(())
651    }
652
653    fn load(&mut self) -> Result<()> {
654        self.keys.clear();
655
656        // Read all .json files in that directory
657        for entry in fs::read_dir(&self.path)? {
658            let entry = entry?;
659            let path = entry.path();
660
661            if path.extension().and_then(|s| s.to_str()) == Some("json") {
662                let data = fs::read(&path)?;
663                match self.deserialize_key(&data) {
664                    Ok(key) => {
665                        self.keys.insert(key.metadata.id.clone(), key);
666                    }
667                    Err(e) => {
668                        eprintln!("WARNING: Failed to load key from {path:?}: {e}");
669                    }
670                }
671            }
672        }
673
674        Ok(())
675    }
676
677    fn location(&self) -> &str {
678        self.path.to_str().unwrap_or("<invalid_path>")
679    }
680}
681
682impl KeyLifeCycle for FileStore {
683    fn deprecate_key(&mut self, id: &KeyId) -> Result<()> {
684        let key = self.retrieve(id)?;
685        
686        if !matches!(key.metadata.state, KeyState::Active | KeyState::Rotating) {
687            return Err(crate::Error::InvalidKeyState(
688                format!("cannot deprecate key in state: {:?}", key.metadata.state)
689            ));
690        }
691        
692        let mut new_metadata = key.metadata.clone();
693        new_metadata.state = KeyState::Deprecated;
694        
695        self.update_metadata(id, new_metadata)
696    }
697    
698    fn revoke_key(&mut self, id: &KeyId) -> Result<()> {
699        let key = self.retrieve(id)?;
700        
701        let mut new_metadata = key.metadata.clone();
702        new_metadata.state = KeyState::Revoked;
703        
704        self.update_metadata(id, new_metadata)
705    }
706    
707    fn cleanup_old_versions(&mut self, id: &KeyId, keep_versions: usize) -> Result<Vec<KeyId>> {
708        let mut versions = self.get_key_versions(id)?;
709        
710        // Sort by version (newest first)
711        versions.sort_by_key(|k| std::cmp::Reverse(k.metadata.version));
712        
713        let mut removed_keys = Vec::new();
714        
715        // Keep the specified number of versions, remove the rest
716        for key_to_remove in versions.iter().skip(keep_versions) {
717            if matches!(key_to_remove.metadata.state, KeyState::Revoked | KeyState::Deprecated) {
718                self.delete(&key_to_remove.metadata.id)?;
719                removed_keys.push(key_to_remove.metadata.id.clone());
720            }
721        }
722        
723        Ok(removed_keys)
724    }
725}
726
727#[cfg(test)]
728mod tests {
729    use super::*;
730    
731    #[test]
732    fn test_storage_config_default() {
733        let config = StorageConfig::default();
734        assert!(!config.encrypted);
735        assert!(!config.compressed);
736    }
737
738    #[test]
739    fn test_memory_store_basic_operations() {
740        use crate::{Algorithm, key::SecretKey};
741        use std::time::SystemTime;
742
743        let mut store = MemoryStore::new();
744
745        // Create a test key
746        let key_id = KeyId::from_bytes([1; 16]);
747        let secret_key = SecretKey::from_bytes(vec![0u8; 32], Algorithm::ChaCha20Poly1305).unwrap();
748        let metadata = KeyMetadata {
749            id: key_id.clone(),
750            base_id: key_id.clone(),
751            algorithm: Algorithm::ChaCha20Poly1305,
752            created_at: SystemTime::now(),
753            state: KeyState::Active,
754            version: 1,
755            expires_at: None,
756        };
757        let versioned_key = VersionedKey {
758            key: secret_key,
759            metadata: metadata.clone(),
760        };
761
762        // Test store and retrieve
763        store.store(versioned_key).unwrap();
764        let retrieved = store.retrieve(&key_id).unwrap();
765        assert_eq!(retrieved.metadata.id, key_id);
766        assert_eq!(retrieved.metadata.state, KeyState::Active);
767
768        // Test list
769        let keys = store.list().unwrap();
770        assert_eq!(keys.len(), 1);
771        assert!(keys.contains(&key_id));
772
773        // Test find by state
774        let active_keys = store.find_by_state(KeyState::Active).unwrap();
775        assert_eq!(active_keys.len(), 1);
776
777        // Test delete
778        store.delete(&key_id).unwrap();
779        let keys = store.list().unwrap();
780        assert_eq!(keys.len(), 0);
781    }
782
783    #[test]
784    fn test_file_store_basic_operations() {
785        use tempfile::tempdir;
786        use crate::{Algorithm, key::SecretKey};
787        use std::time::SystemTime;
788        
789        // Create temporary directory
790        let temp_dir = tempdir().unwrap();
791        let config = StorageConfig::default();
792        let mut store = FileStore::new(temp_dir.path(), config).unwrap();
793        
794        // Create test key
795        let key_id = KeyId::from_bytes([2; 16]);
796        let secret_key = SecretKey::from_bytes(vec![0x42; 32], Algorithm::ChaCha20Poly1305).unwrap();
797        let metadata = KeyMetadata {
798            id: key_id.clone(),
799            base_id: key_id.clone(),
800            algorithm: Algorithm::ChaCha20Poly1305,
801            created_at: SystemTime::now(),
802            expires_at: None,
803            state: KeyState::Active,
804            version: 1,
805        };
806        let versioned_key = VersionedKey {
807            key: secret_key,
808            metadata: metadata.clone(),
809        };
810        
811        // Test store and retrieve
812        store.store(versioned_key).unwrap();
813        let retrieved = store.retrieve(&key_id).unwrap();
814        assert_eq!(retrieved.metadata.id, key_id);
815        
816        // Test persistence (create new store instance)
817        let store2 = FileStore::new(temp_dir.path(), StorageConfig::default()).unwrap();
818        let retrieved2 = store2.retrieve(&key_id).unwrap();
819        assert_eq!(retrieved2.metadata.id, key_id);
820    }
821
822    #[test]
823    fn test_file_store_encryption() {
824        use tempfile::tempdir;
825        use crate::{Algorithm, key::SecretKey};
826        use std::time::SystemTime;
827        
828        // Create encrypted store
829        let temp_dir = tempdir().unwrap();
830        let config = StorageConfig {
831            encrypted: true,
832            ..Default::default()
833        };
834        let mut store = FileStore::new(temp_dir.path(), config).unwrap();
835        
836        // Initialize with password and verify unlock state
837        store.init_with_password(b"super-secret-password-123").unwrap();
838        assert!(store.is_unlocked());
839        
840        // Create and store a key
841        let key_id = KeyId::from_bytes([3; 16]);
842        let secret_key = SecretKey::from_bytes(vec![0xFF; 32], Algorithm::ChaCha20Poly1305).unwrap();
843        let metadata = KeyMetadata {
844            id: key_id.clone(),
845            base_id: key_id.clone(),
846            algorithm: Algorithm::ChaCha20Poly1305,
847            created_at: SystemTime::now(),
848            expires_at: None,
849            state: KeyState::Active,
850            version: 1,
851        };
852        let versioned_key = VersionedKey { key: secret_key, metadata };
853        
854        // Store and retrieve - verify round-trip works
855        store.store(versioned_key.clone()).unwrap();
856        let retrieved = store.retrieve(&key_id).unwrap();
857        
858        // Verify the key material and metadata match
859        assert_eq!(retrieved.key.expose_secret(), versioned_key.key.expose_secret());
860        assert_eq!(retrieved.metadata.id, key_id);
861        assert_eq!(retrieved.metadata.algorithm, Algorithm::ChaCha20Poly1305);
862        
863        // Verify file is actually encrypted (contains no plaintext key material)
864        let key_file = store.key_path(&key_id);
865        let file_contents = std::fs::read_to_string(key_file).unwrap();
866        
867        // The file should NOT contain the raw key bytes in any common format
868        assert!(!file_contents.contains("FFFFFFFF"));    // Hex representation
869        assert!(!file_contents.contains("/////"));       // Base64 for 0xFF repeated
870        assert!(!file_contents.contains("255"));         // JSON number representation
871        
872        // But it should contain the expected structure
873        assert!(file_contents.contains("ChaCha20Poly1305")); // Algorithm in metadata
874        assert!(file_contents.contains("encrypted_key"));     // Field name
875        
876        // Verify the encrypted_key field contains binary data (not readable text)
877        let parsed: serde_json::Value = serde_json::from_str(&file_contents).unwrap();
878        let encrypted_array = parsed["encrypted_key"].as_array().unwrap();
879        assert!(encrypted_array.len() > 32); // Should be nonce(12) + ciphertext(32) + tag(16) = 60 bytes minimum
880    }
881
882    #[test]
883    fn test_file_store_wrong_password_fails() {
884        use tempfile::tempdir;
885        use crate::{Algorithm, key::SecretKey};
886        use std::time::SystemTime;
887        
888        let temp_dir = tempdir().unwrap();
889        let config = StorageConfig { encrypted: true, ..Default::default() };
890        
891        // Create and populate store with correct password
892        let mut store1 = FileStore::new(temp_dir.path(), config.clone()).unwrap();
893        store1.init_with_password(b"correct-password").unwrap();
894        
895        let key_id = KeyId::from_bytes([4; 16]);
896        let secret_key = SecretKey::from_bytes(vec![0xAB; 32], Algorithm::Aes256Gcm).unwrap();
897        let metadata = KeyMetadata {
898            id: key_id.clone(),
899            base_id: key_id.clone(),
900            algorithm: Algorithm::Aes256Gcm,
901            created_at: SystemTime::now(),
902            expires_at: None,
903            state: KeyState::Active,
904            version: 1,
905        };
906        let versioned_key = VersionedKey { key: secret_key, metadata };
907        
908        store1.store(versioned_key).unwrap();
909        
910        // Try to read with wrong password - should fail
911        let mut store2 = FileStore::new(temp_dir.path(), config).unwrap();
912        store2.init_with_password(b"wrong-password").unwrap();
913        
914        // This should fail because decryption will fail with wrong master key
915        let result = store2.retrieve(&key_id);
916        assert!(result.is_err());
917        
918        // The error should be a crypto error (AEAD decryption failure)
919        match result.unwrap_err() {
920            crate::Error::CryptoError(_) => {}, // Expected
921            other => panic!("Expected crypto error, got: {:?}", other),
922        }
923    }
924
925    #[test]
926    fn test_file_store_persistence_across_restarts() {
927        use tempfile::tempdir;
928        use crate::{Algorithm, key::SecretKey};
929        use std::time::SystemTime;
930        
931        let temp_dir = tempdir().unwrap();
932        let config = StorageConfig { encrypted: true, ..Default::default() };
933        let password = b"persistent-test-password";
934        
935        let key_id = KeyId::from_bytes([5; 16]);
936        let original_key_bytes = vec![0x12; 32]; // 32 bytes for ChaCha20Poly1305
937        
938        // First session: create and store key
939        {
940            let mut store = FileStore::new(temp_dir.path(), config.clone()).unwrap();
941            store.init_with_password(password).unwrap();
942            
943            let secret_key = SecretKey::from_bytes(original_key_bytes.clone(), Algorithm::ChaCha20Poly1305).unwrap();
944            let metadata = KeyMetadata {
945                id: key_id.clone(),
946                base_id: key_id.clone(),
947                algorithm: Algorithm::ChaCha20Poly1305,
948                created_at: SystemTime::now(),
949                expires_at: None,
950                state: KeyState::Active,
951                version: 1,
952            };
953            let versioned_key = VersionedKey { key: secret_key, metadata };
954            
955            store.store(versioned_key).unwrap();
956        } // store goes out of scope, simulating restart
957        
958        // Second session: load and verify key
959        {
960            let mut store = FileStore::new(temp_dir.path(), config).unwrap();
961            store.init_with_password(password).unwrap();
962            
963            let retrieved = store.retrieve(&key_id).unwrap();
964            assert_eq!(retrieved.key.expose_secret(), &original_key_bytes);
965            assert_eq!(retrieved.metadata.algorithm, Algorithm::ChaCha20Poly1305);
966        }
967    }
968}