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, into_internal};
10use tor_key_forge::{
11 CertData, EncodableItem, ErasedKey, KeystoreItem, KeystoreItemType, ParsedEd25519Cert,
12};
13
14use crate::keystore::ephemeral::err::ArtiEphemeralKeystoreError;
15use crate::raw::RawEntryId;
16use crate::{ArtiPath, Error, KeySpecifier, Keystore, KeystoreEntry, KeystoreId, Result};
17
18use super::KeystoreEntryResult;
19
20type KeyIdent = (ArtiPath, KeystoreItemType);
22
23pub struct ArtiEphemeralKeystore {
35 id: KeystoreId,
37 key_dictionary: Arc<Mutex<HashMap<KeyIdent, KeystoreItem>>>,
39}
40
41impl ArtiEphemeralKeystore {
42 pub fn new(id: String) -> Self {
44 Self {
45 id: KeystoreId(id),
46 key_dictionary: Default::default(),
47 }
48 }
49}
50
51impl Keystore for ArtiEphemeralKeystore {
52 fn id(&self) -> &KeystoreId {
53 &self.id
54 }
55
56 fn contains(
57 &self,
58 key_spec: &dyn KeySpecifier,
59 item_type: &KeystoreItemType,
60 ) -> StdResult<bool, Error> {
61 let arti_path = key_spec
62 .arti_path()
63 .map_err(ArtiEphemeralKeystoreError::ArtiPathUnavailableError)?;
64 let key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
65 let contains_key = key_dictionary.contains_key(&(arti_path, item_type.clone()));
66 Ok(contains_key)
67 }
68
69 fn get(
70 &self,
71 key_spec: &dyn KeySpecifier,
72 item_type: &KeystoreItemType,
73 ) -> StdResult<Option<ErasedKey>, Error> {
74 let arti_path = key_spec
75 .arti_path()
76 .map_err(ArtiEphemeralKeystoreError::ArtiPathUnavailableError)?;
77 let key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
78 match key_dictionary.get(&(arti_path.clone(), item_type.clone())) {
79 Some(key) if key.item_type()? != *item_type => {
80 Err(internal!(
84 "the specified KeystoreItemType does not match key type of the fetched key?!"
85 )
86 .into())
87 }
88 Some(key) => {
89 let key: KeystoreItem = key.clone();
90
91 match key {
92 KeystoreItem::Key(key) => {
93 let key: ErasedKey = key.into_erased()?;
94 Ok(Some(key))
95 }
96 KeystoreItem::Cert(CertData::TorEd25519Cert(c)) => {
97 let cert = ParsedEd25519Cert::decode(c.to_vec())
102 .map_err(into_internal!("found invalid cert in ephemeral store?!"))?;
103
104 Ok(Some(Box::new(cert)))
105 }
106 _ => Err(internal!("unknown item type {key:?} in the keystore").into()),
107 }
108 }
109 None => Ok(None),
110 }
111 }
112
113 #[cfg(feature = "onion-service-cli-extra")]
114 fn raw_entry_id(&self, _raw_id: &str) -> Result<RawEntryId> {
115 Err(ArtiEphemeralKeystoreError::NotSupported {
116 action: "raw_entry_id",
117 }
118 .into())
119 }
120
121 fn insert(&self, key: &dyn EncodableItem, key_spec: &dyn KeySpecifier) -> StdResult<(), Error> {
122 let arti_path = key_spec
123 .arti_path()
124 .map_err(ArtiEphemeralKeystoreError::ArtiPathUnavailableError)?;
125 let key_data = key.as_keystore_item()?;
126 let item_type = key_data.item_type()?;
127
128 let mut key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
130 let _ = key_dictionary.insert((arti_path, item_type), key_data);
131 Ok(())
132 }
133
134 fn remove(
135 &self,
136 key_spec: &dyn KeySpecifier,
137 item_type: &KeystoreItemType,
138 ) -> StdResult<Option<()>, Error> {
139 let arti_path = key_spec
140 .arti_path()
141 .map_err(ArtiEphemeralKeystoreError::ArtiPathUnavailableError)?;
142 let mut key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
143 match key_dictionary.remove(&(arti_path, item_type.clone())) {
144 Some(key) if key.item_type()? != *item_type => {
145 Err(internal!(
149 "the specified KeystoreItemType does not match key type of the removed key?!"
150 )
151 .into())
152 }
153 Some(_) => Ok(Some(())),
154 None => Ok(None),
155 }
156 }
157
158 #[cfg(feature = "onion-service-cli-extra")]
159 fn remove_unchecked(&self, _entry_id: &RawEntryId) -> Result<()> {
160 Err(ArtiEphemeralKeystoreError::NotSupported {
162 action: "remove_uncheked",
163 }
164 .into())
165 }
166
167 fn list(&self) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
168 let key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
169 Ok(key_dictionary
170 .keys()
171 .map(|(arti_path, item_type)| {
172 let raw_id = RawEntryId::Ephemeral((arti_path.clone(), item_type.clone()));
173 Ok(KeystoreEntry::new(
174 arti_path.clone().into(),
175 item_type.clone(),
176 self.id(),
177 raw_id,
178 ))
179 })
180 .collect())
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 #![allow(clippy::bool_assert_comparison)]
188 #![allow(clippy::clone_on_copy)]
189 #![allow(clippy::dbg_macro)]
190 #![allow(clippy::mixed_attributes_style)]
191 #![allow(clippy::print_stderr)]
192 #![allow(clippy::print_stdout)]
193 #![allow(clippy::single_char_pattern)]
194 #![allow(clippy::unwrap_used)]
195 #![allow(clippy::unchecked_time_subtraction)]
196 #![allow(clippy::useless_vec)]
197 #![allow(clippy::needless_pass_by_value)]
198 #![allow(clippy::string_slice)] use tor_basic_utils::test_rng::{TestingRng, testing_rng};
202 use tor_error::{ErrorKind, HasKind};
203 use tor_key_forge::{KeyType, Keygen};
204 use tor_llcrypto::pk::{curve25519, ed25519};
205 use tor_llcrypto::rng::FakeEntropicRng;
206
207 use super::*;
208
209 use crate::test_utils::TestSpecifier;
210
211 fn key() -> Box<dyn EncodableItem> {
214 let mut rng = testing_rng();
215 let keypair = ed25519::Keypair::generate(&mut rng);
216 Box::new(keypair)
217 }
218
219 fn key_type() -> KeystoreItemType {
220 KeyType::Ed25519Keypair.into()
221 }
222
223 fn key_bad() -> Box<dyn EncodableItem> {
224 let mut rng = FakeEntropicRng::<TestingRng>(testing_rng());
225 let keypair = curve25519::StaticKeypair::generate(&mut rng).unwrap();
226 Box::new(keypair)
227 }
228
229 fn key_type_bad() -> KeystoreItemType {
230 KeyType::X25519StaticKeypair.into()
231 }
232
233 fn key_spec() -> Box<dyn KeySpecifier> {
234 Box::<TestSpecifier>::default()
235 }
236
237 #[test]
240 fn id() {
241 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
242
243 assert_eq!(&KeystoreId("test-ephemeral".to_string()), key_store.id());
244 }
245
246 #[test]
247 fn contains() {
248 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
249
250 assert!(
252 !key_store
253 .contains(key_spec().as_ref(), &key_type())
254 .unwrap()
255 );
256
257 assert!(
259 key_store
260 .insert(key().as_ref(), key_spec().as_ref())
261 .is_ok()
262 );
263 assert!(
264 key_store
265 .contains(key_spec().as_ref(), &key_type())
266 .unwrap()
267 );
268 }
269
270 #[test]
271 fn get() {
272 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
273
274 assert!(
276 key_store
277 .get(key_spec().as_ref(), &key_type())
278 .unwrap()
279 .is_none()
280 );
281
282 assert!(
284 key_store
285 .insert(key().as_ref(), key_spec().as_ref())
286 .is_ok()
287 );
288
289 let key = key_store
290 .get(key_spec().as_ref(), &key_type())
291 .unwrap()
292 .unwrap();
293
294 assert!(key.downcast::<ed25519::Keypair>().is_ok());
296
297 key_store.remove(key_spec().as_ref(), &key_type()).unwrap();
299 {
300 let mut key_dictionary = key_store.key_dictionary.lock().unwrap();
301 let _ = key_dictionary.insert(
302 (key_spec().arti_path().unwrap(), key_type()),
303 key_bad().as_keystore_item().unwrap(),
304 );
305 }
306 assert!(matches!(
307 key_store
308 .get(key_spec().as_ref(), &key_type())
309 .err()
310 .unwrap()
311 .kind(),
312 ErrorKind::Internal
313 ));
314 }
315
316 #[test]
317 fn insert() {
318 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
319
320 assert!(
321 !key_store
322 .contains(key_spec().as_ref(), &key_type_bad())
323 .unwrap()
324 );
325 assert!(
326 key_store
327 .get(key_spec().as_ref(), &key_type_bad())
328 .unwrap()
329 .is_none()
330 );
331 assert!(key_store.list().unwrap().is_empty());
332
333 assert!(
335 key_store
336 .insert(key().as_ref(), key_spec().as_ref())
337 .is_ok()
338 );
339
340 assert!(
342 key_store
343 .contains(key_spec().as_ref(), &key_type())
344 .unwrap()
345 );
346 assert!(
347 key_store
348 .get(key_spec().as_ref(), &key_type())
349 .unwrap()
350 .is_some()
351 );
352 assert_eq!(key_store.list().unwrap().len(), 1);
353 }
354
355 #[test]
356 fn remove() {
357 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
358
359 assert!(
361 key_store
362 .remove(key_spec().as_ref(), &key_type())
363 .unwrap()
364 .is_none()
365 );
366
367 assert!(
369 key_store
370 .insert(key().as_ref(), key_spec().as_ref())
371 .is_ok()
372 );
373 assert!(
374 key_store
375 .remove(key_spec().as_ref(), &key_type())
376 .unwrap()
377 .is_some()
378 );
379
380 {
382 let mut key_dictionary = key_store.key_dictionary.lock().unwrap();
383 let _ = key_dictionary.insert(
384 (key_spec().arti_path().unwrap(), key_type()),
385 key_bad().as_keystore_item().unwrap(),
386 );
387 }
388 assert!(matches!(
389 key_store
390 .remove(key_spec().as_ref(), &key_type())
391 .err()
392 .unwrap()
393 .kind(),
394 ErrorKind::Internal
395 ));
396 }
397
398 #[test]
399 fn list() {
400 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
401
402 assert!(key_store.list().unwrap().is_empty());
404
405 assert!(
407 key_store
408 .insert(key().as_ref(), key_spec().as_ref())
409 .is_ok()
410 );
411 assert_eq!(key_store.list().unwrap().len(), 1);
412 }
413}