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