1
//! Code for managing multiple [`Keystore`](crate::Keystore)s.
2
//!
3
//! See the [`KeyMgr`] docs for more details.
4

            
5
use crate::raw::{RawEntryId, RawKeystoreEntry};
6
use crate::{
7
    ArtiPath, BoxedKeystore, KeyCertificateSpecifier, KeyPath, KeyPathError, KeyPathInfo,
8
    KeyPathInfoExtractor, KeyPathPattern, KeySpecifier, KeystoreCorruptionError,
9
    KeystoreEntryResult, KeystoreId, KeystoreSelector, Result,
10
};
11

            
12
use itertools::Itertools;
13
use std::iter;
14
use std::result::Result as StdResult;
15
use tor_error::{bad_api_usage, internal, into_bad_api_usage};
16
use tor_key_forge::{
17
    ItemType, Keygen, KeygenRng, KeystoreItemType, ToEncodableCert, ToEncodableKey,
18
};
19

            
20
/// A key manager that acts as a frontend to a primary [`Keystore`](crate::Keystore) and
21
/// any number of secondary [`Keystore`](crate::Keystore)s.
22
///
23
/// Note: [`KeyMgr`] is a low-level utility and does not implement caching (the key stores are
24
/// accessed for every read/write).
25
///
26
/// The `KeyMgr` accessors - currently just [`get()`](KeyMgr::get) -
27
/// search the configured key stores in order: first the primary key store,
28
/// and then the secondary stores, in order.
29
///
30
///
31
/// ## Concurrent key store access
32
///
33
/// The key stores will allow concurrent modification by different processes. In
34
/// order to implement this safely without locking, the key store operations (get,
35
/// insert, remove) will need to be atomic.
36
///
37
/// **Note**: [`KeyMgr::generate`] and [`KeyMgr::get_or_generate`] should **not** be used
38
/// concurrently with any other `KeyMgr` operation that mutates the same key
39
/// (i.e. a key with the same `ArtiPath`), because
40
/// their outcome depends on whether the selected key store
41
/// [`contains`][crate::Keystore::contains]
42
/// the specified key (and thus suffers from a TOCTOU race).
43
2460
#[derive(derive_builder::Builder)]
44
#[builder(pattern = "owned", build_fn(private, name = "build_unvalidated"))]
45
pub struct KeyMgr {
46
    /// The primary key store.
47
    primary_store: BoxedKeystore,
48
    /// The secondary key stores.
49
    #[builder(default, setter(custom))]
50
    secondary_stores: Vec<BoxedKeystore>,
51
    /// The key info extractors.
52
    ///
53
    /// These are initialized internally by [`KeyMgrBuilder::build`], using the values collected
54
    /// using `inventory`.
55
    #[builder(default, setter(skip))]
56
    key_info_extractors: Vec<&'static dyn KeyPathInfoExtractor>,
57
}
58

            
59
/// A keystore entry descriptor.
60
///
61
/// This identifies a key entry from a specific keystore.
62
/// The key entry can be retrieved, using [`KeyMgr::get_entry`],
63
/// or removed, using [`KeyMgr::remove_entry`].
64
///
65
/// Returned from [`KeyMgr::list_matching`].
66
#[derive(Clone, Debug, PartialEq, amplify::Getters)]
67
pub struct KeystoreEntry<'a> {
68
    /// The [`KeyPath`] of the key.
69
    key_path: KeyPath,
70
    /// The [`KeystoreItemType`] of the key.
71
    key_type: KeystoreItemType,
72
    /// The [`KeystoreId`] of the keystore where the key was found.
73
    #[getter(as_copy)]
74
    keystore_id: &'a KeystoreId,
75
    /// The [`RawEntryId`] of the key, an identifier used in
76
    /// `arti raw` operations.
77
    #[getter(skip)]
78
    raw_id: RawEntryId,
79
}
80

            
81
impl<'a> KeystoreEntry<'a> {
82
    /// Create a new `KeystoreEntry`
83
3586
    pub(crate) fn new(
84
3586
        key_path: KeyPath,
85
3586
        key_type: KeystoreItemType,
86
3586
        keystore_id: &'a KeystoreId,
87
3586
        raw_id: RawEntryId,
88
3586
    ) -> Self {
89
3586
        Self {
90
3586
            key_path,
91
3586
            key_type,
92
3586
            keystore_id,
93
3586
            raw_id,
94
3586
        }
95
3586
    }
96

            
97
    /// Return an instance of [`RawKeystoreEntry`]
98
    #[cfg(feature = "onion-service-cli-extra")]
99
    #[cfg_attr(docsrs, doc(cfg(feature = "onion-service-cli-extra")))]
100
    pub fn raw_entry(&self) -> RawKeystoreEntry {
101
        RawKeystoreEntry::new(self.raw_id.clone(), self.keystore_id.clone())
102
    }
103
}
104

            
105
// NOTE: Some methods require a `KeystoreEntryResult<KeystoreEntry>` as an
106
// argument (e.g.: `KeyMgr::raw_keystore_entry`). For this reason  implementing
107
// `From<KeystoreEntry<'a>> for KeystoreEntryResult<KeystoreEntry<'a>>` makes
108
// `KeystoreEntry` more ergonomic.
109
impl<'a> From<KeystoreEntry<'a>> for KeystoreEntryResult<KeystoreEntry<'a>> {
110
    fn from(val: KeystoreEntry<'a>) -> Self {
111
        Ok(val)
112
    }
