1use std::collections::HashMap;
17use std::ffi::c_void;
18
19use crate::backend::{Compartment, Options, StoreBackend, StoreInstance};
20use crate::error::{Result, ValueNotFoundSnafu};
21use crate::register_backend;
22
23#[derive(Default)]
25struct Scope {
26 private: HashMap<String, *mut c_void>,
27 public: HashMap<String, (*mut c_void, Vec<u8>)>,
28}
29
30pub struct MemoryBackend;
33
34impl StoreBackend for MemoryBackend {
35 fn name(&self) -> &'static str {
36 "memory"
37 }
38
39 fn open(&self, _opts: &Options) -> Result<Box<dyn StoreInstance>> {
40 Ok(Box::new(MemoryInstance::default()))
41 }
42}
43
44register_backend!(MemoryBackend);
45
46#[derive(Default)]
47pub struct MemoryInstance {
48 scopes: HashMap<(String, String), Scope>,
49}
50
51impl MemoryInstance {
52 fn scope(&self, module: &str, app: &str) -> Option<&Scope> {
53 self.scopes.get(&(module.to_string(), app.to_string()))
54 }
55
56 fn scope_mut(&mut self, module: &str, app: &str) -> &mut Scope {
57 self.scopes
58 .entry((module.to_string(), app.to_string()))
59 .or_default()
60 }
61}
62
63impl StoreInstance for MemoryInstance {
64 fn put_secret(
65 &mut self,
66 module: &str,
67 app: &str,
68 key_id: &str,
69 key: *mut c_void,
70 ) -> Result<()> {
71 self.scope_mut(module, app)
72 .private
73 .insert(key_id.to_string(), key);
74 Ok(())
75 }
76
77 fn get_secret(&self, module: &str, app: &str, key_id: &str) -> Result<*mut c_void> {
78 self.scope(module, app)
79 .and_then(|s| s.private.get(key_id))
80 .copied()
81 .ok_or_else(|| ValueNotFoundSnafu.build())
82 }
83
84 fn put_public(
85 &mut self,
86 module: &str,
87 app: &str,
88 identity: &str,
89 key: *mut c_void,
90 sig: &[u8],
91 ) -> Result<()> {
92 self.scope_mut(module, app)
93 .public
94 .insert(identity.to_string(), (key, sig.to_vec()));
95 Ok(())
96 }
97
98 fn get_public(
99 &self,
100 module: &str,
101 app: &str,
102 identity: &str,
103 ) -> Result<(*mut c_void, Vec<u8>)> {
104 self.scope(module, app)
105 .and_then(|s| s.public.get(identity))
106 .map(|(k, sig)| (*k, sig.clone()))
107 .ok_or_else(|| ValueNotFoundSnafu.build())
108 }
109
110 fn enumerate(
111 &self,
112 module: &str,
113 app: &str,
114 compartment: Compartment,
115 ) -> Result<Vec<(*mut c_void, String)>> {
116 let Some(scope) = self.scope(module, app) else {
117 return Ok(Vec::new());
118 };
119 let entries: Vec<(*mut c_void, String)> = match compartment {
120 Compartment::Private => scope.private.iter().map(|(k, v)| (*v, k.clone())).collect(),
121 Compartment::Public => scope
122 .public
123 .iter()
124 .map(|(id, (k, _))| (*k, id.clone()))
125 .collect(),
126 };
127 Ok(entries)
128 }
129}
130
131unsafe impl Send for MemoryInstance {}
138unsafe impl Sync for MemoryInstance {}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use crate::backend::{StoreBackend, StoreInstance};
144
145 fn open() -> Box<dyn StoreInstance> {
146 MemoryBackend
147 .open(&Options::new())
148 .expect("memory backend opens")
149 }
150
151 fn sentinel(n: usize) -> *mut c_void {
155 n as *mut c_void
156 }
157
158 #[test]
159 fn put_get_secret_round_trip() {
160 let mut ks = open();
161 let key = sentinel(0x1000);
162 ks.put_secret("mod", "app", "key-1", key)
163 .expect("put_secret");
164 let got = ks.get_secret("mod", "app", "key-1").expect("get_secret");
165 assert_eq!(got, key);
166 }
167
168 #[test]
169 fn put_get_public_round_trip() {
170 let mut ks = open();
171 let key = sentinel(0x2000);
172 let sig = vec![0xDE, 0xAD, 0xBE, 0xEF];
173 ks.put_public("mod", "app", "email:alice@example.com", key, &sig)
174 .expect("put_public");
175 let (got_key, got_sig) = ks
176 .get_public("mod", "app", "email:alice@example.com")
177 .expect("get_public");
178 assert_eq!(got_key, key);
179 assert_eq!(got_sig, sig);
180 }
181
182 #[test]
183 fn wrong_module_returns_value_not_found() {
184 let mut ks = open();
185 ks.put_secret("mod", "app", "key-1", sentinel(0x10))
186 .expect("put_secret");
187 let err = ks.get_secret("other", "app", "key-1").unwrap_err();
188 assert!(matches!(err, crate::error::Error::ValueNotFound));
189 }
190
191 #[test]
192 fn wrong_app_returns_value_not_found() {
193 let mut ks = open();
194 ks.put_secret("mod", "app", "key-1", sentinel(0x10))
195 .expect("put_secret");
196 let err = ks.get_secret("mod", "other", "key-1").unwrap_err();
197 assert!(matches!(err, crate::error::Error::ValueNotFound));
198 }
199
200 #[test]
201 fn wrong_key_id_returns_value_not_found() {
202 let mut ks = open();
203 ks.put_secret("mod", "app", "key-1", sentinel(0x10))
204 .expect("put_secret");
205 let err = ks.get_secret("mod", "app", "missing").unwrap_err();
206 assert!(matches!(err, crate::error::Error::ValueNotFound));
207 }
208
209 #[test]
210 fn wrong_identity_returns_value_not_found() {
211 let mut ks = open();
212 ks.put_public(
213 "mod",
214 "app",
215 "email:alice@example.com",
216 sentinel(0x20),
217 &[1, 2, 3],
218 )
219 .expect("put_public");
220 let err = ks
221 .get_public("mod", "app", "email:bob@example.com")
222 .unwrap_err();
223 assert!(matches!(err, crate::error::Error::ValueNotFound));
224 }
225
226 #[test]
227 fn compartments_are_isolated() {
228 let mut ks = open();
229 let secret = sentinel(0x30);
231 ks.put_secret("mod", "app", "key-1", secret)
232 .expect("put_secret");
233
234 let err = ks.get_public("mod", "app", "key-1").unwrap_err();
237 assert!(matches!(err, crate::error::Error::ValueNotFound));
238
239 let pub_key = sentinel(0x40);
241 ks.put_public("mod", "app", "email:alice@example.com", pub_key, &[9])
242 .expect("put_public");
243 let err = ks
244 .get_secret("mod", "app", "email:alice@example.com")
245 .unwrap_err();
246 assert!(matches!(err, crate::error::Error::ValueNotFound));
247 }
248
249 #[test]
250 fn enumerate_partitions_by_compartment() {
251 let mut ks = open();
252 ks.put_secret("mod", "app", "key-a", sentinel(0x1))
253 .expect("put_secret");
254 ks.put_secret("mod", "app", "key-b", sentinel(0x2))
255 .expect("put_secret");
256 ks.put_public("mod", "app", "email:a@b", sentinel(0x3), &[0])
257 .expect("put_public");
258
259 let private = ks
260 .enumerate("mod", "app", Compartment::Private)
261 .expect("enumerate private");
262 assert_eq!(private.len(), 2, "two private entries expected");
263
264 let public = ks
265 .enumerate("mod", "app", Compartment::Public)
266 .expect("enumerate public");
267 assert_eq!(public.len(), 1, "one public entry expected");
268 assert_eq!(public[0].1, "email:a@b");
269 }
270
271 #[test]
272 fn put_secret_overwrites() {
273 let mut ks = open();
274 let first = sentinel(0x100);
275 let second = sentinel(0x200);
276 ks.put_secret("mod", "app", "key-1", first)
277 .expect("put_secret first");
278 ks.put_secret("mod", "app", "key-1", second)
279 .expect("put_secret second");
280 let got = ks.get_secret("mod", "app", "key-1").expect("get_secret");
281 assert_eq!(got, second, "second put should win");
282 }
283
284 #[test]
285 fn distinct_scopes_do_not_leak() {
286 let mut ks = open();
287 ks.put_secret("mod", "app-a", "key-1", sentinel(0x1))
288 .expect("put_secret");
289 let err = ks.get_secret("mod", "app-b", "key-1").unwrap_err();
290 assert!(matches!(err, crate::error::Error::ValueNotFound));
291 }
292
293 #[test]
294 fn backend_is_registered() {
295 let backend = crate::backend::find("memory").expect("memory backend registered");
298 assert_eq!(backend.name(), "memory");
299 }
300}