Development Documentation (main branch) - For stable release docs, see docs.rs/eidetica

eidetica/user/
key_manager.rs

1//! User key manager for session-based key management
2//!
3//! Manages decrypted private keys during a user session.
4//! Keys are decrypted on login and kept in memory for the session duration.
5
6use std::collections::HashMap;
7
8use zeroize::{Zeroize, ZeroizeOnDrop};
9
10use super::{
11    crypto::{decrypt_private_key, derive_encryption_key, encrypt_private_key},
12    errors::UserError,
13    types::{KeyStorage, UserKey},
14};
15use crate::{
16    Result,
17    auth::crypto::{PrivateKey, PublicKey},
18};
19
20/// Internal key manager that holds decrypted keys during user session
21///
22/// # Security
23///
24/// This struct holds sensitive cryptographic material in memory:
25/// - `decrypted_keys`: Contains plaintext PrivateKeys (zeroized on drop)
26/// - `encryption_key`: Password-derived key (zeroized via manual Zeroize impl), None for passwordless users
27///
28/// All sensitive data is zeroized when the struct is dropped.
29pub struct UserKeyManager {
30    /// Decrypted keys (PublicKey → PrivateKey)
31    decrypted_keys: HashMap<PublicKey, PrivateKey>,
32
33    /// Key metadata (loaded from user database)
34    key_metadata: HashMap<PublicKey, UserKey>,
35
36    /// User's password-derived encryption key (for saving new keys)
37    /// None for passwordless users
38    encryption_key: Option<Vec<u8>>,
39}
40
41impl UserKeyManager {
42    /// Create from user password and encrypted keys
43    ///
44    /// Decrypts all provided keys using the password-derived encryption key.
45    ///
46    /// # Arguments
47    /// * `password` - The user's password
48    /// * `salt` - The password salt (base64 encoded string)
49    /// * `encrypted_keys` - Vec of encrypted UserKey entries from database
50    ///
51    /// # Returns
52    /// A UserKeyManager with all keys decrypted and ready for use
53    pub fn new(password: &str, salt: &str, encrypted_keys: Vec<UserKey>) -> Result<Self> {
54        // Derive encryption key from password
55        let encryption_key = derive_encryption_key(password, salt)?;
56
57        // Create empty manager with pre-allocated capacity
58        let capacity = encrypted_keys.len();
59        let mut manager = Self {
60            decrypted_keys: HashMap::with_capacity(capacity),
61            key_metadata: HashMap::with_capacity(capacity),
62            encryption_key: Some(encryption_key),
63        };
64
65        // Add all keys using add_key
66        for user_key in encrypted_keys {
67            manager.add_key(user_key)?;
68        }
69
70        Ok(manager)
71    }
72
73    /// Create from unencrypted keys (for passwordless users)
74    ///
75    /// Keys are stored and loaded unencrypted for performance.
76    ///
77    /// # Arguments
78    /// * `keys` - Vec of UserKey entries with unencrypted keys
79    ///
80    /// # Returns
81    /// A UserKeyManager with all keys ready for use
82    pub fn new_passwordless(keys: Vec<UserKey>) -> Result<Self> {
83        let capacity = keys.len();
84        let mut manager = Self {
85            decrypted_keys: HashMap::with_capacity(capacity),
86            key_metadata: HashMap::with_capacity(capacity),
87            encryption_key: None,
88        };
89
90        // Add all keys
91        for user_key in keys {
92            manager.add_key(user_key)?;
93        }
94
95        Ok(manager)
96    }
97
98    /// Get a decrypted signing key
99    ///
100    /// # Arguments
101    /// * `key_id` - The public key identifier
102    ///
103    /// # Returns
104    /// A reference to the PrivateKey if found
105    pub fn get_signing_key(&self, key_id: &PublicKey) -> Option<&PrivateKey> {
106        self.decrypted_keys.get(key_id)
107    }
108
109    /// Add a key to the manager from metadata
110    ///
111    /// Handles both encrypted and unencrypted keys based on metadata.
112    /// Use `serialize_keys()` to get updated keys for storage.
113    ///
114    /// # Arguments
115    /// * `metadata` - The UserKey metadata with storage info
116    ///
117    /// # Returns
118    /// Ok(()) if the key was successfully added
119    pub fn add_key(&mut self, metadata: UserKey) -> Result<()> {
120        // Decrypt or extract the key based on storage type
121        let signing_key = match &metadata.storage {
122            KeyStorage::Encrypted {
123                algorithm,
124                ciphertext,
125                nonce,
126            } => {
127                // Validate encryption algorithm
128                if algorithm != "aes-256-gcm" {
129                    return Err(UserError::DecryptionFailed {
130                        reason: format!("Unsupported encryption algorithm: {algorithm}"),
131                    }
132                    .into());
133                }
134                // Encrypted key - needs decryption
135                let encryption_key =
136                    self.encryption_key
137                        .as_ref()
138                        .ok_or_else(|| UserError::PasswordRequired {
139                            operation: "decrypt encrypted key".to_string(),
140                        })?;
141                decrypt_private_key(ciphertext, nonce, encryption_key)?
142            }
143            KeyStorage::Unencrypted { key } => {
144                // Unencrypted key - use directly
145                key.clone()
146            }
147        };
148
149        let key_id = metadata.key_id.clone();
150        self.decrypted_keys.insert(key_id.clone(), signing_key);
151        self.key_metadata.insert(key_id, metadata);
152
153        Ok(())
154    }
155
156    /// Serialize all keys for storage
157    ///
158    /// Returns UserKey metadata suitable for storing in the database.
159    /// Encrypted keys are re-encrypted with the current encryption key.
160    /// Unencrypted keys are serialized directly.
161    ///
162    /// Keys are returned in sorted order by key_id for deterministic output.
163    ///
164    /// # Returns
165    /// Vec of UserKey with updated storage, sorted by key_id
166    pub fn serialize_keys(&self) -> Result<Vec<UserKey>> {
167        let mut serialized = Vec::new();
168
169        for (key_id, signing_key) in &self.decrypted_keys {
170            // Get metadata
171            let metadata = self
172                .key_metadata
173                .get(key_id)
174                .ok_or_else(|| UserError::KeyNotFound {
175                    key_id: key_id.to_string(),
176                })?;
177
178            // Build storage based on current storage type
179            let storage = match &metadata.storage {
180                KeyStorage::Encrypted { algorithm, .. } => {
181                    // Re-encrypt the key
182                    let encryption_key = self.encryption_key.as_ref().ok_or_else(|| {
183                        UserError::PasswordRequired {
184                            operation: "encrypt key".to_string(),
185                        }
186                    })?;
187                    let (ciphertext, nonce) = encrypt_private_key(signing_key, encryption_key)?;
188                    KeyStorage::Encrypted {
189                        algorithm: algorithm.clone(),
190                        ciphertext,
191                        nonce,
192                    }
193                }
194                KeyStorage::Unencrypted { .. } => {
195                    // Store key directly
196                    KeyStorage::Unencrypted {
197                        key: signing_key.clone(),
198                    }
199                }
200            };
201
202            // Create updated UserKey
203            let updated_key = UserKey {
204                key_id: key_id.clone(),
205                storage,
206                display_name: metadata.display_name.clone(),
207                created_at: metadata.created_at,
208                last_used: metadata.last_used,
209                is_default: metadata.is_default,
210                database_sigkeys: metadata.database_sigkeys.clone(),
211            };
212
213            serialized.push(updated_key);
214        }
215
216        // Sort by key_id string representation for deterministic output
217        serialized.sort_by(|a, b| a.key_id.to_string().cmp(&b.key_id.to_string()));
218
219        Ok(serialized)
220    }
221
222    /// Clear all decrypted keys from memory
223    ///
224    /// Explicitly zeroizes all sensitive key material.
225    /// Called automatically on Drop via ZeroizeOnDrop, but can be called manually to end session early.
226    pub fn clear(&mut self) {
227        self.zeroize();
228    }
229
230    /// List all key IDs managed by this manager
231    ///
232    /// Returns key IDs sorted by creation timestamp (oldest first) for deterministic behavior.
233    pub fn list_key_ids(&self) -> Vec<PublicKey> {
234        let mut keys: Vec<(PublicKey, i64)> = self
235            .decrypted_keys
236            .keys()
237            .filter_map(|key_id| {
238                self.key_metadata
239                    .get(key_id)
240                    .map(|meta| (key_id.clone(), meta.created_at))
241            })
242            .collect();
243
244        // Sort by created_at timestamp (oldest first)
245        keys.sort_by_key(|(_, created_at)| *created_at);
246
247        // Return just the key IDs
248        keys.into_iter().map(|(key_id, _)| key_id).collect()
249    }
250
251    /// Get metadata for a key
252    pub fn get_key_metadata(&self, key_id: &PublicKey) -> Option<&UserKey> {
253        self.key_metadata.get(key_id)
254    }
255
256    /// Get the default key ID
257    ///
258    /// Returns the key marked as is_default=true, or falls back to the
259    /// oldest key by creation timestamp if no default is explicitly set.
260    ///
261    /// # Returns
262    /// The PublicKey of the default key, or None if there are no keys
263    pub fn get_default_key_id(&self) -> Option<PublicKey> {
264        // First try to find a key explicitly marked as default
265        for (key_id, metadata) in &self.key_metadata {
266            if metadata.is_default {
267                return Some(key_id.clone());
268            }
269        }
270
271        // Fall back to oldest key by creation timestamp
272        self.list_key_ids().first().cloned()
273    }
274
275    /// Get the encryption key for encrypting new keys
276    ///
277    /// Returns None for passwordless users.
278    ///
279    /// # Security Considerations
280    ///
281    /// This method exposes the raw password-derived encryption key. It should only be used
282    /// internally within the user module for operations that require encrypting new key material.
283    ///
284    /// **WARNING**: Exposing this key outside the user module could compromise the security of
285    /// all encrypted keys. The key is derived from the user's password and is capable of
286    /// decrypting all stored private keys.
287    ///
288    /// # Usage
289    ///
290    /// This is currently used internally for:
291    /// - Creating properly encrypted UserKey entries when adding new keys
292    /// - Re-encrypting keys when changing passwords
293    ///
294    /// The `pub(super)` visibility ensures this remains internal to the user module only.
295    #[allow(dead_code)]
296    pub(super) fn encryption_key(&self) -> Option<&[u8]> {
297        self.encryption_key.as_deref()
298    }
299}
300
301impl Zeroize for UserKeyManager {
302    fn zeroize(&mut self) {
303        // Zeroize the encryption key if present
304        if let Some(key) = &mut self.encryption_key {
305            key.zeroize();
306        }
307
308        // Clear the HashMap - this drops all PrivateKeys (which zeroize via Zeroize impl)
309        self.decrypted_keys.clear();
310
311        // Clear metadata (contains no plaintext sensitive data)
312        self.key_metadata.clear();
313    }
314}
315
316impl ZeroizeOnDrop for UserKeyManager {}
317
318#[cfg(test)]
319mod tests {
320    use super::*;
321    use crate::auth::crypto::generate_keypair;
322    use crate::user::crypto::{encrypt_private_key, hash_password};
323    use crate::{Clock, Error, SystemClock};
324
325    fn create_test_user_key(signing_key: &PrivateKey, encryption_key: &[u8]) -> UserKey {
326        let (ciphertext, nonce) = encrypt_private_key(signing_key, encryption_key).unwrap();
327        let key_id = signing_key.public_key();
328
329        UserKey {
330            key_id,
331            storage: KeyStorage::Encrypted {
332                algorithm: "aes-256-gcm".to_string(),
333                ciphertext,
334                nonce,
335            },
336            display_name: Some("Test key".to_string()),
337            created_at: SystemClock.now_secs(),
338            last_used: None,
339            is_default: false,
340            database_sigkeys: HashMap::new(),
341        }
342    }
343
344    #[test]
345    #[cfg_attr(miri, ignore)] // Argon2 is extremely slow under Miri
346    fn test_key_manager_new() {
347        let password = "test_password";
348        let (_, salt) = hash_password(password).unwrap();
349        let encryption_key = derive_encryption_key(password, &salt).unwrap();
350
351        // Create some test keys
352        let (key1, pub1) = generate_keypair();
353        let (key2, pub2) = generate_keypair();
354
355        let user_key1 = create_test_user_key(&key1, &encryption_key);
356        let user_key2 = create_test_user_key(&key2, &encryption_key);
357
358        // Create key manager
359        let manager = UserKeyManager::new(password, &salt, vec![user_key1, user_key2]).unwrap();
360
361        // Verify keys were decrypted
362        assert!(manager.get_signing_key(&pub1).is_some());
363        assert!(manager.get_signing_key(&pub2).is_some());
364
365        // Non-existent key returns None
366        let (_, nonexistent) = generate_keypair();
367        assert!(manager.get_signing_key(&nonexistent).is_none());
368    }
369
370    #[test]
371    #[cfg_attr(miri, ignore)] // Argon2 is extremely slow under Miri
372    fn test_key_manager_get_signing_key() {
373        let password = "test_password";
374        let (_, salt) = hash_password(password).unwrap();
375        let encryption_key = derive_encryption_key(password, &salt).unwrap();
376
377        let (key1, pub1) = generate_keypair();
378        let user_key1 = create_test_user_key(&key1, &encryption_key);
379
380        let manager = UserKeyManager::new(password, &salt, vec![user_key1]).unwrap();
381
382        // Get key and verify it's the same
383        let retrieved_key = manager.get_signing_key(&pub1).unwrap();
384        assert_eq!(retrieved_key.to_bytes(), key1.to_bytes());
385    }
386
387    #[test]
388    #[cfg_attr(miri, ignore)] // Argon2 is extremely slow under Miri
389    fn test_key_manager_signing_key_produces_correct_public_key() {
390        let password = "test_password";
391        let (_, salt) = hash_password(password).unwrap();
392        let encryption_key = derive_encryption_key(password, &salt).unwrap();
393
394        let (key1, pub_key1) = generate_keypair();
395        let user_key1 = create_test_user_key(&key1, &encryption_key);
396
397        let manager = UserKeyManager::new(password, &salt, vec![user_key1]).unwrap();
398
399        // Verify that the signing key's public key matches
400        let signing_key = manager.get_signing_key(&pub_key1).unwrap();
401        assert_eq!(signing_key.public_key(), pub_key1);
402    }
403
404    #[test]
405    #[cfg_attr(miri, ignore)] // Argon2 is extremely slow under Miri
406    fn test_key_manager_multiple_keys() {
407        let password = "test_password";
408        let (_, salt) = hash_password(password).unwrap();
409        let encryption_key = derive_encryption_key(password, &salt).unwrap();
410
411        // Create multiple keys
412        let (key1, pub_key1) = generate_keypair();
413        let (key2, pub_key2) = generate_keypair();
414        let (key3, pub_key3) = generate_keypair();
415
416        let user_key1 = create_test_user_key(&key1, &encryption_key);
417        let user_key2 = create_test_user_key(&key2, &encryption_key);
418        let user_key3 = create_test_user_key(&key3, &encryption_key);
419
420        let manager =
421            UserKeyManager::new(password, &salt, vec![user_key1, user_key2, user_key3]).unwrap();
422
423        // Verify all keys are retrievable
424        assert!(manager.get_signing_key(&pub_key1).is_some());
425        assert!(manager.get_signing_key(&pub_key2).is_some());
426        assert!(manager.get_signing_key(&pub_key3).is_some());
427
428        // Verify all public keys are different
429        assert_ne!(pub_key1, pub_key2);
430        assert_ne!(pub_key2, pub_key3);
431        assert_ne!(pub_key1, pub_key3);
432    }
433
434    #[test]
435    #[cfg_attr(miri, ignore)] // Uses SystemTime for timestamp
436    fn test_key_manager_passwordless() {
437        // Create passwordless keys
438        let (key1, pub_key1) = generate_keypair();
439        let (key2, pub_key2) = generate_keypair();
440
441        let user_key1 = UserKey {
442            key_id: pub_key1.clone(),
443            storage: KeyStorage::Unencrypted { key: key1.clone() },
444            display_name: Some("Key 1".to_string()),
445            created_at: SystemClock.now_secs(),
446            last_used: None,
447            is_default: true,
448            database_sigkeys: HashMap::new(),
449        };
450
451        let user_key2 = UserKey {
452            key_id: pub_key2.clone(),
453            storage: KeyStorage::Unencrypted { key: key2.clone() },
454            display_name: Some("Key 2".to_string()),
455            created_at: SystemClock.now_secs(),
456            last_used: None,
457            is_default: false,
458            database_sigkeys: HashMap::new(),
459        };
460
461        let manager = UserKeyManager::new_passwordless(vec![user_key1, user_key2]).unwrap();
462
463        // Verify keys are retrievable
464        assert!(manager.get_signing_key(&pub_key1).is_some());
465        assert!(manager.get_signing_key(&pub_key2).is_some());
466    }
467
468    #[test]
469    #[cfg_attr(miri, ignore)] // Argon2 is extremely slow under Miri
470    fn test_key_manager_add_key() {
471        let password = "test_password";
472        let (_, salt) = hash_password(password).unwrap();
473        let encryption_key = derive_encryption_key(password, &salt).unwrap();
474
475        let (key1, _) = generate_keypair();
476        let user_key1 = create_test_user_key(&key1, &encryption_key);
477
478        let mut manager = UserKeyManager::new(password, &salt, vec![user_key1]).unwrap();
479
480        // Add a new key - only pass metadata, key is decrypted internally
481        let (key2, pub2) = generate_keypair();
482        let user_key2 = create_test_user_key(&key2, &encryption_key);
483        manager.add_key(user_key2).unwrap();
484
485        // Verify it was added and decrypted correctly
486        assert!(manager.get_signing_key(&pub2).is_some());
487        let retrieved_key = manager.get_signing_key(&pub2).unwrap();
488        assert_eq!(retrieved_key.to_bytes(), key2.to_bytes());
489    }
490
491    #[test]
492    #[cfg_attr(miri, ignore)] // Argon2 is extremely slow under Miri
493    fn test_key_manager_serialize_keys() {
494        let password = "test_password";
495        let (_, salt) = hash_password(password).unwrap();
496        let encryption_key = derive_encryption_key(password, &salt).unwrap();
497
498        let (key1, pub1) = generate_keypair();
499        let user_key1 = create_test_user_key(&key1, &encryption_key);
500
501        let manager = UserKeyManager::new(password, &salt, vec![user_key1]).unwrap();
502
503        // Serialize keys
504        let serialized = manager.serialize_keys().unwrap();
505        assert_eq!(serialized.len(), 1);
506
507        // Verify the serialized key has the correct key_id
508        let serialized_key = &serialized[0];
509        assert_eq!(serialized_key.key_id, pub1);
510
511        // Extract ciphertext and nonce from storage
512        let (ciphertext, nonce) = match &serialized_key.storage {
513            KeyStorage::Encrypted {
514                ciphertext, nonce, ..
515            } => (ciphertext, nonce),
516            KeyStorage::Unencrypted { .. } => panic!("Expected encrypted key"),
517        };
518
519        let decrypted = decrypt_private_key(ciphertext, nonce, &encryption_key).unwrap();
520        assert_eq!(decrypted.to_bytes(), key1.to_bytes());
521    }
522
523    #[test]
524    #[cfg_attr(miri, ignore)] // Argon2 is extremely slow under Miri
525    fn test_key_manager_serialize_keys_sorted() {
526        let password = "test_password";
527        let (_, salt) = hash_password(password).unwrap();
528        let encryption_key = derive_encryption_key(password, &salt).unwrap();
529
530        let (key1, _) = generate_keypair();
531        let (key2, _) = generate_keypair();
532        let (key3, _) = generate_keypair();
533
534        let user_key1 = create_test_user_key(&key1, &encryption_key);
535        let user_key2 = create_test_user_key(&key2, &encryption_key);
536        let user_key3 = create_test_user_key(&key3, &encryption_key);
537
538        let manager =
539            UserKeyManager::new(password, &salt, vec![user_key1, user_key2, user_key3]).unwrap();
540
541        // Serialize should return sorted keys (by string representation)
542        let serialized = manager.serialize_keys().unwrap();
543        assert_eq!(serialized.len(), 3);
544
545        // Verify keys are sorted by their string representation
546        let ids: Vec<String> = serialized.iter().map(|k| k.key_id.to_string()).collect();
547        let mut sorted_ids = ids.clone();
548        sorted_ids.sort();
549        assert_eq!(ids, sorted_ids);
550    }
551
552    #[test]
553    #[cfg_attr(miri, ignore)] // Argon2 is extremely slow under Miri
554    fn test_key_manager_clear() {
555        let password = "test_password";
556        let (_, salt) = hash_password(password).unwrap();
557        let encryption_key = derive_encryption_key(password, &salt).unwrap();
558
559        let (key1, pub1) = generate_keypair();
560        let user_key1 = create_test_user_key(&key1, &encryption_key);
561
562        let mut manager = UserKeyManager::new(password, &salt, vec![user_key1]).unwrap();
563
564        // Verify key exists
565        assert!(manager.get_signing_key(&pub1).is_some());
566
567        // Clear
568        manager.clear();
569
570        // Verify keys are gone
571        assert!(manager.get_signing_key(&pub1).is_none());
572        assert_eq!(manager.list_key_ids().len(), 0);
573    }
574
575    #[test]
576    #[cfg_attr(miri, ignore)] // Argon2 is extremely slow under Miri
577    fn test_key_manager_list_key_ids() {
578        let password = "test_password";
579        let (_, salt) = hash_password(password).unwrap();
580        let encryption_key = derive_encryption_key(password, &salt).unwrap();
581
582        let (key1, pub1) = generate_keypair();
583        let (key2, pub2) = generate_keypair();
584        let user_key1 = create_test_user_key(&key1, &encryption_key);
585        let user_key2 = create_test_user_key(&key2, &encryption_key);
586
587        let manager = UserKeyManager::new(password, &salt, vec![user_key1, user_key2]).unwrap();
588
589        let key_ids = manager.list_key_ids();
590        assert_eq!(key_ids.len(), 2);
591        assert!(key_ids.contains(&pub1));
592        assert!(key_ids.contains(&pub2));
593    }
594
595    #[test]
596    #[cfg_attr(miri, ignore)] // Argon2 is extremely slow under Miri
597    fn test_key_manager_list_key_ids_sorted_by_timestamp() {
598        let password = "test_password";
599        let (_, salt) = hash_password(password).unwrap();
600        let encryption_key = derive_encryption_key(password, &salt).unwrap();
601
602        // Create keys with specific timestamps
603        let (key_new, pub_new) = generate_keypair();
604        let (key_old, pub_old) = generate_keypair();
605        let (key_mid, pub_mid) = generate_keypair();
606
607        let (ct_new, nonce_new) = encrypt_private_key(&key_new, &encryption_key).unwrap();
608        let (ct_old, nonce_old) = encrypt_private_key(&key_old, &encryption_key).unwrap();
609        let (ct_mid, nonce_mid) = encrypt_private_key(&key_mid, &encryption_key).unwrap();
610
611        // Create keys with explicit timestamps (old, middle, new)
612        let user_key_old = UserKey {
613            key_id: pub_old.clone(),
614            storage: KeyStorage::Encrypted {
615                algorithm: "aes-256-gcm".to_string(),
616                ciphertext: ct_old,
617                nonce: nonce_old,
618            },
619            display_name: Some("Old Key".to_string()),
620            created_at: 1000, // Oldest
621            last_used: None,
622            is_default: true, // Mark oldest as default
623            database_sigkeys: HashMap::new(),
624        };
625
626        let user_key_mid = UserKey {
627            key_id: pub_mid.clone(),
628            storage: KeyStorage::Encrypted {
629                algorithm: "aes-256-gcm".to_string(),
630                ciphertext: ct_mid,
631                nonce: nonce_mid,
632            },
633            display_name: Some("Mid Key".to_string()),
634            created_at: 2000, // Middle
635            last_used: None,
636            is_default: false,
637            database_sigkeys: HashMap::new(),
638        };
639
640        let user_key_new = UserKey {
641            key_id: pub_new.clone(),
642            storage: KeyStorage::Encrypted {
643                algorithm: "aes-256-gcm".to_string(),
644                ciphertext: ct_new,
645                nonce: nonce_new,
646            },
647            display_name: Some("New Key".to_string()),
648            created_at: 3000, // Newest
649            last_used: None,
650            is_default: false,
651            database_sigkeys: HashMap::new(),
652        };
653
654        // Add keys in non-chronological order
655        let manager = UserKeyManager::new(
656            password,
657            &salt,
658            vec![user_key_new, user_key_old, user_key_mid],
659        )
660        .unwrap();
661
662        // list_key_ids() should return keys sorted by created_at (oldest first)
663        let key_ids = manager.list_key_ids();
664        assert_eq!(key_ids.len(), 3);
665        assert_eq!(key_ids[0], pub_old); // created_at: 1000
666        assert_eq!(key_ids[1], pub_mid); // created_at: 2000
667        assert_eq!(key_ids[2], pub_new); // created_at: 3000
668    }
669
670    #[test]
671    #[cfg_attr(miri, ignore)] // Argon2 is extremely slow under Miri
672    fn test_key_manager_get_metadata() {
673        let password = "test_password";
674        let (_, salt) = hash_password(password).unwrap();
675        let encryption_key = derive_encryption_key(password, &salt).unwrap();
676
677        let (key1, pub1) = generate_keypair();
678        let user_key1 = create_test_user_key(&key1, &encryption_key);
679
680        let manager = UserKeyManager::new(password, &salt, vec![user_key1]).unwrap();
681
682        let metadata = manager.get_key_metadata(&pub1).unwrap();
683        assert_eq!(metadata.key_id, pub1);
684        assert_eq!(metadata.display_name, Some("Test key".to_string()));
685    }
686
687    #[test]
688    #[cfg_attr(miri, ignore)] // Argon2 is extremely slow under Miri
689    fn test_key_manager_wrong_password() {
690        let correct_password = "correct_password";
691        let wrong_password = "wrong_password";
692        let (_, salt) = hash_password(correct_password).unwrap();
693        let encryption_key = derive_encryption_key(correct_password, &salt).unwrap();
694
695        // Create keys with correct password
696        let (key1, _) = generate_keypair();
697        let user_key1 = create_test_user_key(&key1, &encryption_key);
698
699        // Attempt to create manager with wrong password - should fail
700        let result = UserKeyManager::new(wrong_password, &salt, vec![user_key1]);
701        assert!(result.is_err());
702
703        // The error should be a decryption failure
704        if let Err(err) = result {
705            assert!(matches!(
706                err,
707                Error::User(UserError::DecryptionFailed { .. })
708            ));
709        }
710    }
711
712    #[test]
713    #[cfg_attr(miri, ignore)] // Argon2 is extremely slow under Miri
714    fn test_key_manager_rejects_unknown_encryption_algorithm() {
715        let password = "test_password";
716        let (_, salt) = hash_password(password).unwrap();
717        let encryption_key = derive_encryption_key(password, &salt).unwrap();
718
719        let (key1, _) = generate_keypair();
720        let mut user_key = create_test_user_key(&key1, &encryption_key);
721
722        // Tamper with the algorithm field
723        if let KeyStorage::Encrypted {
724            ref mut algorithm, ..
725        } = user_key.storage
726        {
727            *algorithm = "aes-128-gcm".to_string();
728        }
729
730        let Err(err) = UserKeyManager::new(password, &salt, vec![user_key]) else {
731            panic!("Expected error for unsupported algorithm");
732        };
733        assert!(matches!(
734            err,
735            Error::User(UserError::DecryptionFailed { .. })
736        ));
737        assert!(
738            err.to_string().contains("Unsupported encryption algorithm"),
739            "error message was: {err}"
740        );
741    }
742}