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 use tor_basic_utils::test_rng::{TestingRng, testing_rng};
201 use tor_error::{ErrorKind, HasKind};
202 use tor_key_forge::{KeyType, Keygen};
203 use tor_llcrypto::pk::{curve25519, ed25519};
204 use tor_llcrypto::rng::FakeEntropicRng;
205
206 use super::*;
207
208 use crate::test_utils::TestSpecifier;
209
210 fn key() -> Box<dyn EncodableItem> {
213 let mut rng = testing_rng();
214 let keypair = ed25519::Keypair::generate(&mut rng);
215 Box::new(keypair)
216 }
217
218 fn key_type() -> KeystoreItemType {
219 KeyType::Ed25519Keypair.into()
220 }
221
222 fn key_bad() -> Box<dyn EncodableItem> {
223 let mut rng = FakeEntropicRng::<TestingRng>(testing_rng());
224 let keypair = curve25519::StaticKeypair::generate(&mut rng).unwrap();
225 Box::new(keypair)
226 }
227
228 fn key_type_bad() -> KeystoreItemType {
229 KeyType::X25519StaticKeypair.into()
230 }
231
232 fn key_spec() -> Box<dyn KeySpecifier> {
233 Box::<TestSpecifier>::default()
234 }
235
236 #[test]
239 fn id() {
240 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
241
242 assert_eq!(&KeystoreId("test-ephemeral".to_string()), key_store.id());
243 }
244
245 #[test]
246 fn contains() {
247 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
248
249 assert!(
251 !key_store
252 .contains(key_spec().as_ref(), &key_type())
253 .unwrap()
254 );
255
256 assert!(
258 key_store
259 .insert(key().as_ref(), key_spec().as_ref())
260 .is_ok()
261 );
262 assert!(
263 key_store
264 .contains(key_spec().as_ref(), &key_type())
265 .unwrap()
266 );
267 }
268
269 #[test]
270 fn get() {
271 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
272
273 assert!(
275 key_store
276 .get(key_spec().as_ref(), &key_type())
277 .unwrap()
278 .is_none()
279 );
280
281 assert!(
283 key_store
284 .insert(key().as_ref(), key_spec().as_ref())
285 .is_ok()
286 );
287
288 let key = key_store
289 .get(key_spec().as_ref(), &key_type())
290 .unwrap()
291 .unwrap();
292
293 assert!(key.downcast::<ed25519::Keypair>().is_ok());
295
296 key_store.remove(key_spec().as_ref(), &key_type()).unwrap();
298 {
299 let mut key_dictionary = key_store.key_dictionary.lock().unwrap();
300 let _ = key_dictionary.insert(
301 (key_spec().arti_path().unwrap(), key_type()),
302 key_bad().as_keystore_item().unwrap(),
303 );
304 }
305 assert!(matches!(
306 key_store
307 .get(key_spec().as_ref(), &key_type())
308 .err()
309 .unwrap()
310 .kind(),
311 ErrorKind::Internal
312 ));
313 }
314
315 #[test]
316 fn insert() {
317 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
318
319 assert!(
320 !key_store
321 .contains(key_spec().as_ref(), &key_type_bad())
322 .unwrap()
323 );
324 assert!(
325 key_store
326 .get(key_spec().as_ref(), &key_type_bad())
327 .unwrap()
328 .is_none()
329 );
330 assert!(key_store.list().unwrap().is_empty());
331
332 assert!(
334 key_store
335 .insert(key().as_ref(), key_spec().as_ref())
336 .is_ok()
337 );
338
339 assert!(
341 key_store
342 .contains(key_spec().as_ref(), &key_type())
343 .unwrap()
344 );
345 assert!(
346 key_store
347 .get(key_spec().as_ref(), &key_type())
348 .unwrap()
349 .is_some()
350 );
351 assert_eq!(key_store.list().unwrap().len(), 1);
352 }
353
354 #[test]
355 fn remove() {
356 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
357
358 assert!(
360 key_store
361 .remove(key_spec().as_ref(), &key_type())
362 .unwrap()
363 .is_none()
364 );
365
366 assert!(
368 key_store
369 .insert(key().as_ref(), key_spec().as_ref())
370 .is_ok()
371 );
372 assert!(
373 key_store
374 .remove(key_spec().as_ref(), &key_type())
375 .unwrap()
376 .is_some()
377 );
378
379 {
381 let mut key_dictionary = key_store.key_dictionary.lock().unwrap();
382 let _ = key_dictionary.insert(
383 (key_spec().arti_path().unwrap(), key_type()),
384 key_bad().as_keystore_item().unwrap(),
385 );
386 }
387 assert!(matches!(
388 key_store
389 .remove(key_spec().as_ref(), &key_type())
390 .err()
391 .unwrap()
392 .kind(),
393 ErrorKind::Internal
394 ));
395 }
396
397 #[test]
398 fn list() {
399 let key_store = ArtiEphemeralKeystore::new("test-ephemeral".to_string());
400
401 assert!(key_store.list().unwrap().is_empty());
403
404 assert!(
406 key_store
407 .insert(key().as_ref(), key_spec().as_ref())
408 .is_ok()
409 );
410 assert_eq!(key_store.list().unwrap().len(), 1);
411 }
412}