113
}
114

            
115
impl KeyMgrBuilder {
116
    /// Construct a [`KeyMgr`] from this builder.
117
820
    pub fn build(self) -> StdResult<KeyMgr, KeyMgrBuilderError> {
118
        use itertools::Itertools as _;
119

            
120
820
        let mut keymgr = self.build_unvalidated()?;
121

            
122
882
        if !keymgr.all_stores().map(|s| s.id()).all_unique() {
123
            return Err(KeyMgrBuilderError::ValidationError(
124
                "the keystore IDs are not pairwise unique".into(),
125
            ));
126
820
        }
127
820

            
128
820
        keymgr.key_info_extractors = inventory::iter::<&'static dyn KeyPathInfoExtractor>
129
820
            .into_iter()
130
820
            .copied()
131
820
            .collect();
132
820

            
133
820
        Ok(keymgr)
134
820
    }
135
}
136

            
137
// TODO: auto-generate using define_list_builder_accessors/define_list_builder_helper
138
// when that becomes possible.
139
//
140
// See https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/1760#note_2969841
141
impl KeyMgrBuilder {
142
    /// Access the being-built list of secondary stores (resolving default)
143
    ///
144
    /// If the field has not yet been set or accessed, the default list will be
145
    /// constructed and a mutable reference to the now-defaulted list of builders
146
    /// will be returned.
147
16
    pub fn secondary_stores(&mut self) -> &mut Vec<BoxedKeystore> {
148
16
        self.secondary_stores.get_or_insert(Default::default())
149
16
    }
150

            
151
    /// Set the whole list (overriding the default)
152
    pub fn set_secondary_stores(mut self, list: Vec<BoxedKeystore>) -> Self {
153
        self.secondary_stores = Some(list);
154
        self
155
    }
156

            
157
    /// Inspect the being-built list (with default unresolved)
158
    ///
159
    /// If the list has not yet been set, or accessed, `&None` is returned.
160
    pub fn opt_secondary_stores(&self) -> &Option<Vec<BoxedKeystore>> {
161
        &self.secondary_stores
162
    }
163

            
164
    /// Mutably access the being-built list (with default unresolved)
165
    ///
166
    /// If the list has not yet been set, or accessed, `&mut None` is returned.
167
    pub fn opt_secondary_stores_mut(&mut self) -> &mut Option<Vec<BoxedKeystore>> {
168
        &mut self.secondary_stores
169
    }
170
}
171

            
172
inventory::collect!(&'static dyn crate::KeyPathInfoExtractor);
173

            
174
impl KeyMgr {
175
    /// Read a key from one of the key stores, and try to deserialize it as `K::Key`.
176
    ///
177
    /// The key returned is retrieved from the first key store that contains an entry for the given
178
    /// specifier.
179
    ///
180
    /// Returns `Ok(None)` if none of the key stores have the requested key.
181
426
    pub fn get<K: ToEncodableKey>(&self, key_spec: &dyn KeySpecifier) -> Result<Option<K>> {
182
426
        let result = self.get_from_store(key_spec, &K::Key::item_type(), self.all_stores())?;
183
426
        if result.is_none() {
184
            // If the key_spec is the specifier for the public part of a keypair,
185
            // try getting the pair and extracting the public portion from it.
186
330
            if let Some(key_pair_spec) = key_spec.keypair_specifier() {
187
80
                return Ok(self.get::<K::KeyPair>(&*key_pair_spec)?.map(|k| k.into()));
188
250
            }
189
96
        }
190
346
        Ok(result)
191
426
    }
192

            
193
    /// Retrieve the specified keystore entry, and try to deserialize it as `K::Key`.
194
    ///
195
    /// The key returned is retrieved from the key store specified in the [`KeystoreEntry`].
196
    ///
197
    /// Returns `Ok(None)` if the key store does not contain the requested entry.
198
    ///
199
    /// Returns an error if the specified `key_type` does not match `K::Key::item_type()`.
200
8
    pub fn get_entry<K: ToEncodableKey>(&self, entry: &KeystoreEntry) -> Result<Option<K>> {
201
8
        let selector = entry.keystore_id().into();
202
8
        let store = self.select_keystore(&selector)?;
203
8
        self.get_from_store(entry.key_path(), entry.key_type(), [store].into_iter())
204
8
    }
205

            
206
    /// Read the key identified by `key_spec`.
207
    ///
208
    /// The key returned is retrieved from the first key store that contains an entry for the given
209
    /// specifier.
210
    ///
211
    /// If the requested key does not exist in any of the key stores, this generates a new key of
212
    /// type `K` from the key created using using `K::Key`'s [`Keygen`] implementation, and inserts
213
    /// it into the specified keystore, returning the newly inserted value.
214
    ///
215
    /// This is a convenience wrapper around [`get()`](KeyMgr::get) and
216
    /// [`generate()`](KeyMgr::generate).
217
4
    pub fn get_or_generate<K>(
218
4
        &self,
219
4
        key_spec: &dyn KeySpecifier,
220
4
        selector: KeystoreSelector,
221
4
        rng: &mut dyn KeygenRng,
222
4
    ) -> Result<K>
223
4
    where
224
4
        K: ToEncodableKey,
225
4
        K::Key: Keygen,
226
4
    {
227
4
        match self.get(key_spec)? {
228
2
            Some(k) => Ok(k),
229
2
            None => self.generate(key_spec, selector, rng, false),
230
        }
231
4
    }
232

            
233
    /// Generate a new key of type `K`, and insert it into the key store specified by `selector`.
234
    ///
235
    /// If the key already exists in the specified key store, the `overwrite` flag is used to
236
    /// decide whether to overwrite it with a newly generated key.
237
    ///
238
    /// On success, this function returns the newly generated key.
239
    ///
240
    /// Returns [`Error::KeyAlreadyExists`](crate::Error::KeyAlreadyExists)
241
    /// if the key already exists in the specified key store and `overwrite` is `false`.
242
    ///
243
    /// **IMPORTANT**: using this function concurrently with any other `KeyMgr` operation that
244
    /// mutates the key store state is **not** recommended, as it can yield surprising results! The
245
    /// outcome of [`KeyMgr::generate`] depends on whether the selected key store
246
    /// [`contains`][crate::Keystore::contains] the specified key, and thus suffers from a TOCTOU race.
247
    //
248
    // TODO (#1119): can we make this less racy without a lock? Perhaps we should say we'll always
249
    // overwrite any existing keys.
250
    //
251
    // TODO: consider replacing the overwrite boolean with a GenerateOptions type
252
    // (sort of like std::fs::OpenOptions)
253
212
    pub fn generate<K>(
254
212
        &self,
255
212
        key_spec: &dyn KeySpecifier,
256
212
        selector: KeystoreSelector,
257
212
        rng: &mut dyn KeygenRng,
258
212
        overwrite: bool,
259
212
    ) -> Result<K>
260
212
    where
261
212
        K: ToEncodableKey,
262
212
        K::Key: Keygen,
263
212
    {
264
212
        let store = self.select_keystore(&selector)?;
265

            
266
212
        if overwrite || !store.contains(key_spec, &K::Key::item_type())? {
267
210
            let key = K::Key::generate(rng)?;
268
210
            store.insert(&key, key_spec)?;
269

            
270
210
            Ok(K::from_encodable_key(key))
271
        } else {
272
2
            Err(crate::Error::KeyAlreadyExists)
273
        }
274
212
    }
275

            
276
    /// Insert `key` into the [`Keystore`](crate::Keystore) specified by `selector`.
277
    ///
278
    /// If the key already exists in the specified key store, the `overwrite` flag is used to
279
    /// decide whether to overwrite it with the provided key.
280
    ///
281
    /// If this key is not already in the keystore, `None` is returned.
282
    ///
283
    /// If this key already exists in the keystore, its value is updated
284
    /// and the old value is returned.
285
    ///
286
    /// Returns an error if the selected keystore is not the primary keystore or one of the
287
    /// configured secondary stores.
288
34
    pub fn insert<K: ToEncodableKey>(
289
34
        &self,
290
34
        key: K,
291
34
        key_spec: &dyn KeySpecifier,
292
34
        selector: KeystoreSelector,
293
34
        overwrite: bool,
294
34
    ) -> Result<Option<K>> {
295
34
        let key = key.to_encodable_key();
296
34
        let store = self.select_keystore(&selector)?;
297
34
        let key_type = K::Key::item_type();
298
34
        let old_key: Option<K> = self.get_from_store(key_spec, &key_type, [store].into_iter())?;
299

            
300
34
        if old_key.is_some() && !overwrite {
301
2
            Err(crate::Error::KeyAlreadyExists)
302
        } else {
303
32
            let () = store.insert(&key, key_spec)?;
304
32
            Ok(old_key)
305
        }
306
34
    }
307

            
308
    /// Remove the key identified by `key_spec` from the [`Keystore`](crate::Keystore)
309
    /// specified by `selector`.
310
    ///
311
    /// Returns an error if the selected keystore is not the primary keystore or one of the
312
    /// configured secondary stores.
313
    ///
314
    /// Returns the value of the removed key,
315
    /// or `Ok(None)` if the key does not exist in the requested keystore.
316
    ///
317
    /// Returns `Err` if an error occurred while trying to remove the key.
318
40
    pub fn remove<K: ToEncodableKey>(
319
40
        &self,
320
40
        key_spec: &dyn KeySpecifier,
321
40
        selector: KeystoreSelector,
322
40
    ) -> Result<Option<K>> {
323
40
        let store = self.select_keystore(&selector)?;
324
38
        let key_type = K::Key::item_type();
325
38
        let old_key: Option<K> = self.get_from_store(key_spec, &key_type, [store].into_iter())?;
326

            
327
38
        store.remove(key_spec, &key_type)?;
328

            
329
38
        Ok(old_key)
330
40
    }
331

            
332
    /// Remove the specified keystore entry.
333
    ///
334
    /// Like [`KeyMgr::remove`], except this function does not return the value of the removed key.
335
    ///
336
    /// A return value of `Ok(None)` indicates the key was not found in the specified key store,
337
    /// whereas `Ok(Some(())` means the key was successfully removed.
338
    //
339
    // TODO: We should be consistent and return the removed key.
340
    //
341
    // This probably will involve changing the return type of Keystore::remove
342
    // to Result<Option<ErasedKey>>.
343
644
    pub fn remove_entry(&self, entry: &KeystoreEntry) -> Result<Option<()>> {
344
644
        let selector = entry.keystore_id().into();
345
644
        let store = self.select_keystore(&selector)?;
346

            
347
644
        store.remove(entry.key_path(), entry.key_type())
348
644
    }
349

            
350
    /// Remove the specified keystore entry.
351
    ///
352
    /// Similar to [`KeyMgr::remove_entry`], except this method accepts both recognized and
353
    /// unrecognized entries, identified by a raw id (in the form of a `&str`) and a
354
    /// [`KeystoreId`].
355
    ///
356
    /// Returns an error if the entry could not be removed, or if the entry doesn't exist.
357
    #[cfg(feature = "onion-service-cli-extra")]
358
4
    pub fn remove_unchecked(&self, raw_id: &str, keystore_id: &KeystoreId) -> Result<()> {
359
4
        let selector = KeystoreSelector::from(keystore_id);
360
4
        let store = self.select_keystore(&selector)?;
361
4
        let raw_id = store.raw_entry_id(raw_id)?;
362
4
        let store = self.select_keystore(&selector)?;
363
4
        store.remove_unchecked(&raw_id)
364
4
    }
365

            
366
    /// Return the keystore entry descriptors of the keys matching the specified [`KeyPathPattern`].
367
    ///
368
    /// NOTE: This searches for matching keys in _all_ keystores.
369
    ///
370
    /// NOTE: This function only returns the *recognized* entries that match the provided pattern.
371
    /// The unrecognized entries (i.e. those that do not have a valid [`KeyPath`]) will be filtered out,
372
    /// even if they match the specified pattern.
373
484
    pub fn list_matching(&self, pat: &KeyPathPattern) -> Result<Vec<KeystoreEntry>> {
374
484
        self.all_stores()
375
502
            .map(|store| -> Result<Vec<_>> {
376
488
                Ok(store
377
488
                    .list()?
378
488
                    .into_iter()
379
3528
                    .filter_map(|entry| entry.ok())
380
3528
                    .filter(|entry| entry.key_path().matches(pat))
381
488
                    .collect::<Vec<_>>())
382
502
            })
383
484
            .flatten_ok()
384
484
            .collect::<Result<Vec<_>>>()
385
484
    }
386

            
387
    /// List keys and certificates of the specified keystore.
388
    #[cfg(feature = "onion-service-cli-extra")]
389
4
    pub fn list_by_id(&self, id: &KeystoreId) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
390
4
        self.find_keystore(id)?.list()
391
4
    }
392

            
393
    /// List keys and certificates of all the keystores.
394
    #[cfg(feature = "onion-service-cli-extra")]
395
2
    pub fn list(&self) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
396
2
        self.all_stores()
397
7
            .map(|store| -> Result<Vec<_>> { store.list() })
398
2
            .flatten_ok()
399
2
            .collect::<Result<Vec<_>>>()
400
2
    }
