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::raw::RawEntryId;
14use crate::{ArtiPath, Error, KeySpecifier, Keystore, KeystoreEntry, KeystoreId, Result};
15
16use super::KeystoreEntryResult;
17
18type KeyIdent = (ArtiPath, KeystoreItemType);
20
21pub struct ArtiEphemeralKeystore {
33 id: KeystoreId,
35 key_dictionary: Arc<Mutex<HashMap<KeyIdent, KeystoreItem>>>,
37}
38
39impl ArtiEphemeralKeystore {
40 pub fn new(id: String) -> Self {
42 Self {
43 id: KeystoreId(id),
44 key_dictionary: Default::default(),
45 }
46 }
47}
48
49impl Keystore for ArtiEphemeralKeystore {
50 fn id(&self) -> &KeystoreId {
51 &self.id
52 }
53
54 fn contains(
55 &self,
56 key_spec: &dyn KeySpecifier,
57 item_type: &KeystoreItemType,
58 ) -> StdResult<bool, Error> {
59 let arti_path = key_spec
60 .arti_path()
61 .map_err(ArtiEphemeralKeystoreError::ArtiPathUnavailableError)?;
62 let key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
63 let contains_key = key_dictionary.contains_key(&(arti_path, item_type.clone()));
64 Ok(contains_key)
65 }
66
67 fn get(
68 &self,
69 key_spec: &dyn KeySpecifier,
70 item_type: &KeystoreItemType,
71 ) -> StdResult<Option<ErasedKey>, Error> {
72 let arti_path = key_spec
73 .arti_path()
74 .map_err(ArtiEphemeralKeystoreError::ArtiPathUnavailableError)?;
75 let key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
76 match key_dictionary.get(&(arti_path.clone(), item_type.clone())) {
77 Some(key) if key.item_type()? != *item_type => {
78 Err(internal!(
82 "the specified KeystoreItemType does not match key type of the fetched key?!"
83 )
84 .into())
85 }
86 Some(key) => {
87 let key: KeystoreItem = key.clone();
88 let key: ErasedKey = key.into_erased()?;
89 Ok(Some(key))
90 }
91 None => Ok(None),
92 }
93 }
94
95 #[cfg(feature = "onion-service-cli-extra")]
96 fn raw_entry_id(&self, _raw_id: &str) -> Result<RawEntryId> {
97 Err(ArtiEphemeralKeystoreError::NotSupported {
98 action: "raw_entry_id",
99 }
100 .into())
101 }
102
103 fn insert(&self, key: &dyn EncodableItem, key_spec: &dyn KeySpecifier) -> StdResult<(), Error> {
104 let arti_path = key_spec
105 .arti_path()
106 .map_err(ArtiEphemeralKeystoreError::ArtiPathUnavailableError)?;
107 let key_data = key.as_keystore_item()?;
108 let item_type = key_data.item_type()?;
109
110 let mut key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
112 let _ = key_dictionary.insert((arti_path, item_type), key_data);
113 Ok(())
114 }
115
116 fn remove(
117 &self,
118 key_spec: &dyn KeySpecifier,
119 item_type: &KeystoreItemType,
120 ) -> StdResult<Option<()>, Error> {
121 let arti_path = key_spec
122 .arti_path()
123 .map_err(ArtiEphemeralKeystoreError::ArtiPathUnavailableError)?;
124 let mut key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
125 match key_dictionary.remove(&(arti_path, item_type.clone())) {
126 Some(key) if key.item_type()? != *item_type => {
127 Err(internal!(
131 "the specified KeystoreItemType does not match key type of the removed key?!"
132 )
133 .into())
134 }
135 Some(_) => Ok(Some(())),
136 None => Ok(None),
137 }
138 }
139
140 #[cfg(feature = "onion-service-cli-extra")]
141 fn remove_unchecked(&self, _entry_id: &RawEntryId) -> Result<()> {
142 Err(ArtiEphemeralKeystoreError::NotSupported {
144 action: "remove_uncheked",
145 }
146 .into())
147 }
148
149 fn list(&self) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
150 let key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
151 Ok(key_dictionary
152 .keys()
153 .map(|(arti_path, item_type)| {
154 let raw_id = RawEntryId::Ephemeral((arti_path.clone(), item_type.clone()));
155 Ok(KeystoreEntry::new(
156 arti_path.clone().into(),
157 item_type.clone(),
158 self.id(),
159 raw_id,
160 ))
161 })
162 .collect())
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 #![allow(clippy::bool_assert_comparison)]
170 #![allow(clippy::clone_on_copy)]
171 #![allow(clippy::dbg_macro)]
172 #![allow(clippy::mixed_attributes_style)]
173 #![allow(clippy::print_stderr)]
174 #![allow(clippy::print_stdout)]
175 #![allow(clippy::single_char_pattern)]
176 #![allow(clippy::unwrap_used)]
177 #![allow(clippy::unchecked_duration_subtraction)]
178 #![allow(clippy::useless_vec)]
179 #![allow(clippy::needless_pass_by_value)]
180 use tor_basic_utils::test_rng::{testing_rng, TestingRng};
183 use tor_error::{ErrorKind, HasKind};
184 use tor_key_forge::{KeyType, Keygen};
185 use tor_llcrypto::pk::{curve25519, ed25519};
186 use tor_llcrypto::rng::FakeEntropicRng;
187
188 use super::*;
189
190 use crate::test_utils::TestSpecifier;
191
192 fn key() -> Box<dyn EncodableItem> {
195 let mut rng = testing_rng();
196 let keypair = ed25519::Keypair::generate(&mut rng);
197 Box::new(keypair)
198 }
199
200 fn key_type() -> KeystoreItemType {
201 KeyType::Ed25519Keypair.into()
202 }
203
204 fn key_bad() -> Box<dyn EncodableItem> {
205 let mut rng = FakeEntropicRng::<TestingRng>(testing_rng());
206 let keypair = curve25519::StaticKeypair::generate(&mut rng).unwrap();
207 Box::new(keypair)
208 }
209
210 fn key_type_bad() -> KeystoreItemType {
211 KeyType::X25519StaticKeypair.into()
212 }
213
214 fn key_spec() -> Box<dyn KeySpecifier> {
215 Box::<TestSpecifier>::default()
216 }
217
218 #[test]
221 fn id() {
222 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
223
224 assert_eq!(&KeystoreId("test-ephemeral".to_string()), key_store.id());
225 }
226
227 #[test]
228 fn contains() {
229 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
230
231 assert!(!key_store
233 .contains(key_spec().as_ref(), &key_type())
234 .unwrap());
235
236 assert!(key_store
238 .insert(key().as_ref(), key_spec().as_ref())
239 .is_ok());
240 assert!(key_store
241 .contains(key_spec().as_ref(), &key_type())
242 .unwrap());
243 }
244
245 #[test]
246 fn get() {
247 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
248
249 assert!(key_store
251 .get(key_spec().as_ref(), &key_type())
252 .unwrap()
253 .is_none());
254
255 assert!(key_store
257 .insert(key().as_ref(), key_spec().as_ref())
258 .is_ok());
259
260 let key = key_store
261 .get(key_spec().as_ref(), &key_type())
262 .unwrap()
263 .unwrap();
264
265 assert!(key.downcast::<ed25519::Keypair>().is_ok());
267
268 key_store.remove(key_spec().as_ref(), &key_type()).unwrap();
270 {
271 let mut key_dictionary = key_store.key_dictionary.lock().unwrap();
272 let _ = key_dictionary.insert(
273 (key_spec().arti_path().unwrap(), key_type()),
274 key_bad().as_keystore_item().unwrap(),
275 );
276 }
277 assert!(matches!(
278 key_store
279 .get(key_spec().as_ref(), &key_type())
280 .err()
281 .unwrap()
282 .kind(),
283 ErrorKind::Internal
284 ));
285 }
286
287 #[test]
288 fn insert() {
289 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
290
291 assert!(!key_store
292 .contains(key_spec().as_ref(), &key_type_bad())
293 .unwrap());
294 assert!(key_store
295 .get(key_spec().as_ref(), &key_type_bad())
296 .unwrap()
297 .is_none());
298 assert!(key_store.list().unwrap().is_empty());
299
300 assert!(key_store
302 .insert(key().as_ref(), key_spec().as_ref())
303 .is_ok());
304
305 assert!(key_store
307 .contains(key_spec().as_ref(), &key_type())
308 .unwrap());
309 assert!(key_store
310 .get(key_spec().as_ref(), &key_type())
311 .unwrap()
312 .is_some());
313 assert_eq!(key_store.list().unwrap().len(), 1);
314 }
315
316 #[test]
317 fn remove() {
318 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
319
320 assert!(key_store
322 .remove(key_spec().as_ref(), &key_type())
323 .unwrap()
324 .is_none());
325
326 assert!(key_store
328 .insert(key().as_ref(), key_spec().as_ref())
329 .is_ok());
330 assert!(key_store
331 .remove(key_spec().as_ref(), &key_type())
332 .unwrap()
333 .is_some());
334
335 {
337 let mut key_dictionary = key_store.key_dictionary.lock().unwrap();
338 let _ = key_dictionary.insert(
339 (key_spec().arti_path().unwrap(), key_type()),
340 key_bad().as_keystore_item().unwrap(),
341 );
342 }
343 assert!(matches!(
344 key_store
345 .remove(key_spec().as_ref(), &key_type())
346 .err()
347 .unwrap()
348 .kind(),
349 ErrorKind::Internal
350 ));
351 }
352
353 #[test]
354 fn list() {
355 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
356
357 assert!(key_store.list().unwrap().is_empty());
359
360 assert!(key_store
362 .insert(key().as_ref(), key_spec().as_ref())
363 .is_ok());
364 assert_eq!(key_store.list().unwrap().len(), 1);
365 }
366}