tor_keymgr/keystore/
ephemeral.rs
1pub(crate) mod err;
4
5use std::collections::HashMap;
6use std::result::Result as StdResult;
7use std::sync::{Arc, Mutex};
8
9use tor_error::internal;
10use tor_key_forge::{EncodableItem, ErasedKey, KeystoreItem, KeystoreItemType};
11
12use crate::keystore::ephemeral::err::ArtiEphemeralKeystoreError;
13use crate::{ArtiPath, Error, KeyPath, KeySpecifier, Keystore, KeystoreId, Result};
14
15use super::KeystoreEntryResult;
16
17type KeyIdent = (ArtiPath, KeystoreItemType);
19
20pub struct ArtiEphemeralKeystore {
32 id: KeystoreId,
34 key_dictionary: Arc<Mutex<HashMap<KeyIdent, KeystoreItem>>>,
36}
37
38impl ArtiEphemeralKeystore {
39 pub fn new(id: String) -> Self {
41 Self {
42 id: KeystoreId(id),
43 key_dictionary: Default::default(),
44 }
45 }
46}
47
48impl Keystore for ArtiEphemeralKeystore {
49 fn id(&self) -> &KeystoreId {
50 &self.id
51 }
52
53 fn contains(
54 &self,
55 key_spec: &dyn KeySpecifier,
56 item_type: &KeystoreItemType,
57 ) -> StdResult<bool, Error> {
58 let arti_path = key_spec
59 .arti_path()
60 .map_err(ArtiEphemeralKeystoreError::ArtiPathUnavailableError)?;
61 let key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
62 let contains_key = key_dictionary.contains_key(&(arti_path, item_type.clone()));
63 Ok(contains_key)
64 }
65
66 fn get(
67 &self,
68 key_spec: &dyn KeySpecifier,
69 item_type: &KeystoreItemType,
70 ) -> StdResult<Option<ErasedKey>, Error> {
71 let arti_path = key_spec
72 .arti_path()
73 .map_err(ArtiEphemeralKeystoreError::ArtiPathUnavailableError)?;
74 let key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
75 match key_dictionary.get(&(arti_path.clone(), item_type.clone())) {
76 Some(key) if key.item_type()? != *item_type => {
77 Err(internal!(
81 "the specified KeystoreItemType does not match key type of the fetched key?!"
82 )
83 .into())
84 }
85 Some(key) => {
86 let key: KeystoreItem = key.clone();
87 let key: ErasedKey = key.into_erased()?;
88 Ok(Some(key))
89 }
90 None => Ok(None),
91 }
92 }
93
94 fn insert(&self, key: &dyn EncodableItem, key_spec: &dyn KeySpecifier) -> StdResult<(), Error> {
95 let arti_path = key_spec
96 .arti_path()
97 .map_err(ArtiEphemeralKeystoreError::ArtiPathUnavailableError)?;
98 let key_data = key.as_keystore_item()?;
99 let item_type = key_data.item_type()?;
100
101 let mut key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
103 let _ = key_dictionary.insert((arti_path, item_type), key_data);
104 Ok(())
105 }
106
107 fn remove(
108 &self,
109 key_spec: &dyn KeySpecifier,
110 item_type: &KeystoreItemType,
111 ) -> StdResult<Option<()>, Error> {
112 let arti_path = key_spec
113 .arti_path()
114 .map_err(ArtiEphemeralKeystoreError::ArtiPathUnavailableError)?;
115 let mut key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
116 match key_dictionary.remove(&(arti_path, item_type.clone())) {
117 Some(key) if key.item_type()? != *item_type => {
118 Err(internal!(
122 "the specified KeystoreItemType does not match key type of the removed key?!"
123 )
124 .into())
125 }
126 Some(_) => Ok(Some(())),
127 None => Ok(None),
128 }
129 }
130
131 fn list(&self) -> Result<Vec<KeystoreEntryResult<(KeyPath, KeystoreItemType)>>> {
132 let key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
133 Ok(key_dictionary
134 .keys()
135 .map(|(arti_path, item_type)| Ok((arti_path.clone().into(), item_type.clone())))
136 .collect())
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 #![allow(clippy::bool_assert_comparison)]
144 #![allow(clippy::clone_on_copy)]
145 #![allow(clippy::dbg_macro)]
146 #![allow(clippy::mixed_attributes_style)]
147 #![allow(clippy::print_stderr)]
148 #![allow(clippy::print_stdout)]
149 #![allow(clippy::single_char_pattern)]
150 #![allow(clippy::unwrap_used)]
151 #![allow(clippy::unchecked_duration_subtraction)]
152 #![allow(clippy::useless_vec)]
153 #![allow(clippy::needless_pass_by_value)]
154 use tor_basic_utils::test_rng::{testing_rng, TestingRng};
157 use tor_error::{ErrorKind, HasKind};
158 use tor_key_forge::{KeyType, Keygen};
159 use tor_llcrypto::pk::{curve25519, ed25519};
160 use tor_llcrypto::rng::FakeEntropicRng;
161
162 use super::*;
163
164 use crate::test_utils::TestSpecifier;
165
166 fn key() -> Box<dyn EncodableItem> {
169 let mut rng = testing_rng();
170 let keypair = ed25519::Keypair::generate(&mut rng);
171 Box::new(keypair)
172 }
173
174 fn key_type() -> KeystoreItemType {
175 KeyType::Ed25519Keypair.into()
176 }
177
178 fn key_bad() -> Box<dyn EncodableItem> {
179 let mut rng = FakeEntropicRng::<TestingRng>(testing_rng());
180 let keypair = curve25519::StaticKeypair::generate(&mut rng).unwrap();
181 Box::new(keypair)
182 }
183
184 fn key_type_bad() -> KeystoreItemType {
185 KeyType::X25519StaticKeypair.into()
186 }
187
188 fn key_spec() -> Box<dyn KeySpecifier> {
189 Box::<TestSpecifier>::default()
190 }
191
192 #[test]
195 fn id() {
196 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
197
198 assert_eq!(&KeystoreId("test-ephemeral".to_string()), key_store.id());
199 }
200
201 #[test]
202 fn contains() {
203 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
204
205 assert!(!key_store
207 .contains(key_spec().as_ref(), &key_type())
208 .unwrap());
209
210 assert!(key_store
212 .insert(key().as_ref(), key_spec().as_ref())
213 .is_ok());
214 assert!(key_store
215 .contains(key_spec().as_ref(), &key_type())
216 .unwrap());
217 }
218
219 #[test]
220 fn get() {
221 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
222
223 assert!(key_store
225 .get(key_spec().as_ref(), &key_type())
226 .unwrap()
227 .is_none());
228
229 assert!(key_store
231 .insert(key().as_ref(), key_spec().as_ref())
232 .is_ok());
233
234 let key = key_store
235 .get(key_spec().as_ref(), &key_type())
236 .unwrap()
237 .unwrap();
238
239 assert!(key.downcast::<ed25519::Keypair>().is_ok());
241
242 key_store.remove(key_spec().as_ref(), &key_type()).unwrap();
244 {
245 let mut key_dictionary = key_store.key_dictionary.lock().unwrap();
246 let _ = key_dictionary.insert(
247 (key_spec().arti_path().unwrap(), key_type()),
248 key_bad().as_keystore_item().unwrap(),
249 );
250 }
251 assert!(matches!(
252 key_store
253 .get(key_spec().as_ref(), &key_type())
254 .err()
255 .unwrap()
256 .kind(),
257 ErrorKind::Internal
258 ));
259 }
260
261 #[test]
262 fn insert() {
263 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
264
265 assert!(!key_store
266 .contains(key_spec().as_ref(), &key_type_bad())
267 .unwrap());
268 assert!(key_store
269 .get(key_spec().as_ref(), &key_type_bad())
270 .unwrap()
271 .is_none());
272 assert!(key_store.list().unwrap().is_empty());
273
274 assert!(key_store
276 .insert(key().as_ref(), key_spec().as_ref())
277 .is_ok());
278
279 assert!(key_store
281 .contains(key_spec().as_ref(), &key_type())
282 .unwrap());
283 assert!(key_store
284 .get(key_spec().as_ref(), &key_type())
285 .unwrap()
286 .is_some());
287 assert_eq!(key_store.list().unwrap().len(), 1);
288 }
289
290 #[test]
291 fn remove() {
292 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
293
294 assert!(key_store
296 .remove(key_spec().as_ref(), &key_type())
297 .unwrap()
298 .is_none());
299
300 assert!(key_store
302 .insert(key().as_ref(), key_spec().as_ref())
303 .is_ok());
304 assert!(key_store
305 .remove(key_spec().as_ref(), &key_type())
306 .unwrap()
307 .is_some());
308
309 {
311 let mut key_dictionary = key_store.key_dictionary.lock().unwrap();
312 let _ = key_dictionary.insert(
313 (key_spec().arti_path().unwrap(), key_type()),
314 key_bad().as_keystore_item().unwrap(),
315 );
316 }
317 assert!(matches!(
318 key_store
319 .remove(key_spec().as_ref(), &key_type())
320 .err()
321 .unwrap()
322 .kind(),
323 ErrorKind::Internal
324 ));
325 }
326
327 #[test]
328 fn list() {
329 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
330
331 assert!(key_store.list().unwrap().is_empty());
333
334 assert!(key_store
336 .insert(key().as_ref(), key_spec().as_ref())
337 .is_ok());
338 assert_eq!(key_store.list().unwrap().len(), 1);
339 }
340}