401

            
402
    /// List keys and certificates of a specific keystore.
403
    #[cfg(feature = "onion-service-cli-extra")]
404
42
    pub fn list_keystores(&self) -> Vec<KeystoreId> {
405
42
        self.all_stores()
406
48
            .map(|store| store.id().to_owned())
407
42
            .collect()
408
42
    }
409

            
410
    /// Describe the specified key.
411
    ///
412
    /// Returns [`KeyPathError::Unrecognized`] if none of the registered
413
    /// [`KeyPathInfoExtractor`]s is able to parse the specified [`KeyPath`].
414
    ///
415
    /// This function uses the [`KeyPathInfoExtractor`]s registered using
416
    /// [`register_key_info_extractor`](crate::register_key_info_extractor),
417
    /// or by [`DefaultKeySpecifier`](crate::derive_deftly_template_KeySpecifier).
418
    pub fn describe(&self, path: &KeyPath) -> StdResult<KeyPathInfo, KeyPathError> {
419
        for info_extractor in &self.key_info_extractors {
420
            if let Ok(info) = info_extractor.describe(path) {
421
                return Ok(info);
422
            }
423
        }
424

            
425
        Err(KeyPathError::Unrecognized(path.clone()))
426
    }
427

            
428
    /// Attempt to retrieve a key from one of the specified `stores`.
429
    ///
430
    /// Returns the `<K as ToEncodableKey>::Key` representation of the key.
431
    ///
432
    /// See [`KeyMgr::get`] for more details.
433
522
    fn get_from_store_raw<'a, K: ItemType>(
434
522
        &self,
435
522
        key_spec: &dyn KeySpecifier,
436
522
        key_type: &KeystoreItemType,
437
522
        stores: impl Iterator<Item = &'a BoxedKeystore>,
438
522
    ) -> Result<Option<K>> {
439
522
        let static_key_type = K::item_type();
440
522
        if key_type != &static_key_type {
441
            return Err(internal!(
442
                "key type {:?} does not match the key type {:?} of requested key K::Key",
443
                key_type,
444
                static_key_type
445
            )
446
            .into());
447
522
        }
448

            
449
962
        for store in stores {
450
584
            let key = match store.get(key_spec, &K::item_type()) {
451
                Ok(None) => {
452
                    // The key doesn't exist in this store, so we check the next one...
453
440
                    continue;
454
                }
455
144
                Ok(Some(k)) => k,
456
                Err(e) => {
457
                    // Note: we immediately return if one of the keystores is inaccessible.
458
                    return Err(e);
459
                }
460
            };
461

            
462
            // Found it! Now try to downcast it to the right type (this should _not_ fail)...
463
144
            let key: K = key
464
144
                .downcast::<K>()
465
144
                .map(|k| *k)
466
144
                .map_err(|_| internal!("failed to downcast key to requested type"))?;
467

            
468
144
            return Ok(Some(key));
469
        }
470

            
471
378
        Ok(None)
472
522
    }
473

            
474
    /// Attempt to retrieve a key from one of the specified `stores`.
475
    ///
476
    /// See [`KeyMgr::get`] for more details.
477
514
    fn get_from_store<'a, K: ToEncodableKey>(
478
514
        &self,
479
514
        key_spec: &dyn KeySpecifier,
480
514
        key_type: &KeystoreItemType,
481
514
        stores: impl Iterator<Item = &'a BoxedKeystore>,
482
514
    ) -> Result<Option<K>> {
483
514
        let Some(key) = self.get_from_store_raw::<K::Key>(key_spec, key_type, stores)? else {
484
370
            return Ok(None);
485
        };
486

            
487
144
        Ok(Some(K::from_encodable_key(key)))
488
514
    }
489

            
490
    /// Read the specified key and certificate from one of the key stores,
491
    /// deserializing the subject key as `K::Key`, the cert as `C::Cert`,
492
    /// and the signing key as `C::SigningKey`.
493
    ///
494
    /// Returns `Ok(None)` if none of the key stores have the requested key.
495
    ///
496
    // Note: the behavior of this function is a bit inconsistent with
497
    // get_or_generate_key_and_cert: here, if the cert is absent but
498
    // its subject key is not, we return Ok(None).
499
    // In get_or_generate_key_and_cert, OTOH< we return an error in that case
500
    // (because we can't possibly generate the missing subject key
501
    // without overwriting the cert of the missing key).
502
    ///
503
    /// This function validates the certificate using [`ToEncodableCert::validate`],
504
    /// returning an error if it is invalid or missing.
505
    #[cfg(feature = "experimental-api")]
506
    pub fn get_key_and_cert<K, C>(
507
        &self,
508
        spec: &dyn KeyCertificateSpecifier,
509
    ) -> Result<Option<(K, C)>>
510
    where
511
        K: ToEncodableKey,
512
        C: ToEncodableCert<K>,
513
    {
514
        let subject_key_spec = spec.subject_key_specifier();
515
        // Get the subject key...
516
        let Some(key) =
517
            self.get_from_store::<K>(subject_key_spec, &K::Key::item_type(), self.all_stores())?
518
        else {
519
            return Ok(None);
520
        };
521

            
522
        let subject_key_arti_path = subject_key_spec
523
            .arti_path()
524
            .map_err(|_| bad_api_usage!("subject key does not have an ArtiPath?!"))?;
525
        let cert_spec =
526
            ArtiPath::from_path_and_denotators(subject_key_arti_path, &spec.cert_denotators())
527
                .map_err(into_bad_api_usage!("invalid certificate specifier"))?;
528

            
529
        let Some(cert) = self.get_from_store_raw::<C::ParsedCert>(
530
            &cert_spec,
531
            &<C::ParsedCert as ItemType>::item_type(),
532
            self.all_stores(),
533
        )?
534
        else {
535
            return Err(KeystoreCorruptionError::MissingCertificate.into());
536
        };
537

            
538
        // Finally, get the signing key and validate the cert
539
        let signed_with = self.get_cert_signing_key::<K, C>(spec)?;
540
        let cert = C::validate(cert, &key, &signed_with)?;
541

            
542
        Ok(Some((key, cert)))
543
    }
544

            
545
    /// Like [`KeyMgr::get_key_and_cert`], except this function also generates the subject key
546
    /// and its corresponding certificate if they don't already exist.
547
    ///
548
    /// If the key certificate is missing, it will be generated
549
    /// from the subject key and signing key using the provided `make_certificate` callback.
550
    ///
551
    /// Generates the missing key and/or certificate as follows:
552
    ///
553
    /// ```text
554
    /// | Subject Key exists | Signing Key exists | Cert exists | Action                                 |
555
    /// |--------------------|--------------------|-------------|----------------------------------------|
556
    /// | Y                  | Y                  | Y           | Validate cert, return key and cert     |
557
    /// |                    |                    |             | if valid, error otherwise              |
558
    /// |--------------------|--------------------|-------------|----------------------------------------|
559
    /// | N                  | Y                  | N           | Generate subject key and               |
560
    /// |                    |                    |             | a new cert signed with signing key     |
561
    /// |--------------------|--------------------|-------------|----------------------------------------|
562
    /// | Y                  | Y                  | N           | Generate cert signed with signing key  |
563
    /// |--------------------|--------------------|-------------|----------------------------------------|
564
    /// | Y                  | N                  | N           | Error - cannot validate cert           |
565
    /// |                    |                    |             | if signing key is not available        |
566
    /// |--------------------|--------------------|-------------|----------------------------------------|
567
    /// | Y/N                | N                  | N           | Error - cannot generate cert           |
568
    /// |                    |                    |             | if signing key is not available        |
569
    /// |--------------------|--------------------|-------------|----------------------------------------|
570
    /// | N                  | Y/N                | Y           | Error - subject key was removed?       |
571
    /// |                    |                    |             | (we found the cert,                    |
572
    /// |                    |                    |             | but the subject key is missing)        |
573
    /// ```
574
    ///
575
    //
576
    // Note; the table above isn't a markdown table because CommonMark-flavor markdown
577
    // doesn't support multiline text in tables. Even if we trim down the text,
578
    // the resulting markdown table would be pretty unreadable in raw form
579
    // (it would have several excessively long lines, over 120 chars in len).
580
    #[cfg(feature = "experimental-api")]
581
8
    pub fn get_or_generate_key_and_cert<K, C>(
582
8
        &self,
583
8
        spec: &dyn KeyCertificateSpecifier,
584
8
        make_certificate: impl FnOnce(&K, &<C as ToEncodableCert<K>>::SigningKey) -> C,
585
8
        selector: KeystoreSelector,
586
8
        rng: &mut dyn KeygenRng,
587
8
    ) -> Result<(K, C)>
588
8
    where
589
8
        K: ToEncodableKey,
590
8
        K::Key: Keygen,
591
8
        C: ToEncodableCert<K>,
