1
//! ArtiEphemeralKeystore implementation (in-memory ephemeral key storage)
2

            
3
pub(crate) mod err;
4

            
5
use std::collections::HashMap;
6
use std::result::Result as StdResult;
7
use std::sync::{Arc, Mutex};
8

            
9
use tor_error::internal;
10
use tor_key_forge::{EncodableItem, ErasedKey, KeystoreItem, KeystoreItemType};
11

            
12
use crate::keystore::ephemeral::err::ArtiEphemeralKeystoreError;
13
use crate::{ArtiPath, Error, KeyPath, KeySpecifier, Keystore, KeystoreId, Result};
14

            
15
use super::KeystoreEntryResult;
16

            
17
/// The identifier of a key stored in the `ArtiEphemeralKeystore`.
18
type KeyIdent = (ArtiPath, KeystoreItemType);
19

            
20
/// The Ephemeral Arti key store
21
///
22
/// This is a purely in-memory key store. Keys written to this store
23
/// are never written to disk, and are stored in-memory as [`KeystoreItem`]s.
24
/// Keys saved in this Keystore do not persist between restarts!
25
///
26
/// While Arti never writes the keys for this key store to disk, the operating
27
/// system may do so for reasons outside of this library's control. Some
28
/// examples are swapping RAM to disk, generating core dumps, invoking
29
/// suspend-to-disk power management, etc. This key store does not attempt to
30
/// prevent this operating system behaviour.
31
pub struct ArtiEphemeralKeystore {
32
    /// Identifier hard-coded to 'ephemeral'
33
    id: KeystoreId,
34
    /// Keys stored as [`KeystoreItem`].
35
    key_dictionary: Arc<Mutex<HashMap<KeyIdent, KeystoreItem>>>,
36
}
37

            
38
impl ArtiEphemeralKeystore {
39
    /// Create a new [`ArtiEphemeralKeystore`]
40
12
    pub fn new(id: String) -> Self {
41
12
        Self {
42
12
            id: KeystoreId(id),
43
12
            key_dictionary: Default::default(),
44
12
        }
45
12
    }
46
}
47

            
48
impl Keystore for ArtiEphemeralKeystore {
49
2
    fn id(&self) -> &KeystoreId {
50
2
        &self.id
51
2
    }
52

            
53
8
    fn contains(
54
8
        &self,
55
8
        key_spec: &dyn KeySpecifier,
56
8
        item_type: &KeystoreItemType,
57
8
    ) -> StdResult<bool, Error> {
58
8
        let arti_path = key_spec
59
8
            .arti_path()
60
8
            .map_err(ArtiEphemeralKeystoreError::ArtiPathUnavailableError)?;
61
8
        let key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
62
8
        let contains_key = key_dictionary.contains_key(&(arti_path, item_type.clone()));
63
8
        Ok(contains_key)
64
8
    }
65

            
66
10
    fn get(
67
10
        &self,
68
10
        key_spec: &dyn KeySpecifier,
69
10
        item_type: &KeystoreItemType,
70
10
    ) -> StdResult<Option<ErasedKey>, Error> {
71
10
        let arti_path = key_spec
72
10
            .arti_path()
73
10
            .map_err(ArtiEphemeralKeystoreError::ArtiPathUnavailableError)?;
74
10
        let key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
75
10
        match key_dictionary.get(&(arti_path.clone(), item_type.clone())) {
76
6
            Some(key) if key.item_type()? != *item_type => {
77
2
                // This should only happen if some external factor alters the
78
2
                // process memory or if there's a bug in our implementation of
79
2
                // Keystore::insert().
80
2
                Err(internal!(
81
2
                    "the specified KeystoreItemType does not match key type of the fetched key?!"
82
2
                )
83
2
                .into())
84
            }
85
4
            Some(key) => {
86
4
                let key: KeystoreItem = key.clone();
87
4
                let key: ErasedKey = key.into_erased()?;
88
4
                Ok(Some(key))
89
            }
90
4
            None => Ok(None),
91
        }
92
10
    }
93

            
94
10
    fn insert(&self, key: &dyn EncodableItem, key_spec: &dyn KeySpecifier) -> StdResult<(), Error> {
95
10
        let arti_path = key_spec
96
10
            .arti_path()
97
10
            .map_err(ArtiEphemeralKeystoreError::ArtiPathUnavailableError)?;
98
10
        let key_data = key.as_keystore_item()?;
99
10
        let item_type = key_data.item_type()?;
100

            
101
        // save to dictionary
102
10
        let mut key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
103
10
        let _ = key_dictionary.insert((arti_path, item_type), key_data);
104
10
        Ok(())
105
10
    }
106

            
107
8
    fn remove(
108
8
        &self,
109
8
        key_spec: &dyn KeySpecifier,
110
8
        item_type: &KeystoreItemType,
111
8
    ) -> StdResult<Option<()>, Error> {
112
8
        let arti_path = key_spec
113
8
            .arti_path()
114
8
            .map_err(ArtiEphemeralKeystoreError::ArtiPathUnavailableError)?;
115
8
        let mut key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
116
8
        match key_dictionary.remove(&(arti_path, item_type.clone())) {
117
6
            Some(key) if key.item_type()? != *item_type => {
118
2
                // This should only happen if some external factor alters the
119
2
                // process memory or if there's a bug in our implementation of
120
2
                // Keystore::insert().
121
2
                Err(internal!(
122
2
                    "the specified KeystoreItemType does not match key type of the removed key?!"
123
2
                )
124
2
                .into())
125
            }
126
4
            Some(_) => Ok(Some(())),
127
2
            None => Ok(None),
128
        }
129
8
    }
130

            
131
8
    fn list(&self) -> Result<Vec<KeystoreEntryResult<(KeyPath, KeystoreItemType)>>> {
132
8
        let key_dictionary = self.key_dictionary.lock().expect("lock poisoned");
133
8
        Ok(key_dictionary
134
8
            .keys()
135
10
            .map(|(arti_path, item_type)| Ok((arti_path.clone().into(), item_type.clone())))
136
8
            .collect())
137
8
    }
138
}
139

            
140
#[cfg(test)]
141
mod tests {
142
    // @@ begin test lint list maintained by maint/add_warning @@
143
    #![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
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
155

            
156
    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
    // some helper methods
167

            
168
    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
    // tests!
193

            
194
    #[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
        // verify no key in store
206
        assert!(!key_store
207
            .contains(key_spec().as_ref(), &key_type())
208
            .unwrap());
209

            
210
        // insert key and verify in store
211
        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
        // verify no result to get
224
        assert!(key_store
225
            .get(key_spec().as_ref(), &key_type())
226
            .unwrap()
227
            .is_none());
228

            
229
        // insert and verify get is a result
230
        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
        // Ensure the returned key is of the right type
240
        assert!(key.downcast::<ed25519::Keypair>().is_ok());
241

            
242
        // verify receiving a key of a different type results in the appropriate error
243
        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
        // verify inserting a key succeeds
275
        assert!(key_store
276
            .insert(key().as_ref(), key_spec().as_ref())
277
            .is_ok());
278

            
279
        // further ensure correct side effects
280
        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
        // verify removing from an empty store returns None
295
        assert!(key_store
296
            .remove(key_spec().as_ref(), &key_type())
297
            .unwrap()
298
            .is_none());
299

            
300
        // verify inserting and removing results in Some(())
301
        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
        // verify mismatched key type on removal results in the appropriate error
310
        {
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
        // verify empty by default
332
        assert!(key_store.list().unwrap().is_empty());
333

            
334
        // verify size 1 after inserting a key
335
        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
}