Skip to main content

auths_core/storage/
memory.rs

1//! In-memory key storage for testing.
2
3// Defauly memory fallback for non-iOS and non-macOS devices
4use crate::error::AgentError;
5use crate::storage::keychain::{IdentityDID, KeyAlias, KeyRole, KeyStorage};
6use once_cell::sync::Lazy;
7use std::collections::HashMap;
8use std::sync::{Arc, Mutex};
9
10/// An in-memory key storage implementation for fallback or testing.
11#[derive(Default)]
12pub struct MemoryStorage {
13    /// Internal mapping of alias -> (identity_did, role, encrypted_key_bytes)
14    data: HashMap<String, (IdentityDID, KeyRole, Vec<u8>)>,
15}
16
17impl std::fmt::Debug for MemoryStorage {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        f.debug_struct("MemoryStorage")
20            .field("key_count", &self.data.len())
21            .finish()
22    }
23}
24
25/// Global singleton memory store.
26pub static MEMORY_KEYCHAIN: Lazy<Mutex<MemoryStorage>> =
27    Lazy::new(|| Mutex::new(MemoryStorage::default()));
28
29/// A handle that interacts with the global in-memory keychain.
30/// Safe to use in tests or fallback environments.
31#[derive(Debug, Clone, Copy)]
32pub struct MemoryKeychainHandle;
33
34impl MemoryStorage {
35    /// Stores a key under the given alias.
36    pub fn store_key(
37        &mut self,
38        alias: &KeyAlias,
39        identity_did: &IdentityDID,
40        role: KeyRole,
41        encrypted_key_data: &[u8],
42    ) -> Result<(), AgentError> {
43        self.data.insert(
44            alias.as_str().to_string(),
45            (identity_did.clone(), role, encrypted_key_data.to_vec()),
46        );
47        Ok(())
48    }
49
50    /// Loads the key data for the given alias.
51    pub fn load_key(
52        &self,
53        alias: &KeyAlias,
54    ) -> Result<(IdentityDID, KeyRole, Vec<u8>), AgentError> {
55        self.data
56            .get(alias.as_str())
57            .cloned()
58            .ok_or(AgentError::KeyNotFound)
59    }
60
61    /// Deletes the key stored under the given alias.
62    pub fn delete_key(&mut self, alias: &KeyAlias) -> Result<(), AgentError> {
63        self.data.remove(alias.as_str());
64        Ok(())
65    }
66
67    /// Lists all stored aliases.
68    pub fn list_aliases(&self) -> Result<Vec<KeyAlias>, AgentError> {
69        Ok(self
70            .data
71            .keys()
72            .map(|k| KeyAlias::new_unchecked(k.clone()))
73            .collect())
74    }
75
76    /// Lists aliases associated with a specific identity.
77    pub fn list_aliases_for_identity(
78        &self,
79        identity_did: &IdentityDID,
80    ) -> Result<Vec<KeyAlias>, AgentError> {
81        let aliases = self
82            .data
83            .iter()
84            .filter_map(|(alias, (did, _role, _))| {
85                if did == identity_did {
86                    Some(KeyAlias::new_unchecked(alias.clone()))
87                } else {
88                    None
89                }
90            })
91            .collect();
92        Ok(aliases)
93    }
94
95    /// Returns the identity DID associated with the given alias.
96    pub fn get_identity_for_alias(&self, alias: &KeyAlias) -> Result<IdentityDID, AgentError> {
97        self.data
98            .get(alias.as_str())
99            .map(|(did, _role, _)| did.clone())
100            .ok_or(AgentError::KeyNotFound)
101    }
102
103    /// Removes all stored keys.
104    pub fn clear_all(&mut self) -> Result<(), AgentError> {
105        self.data.clear();
106        Ok(())
107    }
108
109    /// Returns the storage backend name.
110    pub fn backend_name(&self) -> &'static str {
111        "Memory"
112    }
113}
114
115impl KeyStorage for MemoryKeychainHandle {
116    fn store_key(
117        &self,
118        alias: &KeyAlias,
119        identity_did: &IdentityDID,
120        role: KeyRole,
121        encrypted_key_data: &[u8],
122    ) -> Result<(), AgentError> {
123        MEMORY_KEYCHAIN
124            .lock()
125            .map_err(|_| AgentError::MutexError("global memory keychain".into()))?
126            .store_key(alias, identity_did, role, encrypted_key_data)
127    }
128
129    fn load_key(&self, alias: &KeyAlias) -> Result<(IdentityDID, KeyRole, Vec<u8>), AgentError> {
130        MEMORY_KEYCHAIN
131            .lock()
132            .map_err(|_| AgentError::MutexError("global memory keychain".into()))?
133            .load_key(alias)
134    }
135
136    fn delete_key(&self, alias: &KeyAlias) -> Result<(), AgentError> {
137        MEMORY_KEYCHAIN
138            .lock()
139            .map_err(|_| AgentError::MutexError("global memory keychain".into()))?
140            .delete_key(alias)
141    }
142
143    fn list_aliases(&self) -> Result<Vec<KeyAlias>, AgentError> {
144        MEMORY_KEYCHAIN
145            .lock()
146            .map_err(|_| AgentError::MutexError("global memory keychain".into()))?
147            .list_aliases()
148    }
149
150    fn list_aliases_for_identity(
151        &self,
152        identity_did: &IdentityDID,
153    ) -> Result<Vec<KeyAlias>, AgentError> {
154        MEMORY_KEYCHAIN
155            .lock()
156            .map_err(|_| AgentError::MutexError("global memory keychain".into()))?
157            .list_aliases_for_identity(identity_did)
158    }
159
160    fn get_identity_for_alias(&self, alias: &KeyAlias) -> Result<IdentityDID, AgentError> {
161        MEMORY_KEYCHAIN
162            .lock()
163            .map_err(|_| AgentError::MutexError("global memory keychain".into()))?
164            .get_identity_for_alias(alias)
165    }
166
167    fn backend_name(&self) -> &'static str {
168        "Memory"
169    }
170}
171
172/// A per-instance in-memory keychain that does NOT share the global singleton.
173///
174/// Args:
175/// * (none — carries its own `Arc<Mutex<MemoryStorage>>`)
176///
177/// Usage:
178/// ```rust,ignore
179/// let kc = IsolatedKeychainHandle::new();
180/// kc.store_key(&alias, &did, &data)?;
181/// ```
182#[derive(Debug, Clone)]
183pub struct IsolatedKeychainHandle {
184    store: Arc<Mutex<MemoryStorage>>,
185}
186
187impl IsolatedKeychainHandle {
188    /// Creates a fresh, empty isolated keychain.
189    pub fn new() -> Self {
190        Self {
191            store: Arc::new(Mutex::new(MemoryStorage::default())),
192        }
193    }
194}
195
196impl Default for IsolatedKeychainHandle {
197    fn default() -> Self {
198        Self::new()
199    }
200}
201
202impl KeyStorage for IsolatedKeychainHandle {
203    fn store_key(
204        &self,
205        alias: &KeyAlias,
206        identity_did: &IdentityDID,
207        role: KeyRole,
208        encrypted_key_data: &[u8],
209    ) -> Result<(), AgentError> {
210        self.store
211            .lock()
212            .map_err(|_| AgentError::MutexError("isolated memory keychain".into()))?
213            .store_key(alias, identity_did, role, encrypted_key_data)
214    }
215
216    fn load_key(&self, alias: &KeyAlias) -> Result<(IdentityDID, KeyRole, Vec<u8>), AgentError> {
217        self.store
218            .lock()
219            .map_err(|_| AgentError::MutexError("isolated memory keychain".into()))?
220            .load_key(alias)
221    }
222
223    fn delete_key(&self, alias: &KeyAlias) -> Result<(), AgentError> {
224        self.store
225            .lock()
226            .map_err(|_| AgentError::MutexError("isolated memory keychain".into()))?
227            .delete_key(alias)
228    }
229
230    fn list_aliases(&self) -> Result<Vec<KeyAlias>, AgentError> {
231        self.store
232            .lock()
233            .map_err(|_| AgentError::MutexError("isolated memory keychain".into()))?
234            .list_aliases()
235    }
236
237    fn list_aliases_for_identity(
238        &self,
239        identity_did: &IdentityDID,
240    ) -> Result<Vec<KeyAlias>, AgentError> {
241        self.store
242            .lock()
243            .map_err(|_| AgentError::MutexError("isolated memory keychain".into()))?
244            .list_aliases_for_identity(identity_did)
245    }
246
247    fn get_identity_for_alias(&self, alias: &KeyAlias) -> Result<IdentityDID, AgentError> {
248        self.store
249            .lock()
250            .map_err(|_| AgentError::MutexError("isolated memory keychain".into()))?
251            .get_identity_for_alias(alias)
252    }
253
254    fn backend_name(&self) -> &'static str {
255        "IsolatedMemory"
256    }
257}
258
259/// Returns a cleared memory keychain handle, used in tests.
260#[cfg(any(test, feature = "test-utils"))]
261pub fn get_test_memory_keychain() -> Box<dyn KeyStorage + Send + Sync> {
262    MEMORY_KEYCHAIN
263        .lock()
264        .unwrap_or_else(|e| e.into_inner())
265        .clear_all()
266        .ok();
267    Box::new(MemoryKeychainHandle)
268}