592
8
    {
593
8
        let subject_key_spec = spec.subject_key_specifier();
594
8
        let subject_key_arti_path = subject_key_spec
595
8
            .arti_path()
596
8
            .map_err(|_| bad_api_usage!("subject key does not have an ArtiPath?!"))?;
597

            
598
8
        let cert_specifier =
599
8
            ArtiPath::from_path_and_denotators(subject_key_arti_path, &spec.cert_denotators())
600
8
                .map_err(into_bad_api_usage!("invalid certificate specifier"))?;
601

            
602
8
        let maybe_cert = self.get_from_store_raw::<C::ParsedCert>(
603
8
            &cert_specifier,
604
8
            &C::ParsedCert::item_type(),
605
8
            self.all_stores(),
606
8
        )?;
607

            
608
8
        let maybe_subject_key = self.get::<K>(subject_key_spec)?;
609

            
610
8
        match (&maybe_cert, &maybe_subject_key) {
611
            (Some(_), None) => {
612
                return Err(KeystoreCorruptionError::MissingSubjectKey.into());
613
            }
614
8
            _ => {
615
8
                // generate key and/or cert
616
8
            }
617
        }
618
8
        let subject_key = match maybe_subject_key {
619
4
            Some(key) => key,
620
4
            _ => self.generate(subject_key_spec, selector, rng, false)?,
621
        };
622

            
623
8
        let signed_with = self.get_cert_signing_key::<K, C>(spec)?;
624
4
        let cert = match maybe_cert {
625
            Some(cert) => C::validate(cert, &subject_key, &signed_with)?,
626
            None => {
627
4
                let cert = make_certificate(&subject_key, &signed_with);
628
4

            
629
4
                let () = self.insert_cert(cert.clone(), &cert_specifier, selector)?;
630

            
631
4
                cert
632
            }
633
        };
634

            
635
4
        Ok((subject_key, cert))
636
8
    }
637

            
638
    /// Return an iterator over all configured stores.
639
4506
    fn all_stores(&self) -> impl Iterator<Item = &BoxedKeystore> {
640
4506
        iter::once(&self.primary_store).chain(self.secondary_stores.iter())
641
4506
    }
642

            
643
    /// Return the [`Keystore`](crate::Keystore) matching the specified `selector`.
644
    ///
645
    /// Returns an error if the selected keystore is not the primary keystore or one of the
646
    /// configured secondary stores.
647
2158
    fn select_keystore(&self, selector: &KeystoreSelector) -> Result<&BoxedKeystore> {
648
2158
        match selector {
649
696
            KeystoreSelector::Id(keystore_id) => self.find_keystore(keystore_id),
650
1462
            KeystoreSelector::Primary => Ok(&self.primary_store),
651
        }
652
2158
    }
653

            
654
    /// Return the [`Keystore`](crate::Keystore) with the specified `id`.
655
    ///
656
    /// Returns an error if the specified ID is not the ID of the primary keystore or
657
    /// the ID of one of the configured secondary stores.
658
700
    fn find_keystore(&self, id: &KeystoreId) -> Result<&BoxedKeystore> {
659
700
        self.all_stores()
660
804
            .find(|keystore| keystore.id() == id)
661
701
            .ok_or_else(|| bad_api_usage!("could not find keystore with ID {id}").into())
662
700
    }
663

            
664
    /// Get the signing key of the certificate described by `spec`.
665
    ///
666
    /// Returns a [`KeystoreCorruptionError::MissingSigningKey`] error
667
    /// if the signing key doesn't exist in any of the keystores.
668
    #[cfg(feature = "experimental-api")]
669
8
    fn get_cert_signing_key<K, C>(
670
8
        &self,
671
8
        spec: &dyn KeyCertificateSpecifier,
672
8
    ) -> Result<C::SigningKey>
673
8
    where
674
8
        K: ToEncodableKey,
675
8
        C: ToEncodableCert<K>,
676
8
    {
677
8
        let Some(signing_key_spec) = spec.signing_key_specifier() else {
678
            return Err(bad_api_usage!(
679
                "signing key specifier is None, but external signing key was not provided?"
680
            )
681
            .into());
682
        };
683

            
684
8
        let Some(signing_key) = self.get_from_store::<C::SigningKey>(
685
8
            signing_key_spec,
686
8
            &<C::SigningKey as ToEncodableKey>::Key::item_type(),
687
8
            self.all_stores(),
688
8
        )?
689
        else {
690
4
            return Err(KeystoreCorruptionError::MissingSigningKey.into());
691
        };
692

            
693
4
        Ok(signing_key)
694
8
    }
695

            
696
    /// Insert `cert` into the [`Keystore`](crate::Keystore) specified by `selector`.
697
    ///
698
    /// If the key already exists in the specified key store, it will be overwritten.
699
    ///
700
    // NOTE: if we ever make this public we should rethink/improve its API.
701
    // TODO: maybe fold this into insert() somehow?
702
4
    fn insert_cert<K, C>(
703
4
        &self,
704
4
        cert: C,
705
4
        cert_spec: &dyn KeySpecifier,
706
4
        selector: KeystoreSelector,
707
4
    ) -> Result<()>
708
4
    where
709
4
        K: ToEncodableKey,
710
4
        K::Key: Keygen,
711
4
        C: ToEncodableCert<K>,
712
4
    {
713
4
        let cert = cert.to_encodable_cert();
714
4
        let store = self.select_keystore(&selector)?;
715

            
716
4
        let () = store.insert(&cert, cert_spec)?;
717
4
        Ok(())
718
4
    }
719
}
720

            
721
#[cfg(test)]
722
mod tests {
723
    // @@ begin test lint list maintained by maint/add_warning @@
724
    #![allow(clippy::bool_assert_comparison)]
725
    #![allow(clippy::clone_on_copy)]
726
    #![allow(clippy::dbg_macro)]
727
    #![allow(clippy::mixed_attributes_style)]
728
    #![allow(clippy::print_stderr)]
729
    #![allow(clippy::print_stdout)]
730
    #![allow(clippy::single_char_pattern)]
731
    #![allow(clippy::unwrap_used)]
732
    #![allow(clippy::unchecked_duration_subtraction)]
733
    #![allow(clippy::useless_vec)]
734
    #![allow(clippy::needless_pass_by_value)]
735
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
736
    use super::*;
737
    use crate::keystore::arti::err::{ArtiNativeKeystoreError, MalformedPathError};
738
    use crate::raw::{RawEntryId, RawKeystoreEntry};
739
    use crate::{
740
        ArtiPath, ArtiPathUnavailableError, Error, KeyPath, KeystoreEntryResult, KeystoreError,
741
        UnrecognizedEntryError,
742
    };
743
    use std::path::PathBuf;
744
    use std::result::Result as StdResult;
745
    use std::str::FromStr;
746
    use std::sync::{Arc, RwLock};
747
    use std::time::{Duration, SystemTime};
748
    use tor_basic_utils::test_rng::testing_rng;
749
    use tor_cert::CertifiedKey;
750
    use tor_cert::Ed25519Cert;
751
    use tor_error::{ErrorKind, HasKind};
752
    use tor_key_forge::{
753
        CertData, EncodableItem, ErasedKey, InvalidCertError, KeyType, KeystoreItem,
754
    };
755
    use tor_llcrypto::pk::ed25519::{self, Ed25519PublicKey as _};
756
    use tor_llcrypto::rng::FakeEntropicRng;
757

            
758
    /// Metadata structure for tracking key operations in tests.
759
    #[derive(Clone, Debug, PartialEq)]
760
    struct KeyMetadata {
761
        /// The identifier for the item (e.g., "coot", "moorhen").
762
        item_id: String,
763
        /// The keystore from which the item was retrieved.
764
        ///
765
        /// Set by `Keystore::get`.
766
        retrieved_from: Option<KeystoreId>,
767
        /// Whether the item was generated via `Keygen::generate`.
768
        is_generated: bool,
769
    }
770

            
771
    /// Metadata structure for tracking certificate operations in tests.
772
    #[derive(Clone, Debug, PartialEq)]
773
    struct CertMetadata {
774
        /// The identifier for the subject key (e.g., "coot").
775
        subject_key_id: String,
776
        /// The identifier for the signing key (e.g., "moorhen").
777
        signing_key_id: String,
778
        /// The keystore from which the certificate was retrieved.
779
        ///
780
        /// Set by `Keystore::get`.
781
        retrieved_from: Option<KeystoreId>,
782
        /// Whether the certificate was freshly generated (i.e. returned from the "or generate"
783
        /// branch of `get_or_generate()`) or retrieved from a keystore.
784
        is_generated: bool,
785
    }
786

            
787
    /// Metadata structure for tracking item operations in tests.
788
    #[derive(Clone, Debug, PartialEq, derive_more::From)]
789
    enum ItemMetadata {
790
        /// Metadata about a key.
791
        Key(KeyMetadata),
792
        /// Metadata about a certificate.
793
        Cert(CertMetadata),
794
    }
795

            
796
    impl ItemMetadata {
797
        /// Get the item ID.
798
        ///
799
        /// For keys, this returns the key's ID.
800
        /// For certificates, this returns a formatted string identifying the subject key.
801
        fn item_id(&self) -> &str {
802
            match self {
803
                ItemMetadata::Key(k) => &k.item_id,
804
                ItemMetadata::Cert(c) => &c.subject_key_id,
805
            }
806
        }
807

            
808
        /// Get retrieved_from.
809
        fn retrieved_from(&self) -> Option<&KeystoreId> {
810
            match self {
811
                ItemMetadata::Key(k) => k.retrieved_from.as_ref(),
812
                ItemMetadata::Cert(c) => c.retrieved_from.as_ref(),
813
            }
814
        }
815

            
816
        /// Get is_generated.
817
        fn is_generated(&self) -> bool {
818
            match self {
819
                ItemMetadata::Key(k) => k.is_generated,
820
                ItemMetadata::Cert(c) => c.is_generated,
821
            }
822
        }
823

            
824
        /// Set the retrieved_from field to the specified keystore ID.
825
        fn set_retrieved_from(&mut self, id: KeystoreId) {
826
            match self {
827
                ItemMetadata::Key(meta) => meta.retrieved_from = Some(id),
828
                ItemMetadata::Cert(meta) => meta.retrieved_from = Some(id),
829
            }
830
        }
831

            
832
        /// Returns a reference to key metadata if this is a Key variant.
833
        fn as_key(&self) -> Option<&KeyMetadata> {
834
            match self {
835
                ItemMetadata::Key(meta) => Some(meta),
836
                _ => None,
837
            }
838
        }
839

            
840
        /// Returns a reference to certificate metadata if this is a Cert variant.
841
        fn as_cert(&self) -> Option<&CertMetadata> {
842
            match self {
843
                ItemMetadata::Cert(meta) => Some(meta),
844
                _ => None,
845
            }
846
        }
847
    }
848

            
849
    /// The type of "key" stored in the test key stores.
850
    #[derive(Clone, Debug)]
851
    struct TestItem {
852
        /// The underlying key.
853
        item: KeystoreItem,
854
        /// Metadata about the key.
855
        meta: ItemMetadata,
856
    }
857

            
858
    /// A "certificate" used for testing purposes.
859
    #[derive(Clone, Debug)]
860
    struct AlwaysValidCert(TestItem);
861

            
862
    /// The corresponding fake public key type.
863
    #[derive(Clone, Debug)]
864
    struct TestPublicKey {
865
        /// The underlying key.
866
        key: KeystoreItem,
867
    }
868

            
869
    impl From<TestItem> for TestPublicKey {
870
        fn from(tk: TestItem) -> TestPublicKey {
871
            TestPublicKey { key: tk.item }
872
        }
873
    }
874

            
875
    impl TestItem {
876
        /// Create a new test key with the specified metadata.
877
        fn new(item_id: &str) -> Self {
878
            let mut rng = testing_rng();
879
            TestItem {
880
                item: ed25519::Keypair::generate(&mut rng)
881
                    .as_keystore_item()
882
                    .unwrap(),
883
                meta: ItemMetadata::Key(KeyMetadata {
884
                    item_id: item_id.to_string(),
885
                    retrieved_from: None,
886
                    is_generated: false,
887
                }),
888
            }
889
        }
890
    }
891

            
892
    impl Keygen for TestItem {
893
        fn generate(mut rng: &mut dyn KeygenRng) -> tor_key_forge::Result<Self>
894
        where
895
            Self: Sized,
896
        {
897
            Ok(TestItem {
898
                item: ed25519::Keypair::generate(&mut rng).as_keystore_item()?,
899
                meta: ItemMetadata::Key(KeyMetadata {
900
                    item_id: "generated_test_key".to_string(),
901
                    retrieved_from: None,
902
                    is_generated: true,
903
                }),
904
            })
905
        }
906
    }
907

            
908
    impl ItemType for TestItem {
909
        fn item_type() -> KeystoreItemType
910
        where
911
            Self: Sized,
912
        {
913
            // Dummy value
914
            KeyType::Ed25519Keypair.into()
915
        }
916
    }
917

            
918
    impl EncodableItem for TestItem {
919
        fn as_keystore_item(&self) -> tor_key_forge::Result<KeystoreItem> {
920
            Ok(self.item.clone())
921
        }
922
    }
923

            
924
    impl ToEncodableKey for TestItem {
925
        type Key = Self;
926
        type KeyPair = Self;
927

            
928
        fn to_encodable_key(self) -> Self::Key {
929
            self
930
        }
931

            
932
        fn from_encodable_key(key: Self::Key) -> Self {
933
            key
934
        }
935
    }
936

            
937
    impl ItemType for TestPublicKey {
938
        fn item_type() -> KeystoreItemType
939
        where
940
            Self: Sized,
941
        {
942
            KeyType::Ed25519PublicKey.into()
943
        }
944
    }
945

            
946
    impl EncodableItem for TestPublicKey {
947
        fn as_keystore_item(&self) -> tor_key_forge::Result<KeystoreItem> {
948
            Ok(self.key.clone())
949
        }
950
    }
951

            
952
    impl ToEncodableKey for TestPublicKey {
953
        type Key = Self;
954
        type KeyPair = TestItem;
955

            
956
        fn to_encodable_key(self) -> Self::Key {
957
            self
958
        }
959

            
960
        fn from_encodable_key(key: Self::Key) -> Self {
961
            key
962
        }
963
    }
964

            
965
    impl ToEncodableCert<TestItem> for AlwaysValidCert {
966
        type ParsedCert = TestItem;
967
        type EncodableCert = TestItem;
968
        type SigningKey = TestItem;
969

            
970
        fn validate(
971
            cert: Self::ParsedCert,
972
            _subject: &TestItem,
973
            _signed_with: &Self::SigningKey,
974
        ) -> StdResult<Self, InvalidCertError> {
975
            // AlwaysValidCert is always valid
976
            Ok(Self(cert))
977
        }
978

            
979
        /// Convert this cert to a type that implements [`EncodableKey`].
980
        fn to_encodable_cert(self) -> Self::EncodableCert {
981
            self.0
982
        }
983
    }
984

            
985
    #[derive(thiserror::Error, Debug, Clone, derive_more::Display)]
986
    enum MockKeystoreError {
987
        NotFound,
988
    }
989

            
990
    impl KeystoreError for MockKeystoreError {}
991

            
992
    impl HasKind for MockKeystoreError {
993
        fn kind(&self) -> ErrorKind {
994
            // Return a dummy ErrorKind for the purposes of this test
995
            tor_error::ErrorKind::Other
996
        }
997
    }
998

            
999
    fn build_raw_id_path<T: ToString>(key_path: &T, key_type: &KeystoreItemType) -> RawEntryId {
        let mut path = key_path.to_string();
        path.push('.');
        path.push_str(&key_type.arti_extension());
        RawEntryId::Path(PathBuf::from(&path))
    }
    macro_rules! impl_keystore {
        ($name:tt, $id:expr $(,$unrec:expr)?) => {
            struct $name {
                inner: RwLock<
                    Vec<StdResult<(ArtiPath, KeystoreItemType, TestItem), UnrecognizedEntryError>>,
                >,
                id: KeystoreId,
            }
            impl Default for $name {
                fn default() -> Self {
                    let id = KeystoreId::from_str($id).unwrap();
                    let inner: RwLock<
                        Vec<
                            StdResult<
                                (ArtiPath, KeystoreItemType, TestItem),
                                UnrecognizedEntryError,
                            >,
                        >,
                    > = Default::default();
                    // Populate the Keystore with the specified number
                    // of unrecognized entries.
                    $(
                        for i in 0..$unrec {
                            let invalid_key_path =
                                PathBuf::from(&format!("unrecognized_entry{}", i));
                            let raw_id = RawEntryId::Path(invalid_key_path.clone());
                            let entry = RawKeystoreEntry::new(raw_id, id.clone()).into();
                            let entry = UnrecognizedEntryError::new(
                                entry,
                                Arc::new(ArtiNativeKeystoreError::MalformedPath {
                                    path: invalid_key_path,
                                    err: MalformedPathError::NoExtension,
                                }),
                            );
                            inner.write().unwrap().push(Err(entry));
                        }
                    )?
                    Self {
                        inner,
                        id,
                    }
                }
            }
            #[allow(dead_code)] // this is only dead code for Keystore1
            impl $name {
                fn new_boxed() -> BoxedKeystore {
                    Box::<Self>::default()
                }
            }
            impl crate::Keystore for $name {
                fn contains(
                    &self,
                    key_spec: &dyn KeySpecifier,
                    item_type: &KeystoreItemType,
                ) -> Result<bool> {
                    let wanted_arti_path = key_spec.arti_path().unwrap();
                    Ok(self
                        .inner
                        .read()
                        .unwrap()
                        .iter()
                        .find(|res| match res {
                            Ok((spec, ty, _)) => spec == &wanted_arti_path && ty == item_type,
                            Err(_) => false,
                        })
                        .is_some())
                }
                fn id(&self) -> &KeystoreId {
                    &self.id
                }
                fn get(
                    &self,
                    key_spec: &dyn KeySpecifier,
                    item_type: &KeystoreItemType,
                ) -> Result<Option<ErasedKey>> {
                    let key_spec = key_spec.arti_path().unwrap();
                    Ok(self.inner.read().unwrap().iter().find_map(|res| {
                        match res {
                            Ok((arti_path, ty, k)) => {
                                if arti_path == &key_spec && ty == item_type {
                                    let mut k = k.clone();
                                    k.meta.set_retrieved_from(self.id().clone());
                                    return Some(Box::new(k) as Box<dyn ItemType>);
                                }
                            }
                            Err(_) => {}
                        }
                        None
                    }))
                }
                #[cfg(feature = "onion-service-cli-extra")]
                fn raw_entry_id(&self, raw_id: &str) -> Result<RawEntryId> {
                    Ok(RawEntryId::Path(
                        PathBuf::from(raw_id.to_string()),
                    ))
                }
                fn insert(
                    &self,
                    key: &dyn EncodableItem,
                    key_spec: &dyn KeySpecifier,
                ) -> Result<()> {
                    let key = key.downcast_ref::<TestItem>().unwrap();
                    let item = key.as_keystore_item()?;
                    let meta = key.meta.clone();
                    let item_type = item.item_type()?;
                    let key = TestItem { item, meta };
                    self.inner
                        .write()
                        .unwrap()
                        // TODO: `insert` is used instead of `push`, because some of the
                        // tests (mainly `insert_and_get` and `keygen`) fail otherwise.
                        // It could be a good idea to use `push` and adapt the tests,
                        // in order to reduce cognitive complexity.
                        .insert(0, (Ok((key_spec.arti_path().unwrap(), item_type, key))));
                    Ok(())
                }
                fn remove(
                    &self,
                    key_spec: &dyn KeySpecifier,
                    item_type: &KeystoreItemType,
                ) -> Result<Option<()>> {
                    let wanted_arti_path = key_spec.arti_path().unwrap();
                    let index = self.inner.read().unwrap().iter().position(|res| {
                        if let Ok((arti_path, ty, _)) = res {
                            arti_path == &wanted_arti_path && ty == item_type
                        } else {
                            false
                        }
                    });
                    let Some(index) = index else {
                        return Ok(None);
                    };
                    let _ = self.inner.write().unwrap().remove(index);
                    Ok(Some(()))
                }
                #[cfg(feature = "onion-service-cli-extra")]
                fn remove_unchecked(&self, entry_id: &RawEntryId) -> Result<()> {
                    let index = self.inner.read().unwrap().iter().position(|res| match res {
                        Ok((spec, ty, _)) => {
                            let id = build_raw_id_path(spec, ty);
                            entry_id == &id
                        }
                        Err(e) => {
                            e.entry().raw_id() == entry_id
                        }
                    });
                    let Some(index) = index else {
                        return Err(Error::Keystore(Arc::new(MockKeystoreError::NotFound)));
                    };
                    let _ = self.inner.write().unwrap().remove(index);
                    Ok(())
                }
                fn list(&self) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
                    Ok(self
                        .inner
                        .read()
                        .unwrap()
                        .iter()
                        .map(|res| match res {
                            Ok((arti_path, ty, _)) => {
                                let raw_id = RawEntryId::Path(
                                    PathBuf::from(
                                        &arti_path.to_string(),
                                    )
                                );
                                Ok(KeystoreEntry::new(KeyPath::Arti(arti_path.clone()), ty.clone(), self.id(), raw_id))
                            }
                            Err(e) => Err(e.clone()),
                        })
                        .collect())
                }
            }
        };
    }
    macro_rules! impl_specifier {
        ($name:tt, $id:expr) => {
            struct $name;
            impl KeySpecifier for $name {
                fn arti_path(&self) -> StdResult<ArtiPath, ArtiPathUnavailableError> {
                    Ok(ArtiPath::new($id.into()).map_err(|e| tor_error::internal!("{e}"))?)
                }
                fn ctor_path(&self) -> Option<crate::CTorPath> {
                    None
                }
                fn keypair_specifier(&self) -> Option<Box<dyn KeySpecifier>> {
                    None
                }
            }
        };
    }
    impl_keystore!(Keystore1, "keystore1");
    impl_keystore!(Keystore2, "keystore2");
    impl_keystore!(Keystore3, "keystore3");
    impl_keystore!(KeystoreUnrec1, "keystore_unrec1", 1);
    impl_specifier!(TestKeySpecifier1, "spec1");
    impl_specifier!(TestKeySpecifier2, "spec2");
    impl_specifier!(TestKeySpecifier3, "spec3");
    impl_specifier!(TestKeySpecifier4, "spec4");
    impl_specifier!(TestPublicKeySpecifier1, "pub-spec1");
    /// Create a test `KeystoreEntry`.
    fn entry_descriptor(specifier: impl KeySpecifier, keystore_id: &KeystoreId) -> KeystoreEntry {
        let arti_path = specifier.arti_path().unwrap();
        let raw_id = RawEntryId::Path(PathBuf::from(arti_path.as_ref()));
        KeystoreEntry {
            key_path: arti_path.into(),
            key_type: TestItem::item_type(),
            keystore_id,
            raw_id,
        }
    }
    #[test]
    #[allow(clippy::cognitive_complexity)]
    fn insert_and_get() {
        let mut builder = KeyMgrBuilder::default().primary_store(Box::<Keystore1>::default());
        builder
            .secondary_stores()
            .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
        let mgr = builder.build().unwrap();
        // Insert a key into Keystore2
        let old_key = mgr
            .insert(
                TestItem::new("coot"),
                &TestKeySpecifier1,
                KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
                true,
            )
            .unwrap();
        assert!(old_key.is_none());
        let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
        assert_eq!(key.meta.item_id(), "coot");
        assert_eq!(
            key.meta.retrieved_from(),
            Some(&KeystoreId::from_str("keystore2").unwrap())
        );
        assert_eq!(key.meta.is_generated(), false);
        // Insert a different key using the _same_ key specifier.
        let old_key = mgr
            .insert(
                TestItem::new("gull"),
                &TestKeySpecifier1,
                KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
                true,
            )
            .unwrap()
            .unwrap();
        assert_eq!(old_key.meta.item_id(), "coot");
        assert_eq!(
            old_key.meta.retrieved_from(),
            Some(&KeystoreId::from_str("keystore2").unwrap())
        );
        assert_eq!(old_key.meta.is_generated(), false);
        // Check that the original value was overwritten:
        let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
        assert_eq!(key.meta.item_id(), "gull");
        assert_eq!(
            key.meta.retrieved_from(),
            Some(&KeystoreId::from_str("keystore2").unwrap())
        );
        assert_eq!(key.meta.is_generated(), false);
        // Insert a different key using the _same_ key specifier (overwrite = false)
        let err = mgr
            .insert(
                TestItem::new("gull"),
                &TestKeySpecifier1,
                KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
                false,
            )
            .unwrap_err();
        assert!(matches!(err, crate::Error::KeyAlreadyExists));
        // Insert a new key into Keystore2 (overwrite = false)
        let old_key = mgr
            .insert(
                TestItem::new("penguin"),
                &TestKeySpecifier2,
                KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
                false,
            )
            .unwrap();
        assert!(old_key.is_none());
        // Insert a key into the primary keystore
        let old_key = mgr
            .insert(
                TestItem::new("moorhen"),
                &TestKeySpecifier3,
                KeystoreSelector::Primary,
                true,
            )
            .unwrap();
        assert!(old_key.is_none());
        let key = mgr.get::<TestItem>(&TestKeySpecifier3).unwrap().unwrap();
        assert_eq!(key.meta.item_id(), "moorhen");
        assert_eq!(
            key.meta.retrieved_from(),
            Some(&KeystoreId::from_str("keystore1").unwrap())
        );
        assert_eq!(key.meta.is_generated(), false);
        // The key doesn't exist in any of the stores yet.
        assert!(mgr.get::<TestItem>(&TestKeySpecifier4).unwrap().is_none());
        // Insert the same key into all 3 key stores, in reverse order of keystore priority
        // (otherwise KeyMgr::get will return the key from the primary store for each iteration and
        // we won't be able to see the key was actually inserted in each store).
        for store in ["keystore3", "keystore2", "keystore1"] {
            let old_key = mgr
                .insert(
                    TestItem::new("cormorant"),
                    &TestKeySpecifier4,
                    KeystoreSelector::Id(&KeystoreId::from_str(store).unwrap()),
                    true,
                )
                .unwrap();
            assert!(old_key.is_none());
            // Ensure the key now exists in `store`.
            let key = mgr.get::<TestItem>(&TestKeySpecifier4).unwrap().unwrap();
            assert_eq!(key.meta.item_id(), "cormorant");
            assert_eq!(
                key.meta.retrieved_from(),
                Some(&KeystoreId::from_str(store).unwrap())
            );
            assert_eq!(key.meta.is_generated(), false);
        }
        // The key exists in all key stores, but if no keystore_id is specified, we return the
        // value from the first key store it is found in (in this case, Keystore1)
        let key = mgr.get::<TestItem>(&TestKeySpecifier4).unwrap().unwrap();
        assert_eq!(key.meta.item_id(), "cormorant");
        assert_eq!(
            key.meta.retrieved_from(),
            Some(&KeystoreId::from_str("keystore1").unwrap())
        );
        assert_eq!(key.meta.is_generated(), false);
    }
    #[test]
    fn remove() {
        let mut builder = KeyMgrBuilder::default().primary_store(Box::<Keystore1>::default());
        builder
            .secondary_stores()
            .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
        let mgr = builder.build().unwrap();
        assert!(!mgr.secondary_stores[0]
            .contains(&TestKeySpecifier1, &TestItem::item_type())
            .unwrap());
        // Insert a key into Keystore2
        mgr.insert(
            TestItem::new("coot"),
            &TestKeySpecifier1,
            KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
            true,
        )
        .unwrap();
        let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
        assert_eq!(key.meta.item_id(), "coot");
        assert_eq!(
            key.meta.retrieved_from(),
            Some(&KeystoreId::from_str("keystore2").unwrap())
        );
        assert_eq!(key.meta.is_generated(), false);
        // Try to remove the key from a non-existent key store
        assert!(mgr
            .remove::<TestItem>(
                &TestKeySpecifier1,
                KeystoreSelector::Id(&KeystoreId::from_str("not_an_id_we_know_of").unwrap())
            )
            .is_err());
        // The key still exists in Keystore2
        assert!(mgr.secondary_stores[0]
            .contains(&TestKeySpecifier1, &TestItem::item_type())
            .unwrap());
        // Try to remove the key from the primary key store
        assert!(mgr
            .remove::<TestItem>(&TestKeySpecifier1, KeystoreSelector::Primary)
            .unwrap()
            .is_none());
        // The key still exists in Keystore2
        assert!(mgr.secondary_stores[0]
            .contains(&TestKeySpecifier1, &TestItem::item_type())
            .unwrap());
        // Removing from Keystore2 should succeed.
        let removed_key = mgr
            .remove::<TestItem>(
                &TestKeySpecifier1,
                KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
            )
            .unwrap()
            .unwrap();
        assert_eq!(removed_key.meta.item_id(), "coot");
        assert_eq!(
            removed_key.meta.retrieved_from(),
            Some(&KeystoreId::from_str("keystore2").unwrap())
        );
        assert_eq!(removed_key.meta.is_generated(), false);
        // The key doesn't exist in Keystore2 anymore
        assert!(!mgr.secondary_stores[0]
            .contains(&TestKeySpecifier1, &TestItem::item_type())
            .unwrap());
    }
    #[test]
    fn keygen() {
        let mut rng = FakeEntropicRng(testing_rng());
        let mgr = KeyMgrBuilder::default()
            .primary_store(Box::<Keystore1>::default())
            .build()
            .unwrap();
        mgr.insert(
            TestItem::new("coot"),
            &TestKeySpecifier1,
            KeystoreSelector::Primary,
            true,
        )
        .unwrap();
        // There is no corresponding public key entry.
        assert!(mgr
            .get::<TestPublicKey>(&TestPublicKeySpecifier1)
            .unwrap()
            .is_none());
        // Try to generate a new key (overwrite = false)
        let err = mgr
            .generate::<TestItem>(
                &TestKeySpecifier1,
                KeystoreSelector::Primary,
                &mut rng,
                false,
            )
            .unwrap_err();
        assert!(matches!(err, crate::Error::KeyAlreadyExists));
        // The previous entry was not overwritten because overwrite = false
        let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
        assert_eq!(key.meta.item_id(), "coot");
        assert_eq!(
            key.meta.retrieved_from(),
            Some(&KeystoreId::from_str("keystore1").unwrap())
        );
        assert_eq!(key.meta.is_generated(), false);
        // We don't store public keys in the keystore
        assert!(mgr
            .get::<TestPublicKey>(&TestPublicKeySpecifier1)
            .unwrap()
            .is_none());
        // Try to generate a new key (overwrite = true)
        let generated_key = mgr
            .generate::<TestItem>(
                &TestKeySpecifier1,
                KeystoreSelector::Primary,
                &mut rng,
                true,
            )
            .unwrap();
        assert_eq!(generated_key.meta.item_id(), "generated_test_key");
        // Not set in a freshly generated key, because KeyMgr::generate()
        // returns it straight away, without going through Keystore::get()
        assert_eq!(generated_key.meta.retrieved_from(), None);
        assert_eq!(generated_key.meta.is_generated(), true);
        // Retrieve the inserted key
        let retrieved_key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
        assert_eq!(retrieved_key.meta.item_id(), "generated_test_key");
        assert_eq!(
            retrieved_key.meta.retrieved_from(),
            Some(&KeystoreId::from_str("keystore1").unwrap())
        );
        assert_eq!(retrieved_key.meta.is_generated(), true);
        // We don't store public keys in the keystore
        assert!(mgr
            .get::<TestPublicKey>(&TestPublicKeySpecifier1)
            .unwrap()
            .is_none());
    }
    #[test]
    fn get_or_generate() {
        let mut rng = FakeEntropicRng(testing_rng());
        let mut builder = KeyMgrBuilder::default().primary_store(Box::<Keystore1>::default());
        builder
            .secondary_stores()
            .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
        let mgr = builder.build().unwrap();
        let keystore2 = KeystoreId::from_str("keystore2").unwrap();
        let entry_desc1 = entry_descriptor(TestKeySpecifier1, &keystore2);
        assert!(mgr.get_entry::<TestItem>(&entry_desc1).unwrap().is_none());
        mgr.insert(
            TestItem::new("coot"),
            &TestKeySpecifier1,
            KeystoreSelector::Id(&keystore2),
            true,
        )
        .unwrap();
        // The key already exists in keystore 2 so it won't be auto-generated.
        let key = mgr
            .get_or_generate::<TestItem>(&TestKeySpecifier1, KeystoreSelector::Primary, &mut rng)
            .unwrap();
        assert_eq!(key.meta.item_id(), "coot");
        assert_eq!(
            key.meta.retrieved_from(),
            Some(&KeystoreId::from_str("keystore2").unwrap())
        );
        assert_eq!(key.meta.is_generated(), false);
        assert_eq!(
            mgr.get_entry::<TestItem>(&entry_desc1)
                .unwrap()
                .map(|k| k.meta),
            Some(ItemMetadata::Key(KeyMetadata {
                item_id: "coot".to_string(),
                retrieved_from: Some(keystore2.clone()),
                is_generated: false,
            }))
        );
        // This key doesn't exist in any of the keystores, so it will be auto-generated and
        // inserted into keystore 3.
        let keystore3 = KeystoreId::from_str("keystore3").unwrap();
        let generated_key = mgr
            .get_or_generate::<TestItem>(
                &TestKeySpecifier2,
                KeystoreSelector::Id(&keystore3),
                &mut rng,
            )
            .unwrap();
        assert_eq!(generated_key.meta.item_id(), "generated_test_key");
        // Not set in a freshly generated key, because KeyMgr::get_or_generate()
        // returns it straight away, without going through Keystore::get()
        assert_eq!(generated_key.meta.retrieved_from(), None);
        assert_eq!(generated_key.meta.is_generated(), true);
        // Retrieve the inserted key
        let retrieved_key = mgr.get::<TestItem>(&TestKeySpecifier2).unwrap().unwrap();
        assert_eq!(retrieved_key.meta.item_id(), "generated_test_key");
        assert_eq!(
            retrieved_key.meta.retrieved_from(),
            Some(&KeystoreId::from_str("keystore3").unwrap())
        );
        assert_eq!(retrieved_key.meta.is_generated(), true);
        let entry_desc2 = entry_descriptor(TestKeySpecifier2, &keystore3);
        assert_eq!(
            mgr.get_entry::<TestItem>(&entry_desc2)
                .unwrap()
                .map(|k| k.meta),
            Some(ItemMetadata::Key(KeyMetadata {
                item_id: "generated_test_key".to_string(),
                retrieved_from: Some(keystore3.clone()),
                is_generated: true,
            }))
        );
        let arti_pat = KeyPathPattern::Arti("*".to_string());
        let matching = mgr.list_matching(&arti_pat).unwrap();
        assert_eq!(matching.len(), 2);
        assert!(matching.contains(&entry_desc1));
        assert!(matching.contains(&entry_desc2));
        assert_eq!(mgr.remove_entry(&entry_desc2).unwrap(), Some(()));
        assert!(mgr.get_entry::<TestItem>(&entry_desc2).unwrap().is_none());
        assert!(mgr.remove_entry(&entry_desc2).unwrap().is_none());
    }
    #[test]
    fn list_matching_ignores_unrecognized_keys() {
        let builder = KeyMgrBuilder::default().primary_store(Box::new(KeystoreUnrec1::default()));
        let mgr = builder.build().unwrap();
        let unrec_1 = KeystoreId::from_str("keystore_unrec1").unwrap();
        mgr.insert(
            TestItem::new("whale shark"),
            &TestKeySpecifier1,
            KeystoreSelector::Id(&unrec_1),
            true,
        )
        .unwrap();
        let arti_pat = KeyPathPattern::Arti("*".to_string());
        let valid_key_path = KeyPath::Arti(TestKeySpecifier1.arti_path().unwrap());
        let matching = mgr.list_matching(&arti_pat).unwrap();
        // assert the unrecognized key has been filtered out
        assert_eq!(matching.len(), 1);
        assert_eq!(matching.first().unwrap().key_path(), &valid_key_path);
    }
    #[cfg(feature = "onion-service-cli-extra")]
    #[test]
    /// Test all `arti keys` subcommands
    // TODO: split this in different tests
    fn keys_subcommands() {
        let mut builder =
            KeyMgrBuilder::default().primary_store(Box::new(KeystoreUnrec1::default()));
        builder
            .secondary_stores()
            .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
        let mgr = builder.build().unwrap();
        let ks_unrec1id = KeystoreId::from_str("keystore_unrec1").unwrap();
        let keystore2id = KeystoreId::from_str("keystore2").unwrap();
        let keystore3id = KeystoreId::from_str("keystore3").unwrap();
        // Insert a key into KeystoreUnrec1
        let _ = mgr
            .insert(
                TestItem::new("pangolin"),
                &TestKeySpecifier1,
                KeystoreSelector::Id(&ks_unrec1id),
                true,
            )
            .unwrap();
        // Insert a key into Keystore2
        let _ = mgr
            .insert(
                TestItem::new("coot"),
                &TestKeySpecifier2,
                KeystoreSelector::Id(&keystore2id),
                true,
            )
            .unwrap();
        // Insert a key into Keystore3
        let _ = mgr
            .insert(
                TestItem::new("penguin"),
                &TestKeySpecifier3,
                KeystoreSelector::Id(&keystore3id),
                true,
            )
            .unwrap();
        let assert_key = |path, ty, expected_path: &ArtiPath, expected_type| {
            assert_eq!(ty, expected_type);
            assert_eq!(path, &KeyPath::Arti(expected_path.clone()));
        };
        let item_type = TestItem::new("axolotl").item.item_type().unwrap();
        let unrecognized_entry_id = RawEntryId::Path(PathBuf::from("unrecognized_entry0"));
        // Test `list`
        let entries = mgr.list().unwrap();
        let expected_items = [
            (ks_unrec1id, TestKeySpecifier1.arti_path().unwrap()),
            (keystore2id, TestKeySpecifier2.arti_path().unwrap()),
            (keystore3id, TestKeySpecifier3.arti_path().unwrap()),
        ];
        // Secondary keystores contain 1 valid key each
        let mut recognized_entries = 0;
        let mut unrecognized_entries = 0;
        for entry in entries.iter() {
            match entry {
                Ok(e) => {
                    if let Some((_, expected_arti_path)) = expected_items
                        .iter()
                        .find(|(keystore_id, _)| keystore_id == e.keystore_id())
                    {
                        assert_key(e.key_path(), e.key_type(), expected_arti_path, &item_type);
                        recognized_entries += 1;
                        continue;
                    }
                    panic!("Unexpected key encountered {:?}", e);
                }
                Err(u) => {
                    assert_eq!(u.entry().raw_id(), &unrecognized_entry_id);
                    unrecognized_entries += 1;
                }
            }
        }
        assert_eq!(recognized_entries, 3);
        assert_eq!(unrecognized_entries, 1);
        // Test `list_keystores`
        let keystores = mgr.list_keystores().iter().len();
        assert_eq!(keystores, 3);
        // Test `list_by_id`
        let primary_keystore_id = KeystoreId::from_str("keystore_unrec1").unwrap();
        let entries = mgr.list_by_id(&primary_keystore_id).unwrap();
        // Primary keystore contains a valid key and an unrecognized key
        let mut recognized_entries = 0;
        let mut unrecognized_entries = 0;
        // A list of entries, in a form that can be consumed by remove_unchecked
        let mut all_entries = vec![];
        for entry in entries.iter() {
            match entry {
                Ok(entry) => {
                    assert_key(
                        entry.key_path(),
                        entry.key_type(),
                        &TestKeySpecifier1.arti_path().unwrap(),
                        &item_type,
                    );
                    recognized_entries += 1;
                    all_entries.push(RawKeystoreEntry::new(
                        build_raw_id_path(entry.key_path(), entry.key_type()),
                        primary_keystore_id.clone(),
                    ));
                }
                Err(u) => {
                    assert_eq!(u.entry().raw_id(), &unrecognized_entry_id);
                    unrecognized_entries += 1;
                    all_entries.push(u.entry().into());
                }
            }
        }
        assert_eq!(recognized_entries, 1);
        assert_eq!(unrecognized_entries, 1);
        // Remove a recognized entry and an recognized one
        for entry in all_entries {
            mgr.remove_unchecked(&entry.raw_id().to_string(), entry.keystore_id())
                .unwrap();
        }
        // Check the keys have been removed
        let entries = mgr.list_by_id(&primary_keystore_id).unwrap();
        assert_eq!(entries.len(), 0);
    }
    /// Whether to generate a given item before running the `run_certificate_test`.
    #[cfg(feature = "experimental-api")]
    #[derive(Clone, Copy, Debug, PartialEq)]
    enum GenerateItem {
        Yes,
        No,
    }
    #[cfg(feature = "experimental-api")]
    macro_rules! run_certificate_test {
        (
            generate_subject_key = $generate_subject_key:expr,
            generate_signing_key = $generate_signing_key:expr,
            $($expected_err:tt)?
        ) => {{
            use GenerateItem::*;
            let mut rng = FakeEntropicRng(testing_rng());
            let mut builder = KeyMgrBuilder::default().primary_store(Box::<Keystore1>::default());
            builder
                .secondary_stores()
                .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
            let mgr = builder.build().unwrap();
            let spec = crate::test_utils::TestCertSpecifier {
                subject_key_spec: TestKeySpecifier1,
                signing_key_spec: TestKeySpecifier2,
                denotator: vec!["foo".into()],
            };
            if $generate_subject_key == Yes {
                let _ = mgr
                    .generate::<TestItem>(
                        &TestKeySpecifier1,
                        KeystoreSelector::Primary,
                        &mut rng,
                        false,
                    )
                    .unwrap();
            }
            if $generate_signing_key == Yes {
                let _ = mgr
                    .generate::<TestItem>(
                        &TestKeySpecifier2,
                        KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
                        &mut rng,
                        false,
                    )
                    .unwrap();
            }
            let make_certificate = move |subject_key: &TestItem, signed_with: &TestItem| {
                let subject_id = subject_key.meta.as_key().unwrap().item_id.clone();
                let signing_id = signed_with.meta.as_key().unwrap().item_id.clone();
                let meta = ItemMetadata::Cert(CertMetadata {
                    subject_key_id: subject_id,
                    signing_key_id: signing_id,
                    retrieved_from: None,
                    is_generated: true,
                });
                // Note: this is not really a cert for `subject_key` signed with the `signed_with`
                // key!. The two are `TestItem`s and not keys, so we can't really generate a real
                // cert from them. We can, however, pretend we did, for testing purposes.
                // Eventually we might want to rewrite these tests to use real items
                // (like the `ArtiNativeKeystore` tests)
                let mut rng = FakeEntropicRng(testing_rng());
                let keypair = ed25519::Keypair::generate(&mut rng);
                let encoded_cert = Ed25519Cert::constructor()
                    .cert_type(tor_cert::CertType::IDENTITY_V_SIGNING)
                    .expiration(SystemTime::now() + Duration::from_secs(180))
                    .signing_key(keypair.public_key().into())
                    .cert_key(CertifiedKey::Ed25519(keypair.public_key().into()))
                    .encode_and_sign(&keypair)
                    .unwrap();
                let test_cert = CertData::TorEd25519Cert(encoded_cert);
                AlwaysValidCert(TestItem {
                    item: KeystoreItem::Cert(test_cert),
                    meta,
                })
            };
            let res = mgr
                .get_or_generate_key_and_cert::<TestItem, AlwaysValidCert>(
                    &spec,
                    &make_certificate,
                    KeystoreSelector::Primary,
                    &mut rng,
                );
            #[allow(unused_assignments)]
            #[allow(unused_mut)]
            let mut has_error = false;
            $(
                has_error = true;
                let err = res.clone().unwrap_err();
                assert!(
                    matches!(
                        err,
                        crate::Error::Corruption(KeystoreCorruptionError::$expected_err)
                    ),
                    "unexpected error: {err:?}",
                );
            )?
            if !has_error {
                let (key, cert) = res.unwrap();
                let expected_subj_key_id = if $generate_subject_key == Yes {
                    "generated_test_key"
                } else {
                    "generated_test_key"
                };
                assert_eq!(key.meta.item_id(), expected_subj_key_id);
                assert_eq!(
                    cert.0.meta.as_cert().unwrap().subject_key_id,
                    expected_subj_key_id
                );
                assert_eq!(
                    cert.0.meta.as_cert().unwrap().signing_key_id,
                    "generated_test_key"
                );
                assert_eq!(cert.0.meta.is_generated(), true);
            }
        }}
    }
    #[test]
    #[cfg(feature = "experimental-api")]
    #[rustfmt::skip] // preserve the layout for readability
    #[allow(clippy::cognitive_complexity)] // clippy seems confused here...
    fn get_certificate() {
        run_certificate_test!(
            generate_subject_key = No,
            generate_signing_key = No,
            MissingSigningKey
        );
        run_certificate_test!(
            generate_subject_key = Yes,
            generate_signing_key = No,
            MissingSigningKey
        );
        run_certificate_test!(
            generate_subject_key = No,
            generate_signing_key = Yes,
        );
        run_certificate_test!(
            generate_subject_key = Yes,
            generate_signing_key = Yes,
        );
    }
}