1use crate::raw::RawEntryId;
6use crate::{
7 ArtiPath, BoxedKeystore, KeyPath, KeyPathError, KeyPathInfo, KeyPathInfoExtractor,
8 KeyPathPattern, KeySpecifier, KeystoreCorruptionError, KeystoreEntryResult, KeystoreId,
9 KeystoreSelector, Result,
10};
11
12use itertools::Itertools;
13use std::iter;
14use std::result::Result as StdResult;
15use tor_error::{bad_api_usage, internal, into_bad_api_usage};
16use tor_key_forge::{
17 ItemType, Keygen, KeygenRng, KeystoreItemType, ToEncodableCert, ToEncodableKey,
18};
19
20#[cfg(feature = "experimental-api")]
21use crate::KeyCertificateSpecifier;
22
23#[derive(derive_builder::Builder)]
47#[builder(pattern = "owned", build_fn(private, name = "build_unvalidated"))]
48pub struct KeyMgr {
49 primary_store: BoxedKeystore,
51 #[builder(default, setter(custom))]
53 secondary_stores: Vec<BoxedKeystore>,
54 #[builder(default, setter(skip))]
59 key_info_extractors: Vec<&'static dyn KeyPathInfoExtractor>,
60}
61
62#[derive(Clone, Debug, PartialEq, amplify::Getters)]
70pub struct KeystoreEntry<'a> {
71 key_path: KeyPath,
73 key_type: KeystoreItemType,
75 #[getter(as_copy)]
77 keystore_id: &'a KeystoreId,
78 #[cfg_attr(not(feature = "onion-service-cli-extra"), getter(skip))]
81 raw_id: RawEntryId,
82}
83
84impl<'a> KeystoreEntry<'a> {
85 #[cfg_attr(feature = "experimental-api", visibility::make(pub))]
87 pub(crate) fn new(
88 key_path: KeyPath,
89 key_type: KeystoreItemType,
90 keystore_id: &'a KeystoreId,
91 raw_id: RawEntryId,
92 ) -> Self {
93 Self {
94 key_path,
95 key_type,
96 keystore_id,
97 raw_id,
98 }
99 }
100}
101
102impl KeyMgrBuilder {
103 pub fn build(self) -> StdResult<KeyMgr, KeyMgrBuilderError> {
105 use itertools::Itertools as _;
106
107 let mut keymgr = self.build_unvalidated()?;
108
109 if !keymgr.all_stores().map(|s| s.id()).all_unique() {
110 return Err(KeyMgrBuilderError::ValidationError(
111 "the keystore IDs are not pairwise unique".into(),
112 ));
113 }
114
115 keymgr.key_info_extractors = inventory::iter::<&'static dyn KeyPathInfoExtractor>
116 .into_iter()
117 .copied()
118 .collect();
119
120 Ok(keymgr)
121 }
122}
123
124impl KeyMgrBuilder {
129 pub fn secondary_stores(&mut self) -> &mut Vec<BoxedKeystore> {
135 self.secondary_stores.get_or_insert(Default::default())
136 }
137
138 pub fn set_secondary_stores(mut self, list: Vec<BoxedKeystore>) -> Self {
140 self.secondary_stores = Some(list);
141 self
142 }
143
144 pub fn opt_secondary_stores(&self) -> &Option<Vec<BoxedKeystore>> {
148 &self.secondary_stores
149 }
150
151 pub fn opt_secondary_stores_mut(&mut self) -> &mut Option<Vec<BoxedKeystore>> {
155 &mut self.secondary_stores
156 }
157}
158
159inventory::collect!(&'static dyn crate::KeyPathInfoExtractor);
160
161impl KeyMgr {
162 pub fn get<K: ToEncodableKey>(&self, key_spec: &dyn KeySpecifier) -> Result<Option<K>> {
169 self.get_from_store(key_spec, &K::Key::item_type(), self.all_stores())
170 }
171
172 pub fn get_entry<K: ToEncodableKey>(&self, entry: &KeystoreEntry) -> Result<Option<K>> {
180 let selector = entry.keystore_id().into();
181 let store = self.select_keystore(&selector)?;
182 self.get_from_store(entry.key_path(), entry.key_type(), [store].into_iter())
183 }
184
185 #[cfg(feature = "experimental-api")]
200 pub fn get_cert_entry<
201 S: KeyCertificateSpecifier + for<'a> TryFrom<&'a KeyPath, Error = KeyPathError>,
202 K: ToEncodableKey,
203 C: ToEncodableCert<K>,
204 >(
205 &self,
206 entry: &KeystoreEntry,
207 signing_key_spec: &dyn KeySpecifier,
208 ) -> Result<Option<C>> {
209 let selector = entry.keystore_id().into();
210 let store = self.select_keystore(&selector)?;
211 let cert_spec = S::try_from(entry.key_path())
212 .map_err(into_bad_api_usage!("wrong cert specifier for entry?!"))?;
213 let subject_key_spec = cert_spec.subject_key_specifier();
214
215 self.get_cert_from_store(
216 entry.key_path(),
217 entry.key_type(),
218 signing_key_spec,
219 subject_key_spec,
220 [store].into_iter(),
221 )
222 }
223
224 pub fn get_or_generate<K>(
236 &self,
237 key_spec: &dyn KeySpecifier,
238 selector: KeystoreSelector,
239 rng: &mut dyn KeygenRng,
240 ) -> Result<K>
241 where
242 K: ToEncodableKey,
243 K::Key: Keygen,
244 {
245 match self.get(key_spec)? {
246 Some(k) => Ok(k),
247 None => self.generate(key_spec, selector, rng, false),
248 }
249 }
250
251 #[cfg(feature = "onion-service-cli-extra")]
261 pub fn get_from<K: ToEncodableKey>(
262 &self,
263 key_spec: &dyn KeySpecifier,
264 keystore_id: &KeystoreId,
265 ) -> Result<Option<K>> {
266 let store = std::iter::once(self.find_keystore(keystore_id)?);
267 self.get_from_store(key_spec, &K::Key::item_type(), store)
268 }
269
270 #[cfg(feature = "onion-service-cli-extra")]
280 pub fn validate_entry_integrity(&self, entry: &KeystoreEntry) -> Result<()> {
281 let selector = entry.keystore_id().into();
282 let store = self.select_keystore(&selector)?;
283 let _ = store.get(entry.key_path(), entry.key_type())?;
285
286 let path = entry.key_path();
287 let _ = self
289 .describe(path)
290 .ok_or_else(|| KeystoreCorruptionError::Unrecognized(path.clone()))?;
291
292 Ok(())
293 }
294
295 pub fn generate<K>(
316 &self,
317 key_spec: &dyn KeySpecifier,
318 selector: KeystoreSelector,
319 rng: &mut dyn KeygenRng,
320 overwrite: bool,
321 ) -> Result<K>
322 where
323 K: ToEncodableKey,
324 K::Key: Keygen,
325 {
326 let store = self.select_keystore(&selector)?;
327
328 if overwrite || !store.contains(key_spec, &K::Key::item_type())? {
329 let key = K::Key::generate(rng)?;
330 store.insert(&key, key_spec)?;
331
332 Ok(K::from_encodable_key(key))
333 } else {
334 Err(crate::Error::KeyAlreadyExists)
335 }
336 }
337
338 pub fn insert<K: ToEncodableKey>(
351 &self,
352 key: K,
353 key_spec: &dyn KeySpecifier,
354 selector: KeystoreSelector,
355 overwrite: bool,
356 ) -> Result<Option<K>> {
357 let key = key.to_encodable_key();
358 let store = self.select_keystore(&selector)?;
359 let key_type = K::Key::item_type();
360 let old_key: Option<K> = self.get_from_store(key_spec, &key_type, [store].into_iter())?;
361
362 if old_key.is_some() && !overwrite {
363 Err(crate::Error::KeyAlreadyExists)
364 } else {
365 let () = store.insert(&key, key_spec)?;
366 Ok(old_key)
367 }
368 }
369
370 pub fn remove<K: ToEncodableKey>(
381 &self,
382 key_spec: &dyn KeySpecifier,
383 selector: KeystoreSelector,
384 ) -> Result<Option<K>> {
385 let store = self.select_keystore(&selector)?;
386 let key_type = K::Key::item_type();
387 let old_key: Option<K> = self.get_from_store(key_spec, &key_type, [store].into_iter())?;
388
389 store.remove(key_spec, &key_type)?;
390
391 Ok(old_key)
392 }
393
394 pub fn remove_entry(&self, entry: &KeystoreEntry) -> Result<Option<()>> {
406 let selector = entry.keystore_id().into();
407 let store = self.select_keystore(&selector)?;
408
409 store.remove(entry.key_path(), entry.key_type())
410 }
411
412 #[cfg(feature = "onion-service-cli-extra")]
420 pub fn remove_unchecked(&self, raw_id: &str, keystore_id: &KeystoreId) -> Result<()> {
421 let selector = KeystoreSelector::from(keystore_id);
422 let store = self.select_keystore(&selector)?;
423 let raw_id = store.raw_entry_id(raw_id)?;
424 let store = self.select_keystore(&selector)?;
425 store.remove_unchecked(&raw_id)
426 }
427
428 pub fn list_matching(&self, pat: &KeyPathPattern) -> Result<Vec<KeystoreEntry>> {
436 self.all_stores()
437 .map(|store| -> Result<Vec<_>> {
438 Ok(store
439 .list()?
440 .into_iter()
441 .filter_map(|entry| entry.ok())
442 .filter(|entry| entry.key_path().matches(pat))
443 .collect::<Vec<_>>())
444 })
445 .flatten_ok()
446 .collect::<Result<Vec<_>>>()
447 }
448
449 #[cfg(feature = "onion-service-cli-extra")]
451 pub fn list_by_id(&self, id: &KeystoreId) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
452 self.find_keystore(id)?.list()
453 }
454
455 #[cfg(feature = "onion-service-cli-extra")]
457 pub fn list(&self) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
458 self.all_stores()
459 .map(|store| -> Result<Vec<_>> { store.list() })
460 .flatten_ok()
461 .collect::<Result<Vec<_>>>()
462 }
463
464 #[cfg(feature = "onion-service-cli-extra")]
466 pub fn list_keystores(&self) -> Vec<KeystoreId> {
467 self.all_stores()
468 .map(|store| store.id().to_owned())
469 .collect()
470 }
471
472 pub fn describe(&self, path: &KeyPath) -> Option<KeyPathInfo> {
481 for info_extractor in &self.key_info_extractors {
482 if let Ok(info) = info_extractor.describe(path) {
483 return Some(info);
484 }
485 }
486
487 None
488 }
489
490 fn get_from_store_raw<'a, K: ItemType>(
496 &self,
497 key_spec: &dyn KeySpecifier,
498 key_type: &KeystoreItemType,
499 stores: impl Iterator<Item = &'a BoxedKeystore>,
500 ) -> Result<Option<K>> {
501 let static_key_type = K::item_type();
502 if key_type != &static_key_type {
503 return Err(internal!(
504 "key type {:?} does not match the key type {:?} of requested key K::Key",
505 key_type,
506 static_key_type
507 )
508 .into());
509 }
510
511 for store in stores {
512 let key = match store.get(key_spec, &K::item_type()) {
513 Ok(None) => {
514 continue;
516 }
517 Ok(Some(k)) => k,
518 Err(e) => {
519 return Err(e);
521 }
522 };
523
524 let key: K = key
526 .downcast::<K>()
527 .map(|k| *k)
528 .map_err(|_| internal!("failed to downcast key to requested type"))?;
529
530 return Ok(Some(key));
531 }
532
533 Ok(None)
534 }
535
536 #[cfg(feature = "experimental-api")]
538 fn get_cert_from_store<'a, K: ToEncodableKey, C: ToEncodableCert<K>>(
539 &self,
540 cert_spec: &dyn KeySpecifier,
541 cert_type: &KeystoreItemType,
542 signing_cert_spec: &dyn KeySpecifier,
543 subject_cert_spec: &dyn KeySpecifier,
544 stores: impl Iterator<Item = &'a BoxedKeystore>,
545 ) -> Result<Option<C>> {
546 let Some(cert) = self.get_from_store_raw::<C::ParsedCert>(cert_spec, cert_type, stores)?
547 else {
548 return Ok(None);
549 };
550
551 let Some(subject) =
553 self.get_from_store::<K>(subject_cert_spec, &K::Key::item_type(), self.all_stores())?
554 else {
555 return Ok(None);
556 };
557 let signed_with = self.get_cert_signing_key::<K, C>(signing_cert_spec)?;
558 let cert = C::validate(cert, &subject, &signed_with)?;
559
560 Ok(Some(cert))
561 }
562
563 fn get_from_store<'a, K: ToEncodableKey>(
567 &self,
568 key_spec: &dyn KeySpecifier,
569 key_type: &KeystoreItemType,
570 stores: impl Iterator<Item = &'a BoxedKeystore> + Clone,
571 ) -> Result<Option<K>> {
572 let Some(key) = self.get_from_store_raw::<K::Key>(key_spec, key_type, stores.clone())?
573 else {
574 let Some(key_pair_spec) = key_spec.keypair_specifier() else {
577 return Ok(None);
578 };
579
580 let key_type = <K::KeyPair as ToEncodableKey>::Key::item_type();
581 return Ok(self
582 .get_from_store::<K::KeyPair>(&*key_pair_spec, &key_type, stores)?
583 .map(|k| k.into()));
584 };
585
586 Ok(Some(K::from_encodable_key(key)))
587 }
588
589 #[cfg(feature = "experimental-api")]
605 pub fn get_key_and_cert<K, C>(
606 &self,
607 spec: &dyn KeyCertificateSpecifier,
608 signing_key_spec: &dyn KeySpecifier,
609 ) -> Result<Option<(K, C)>>
610 where
611 K: ToEncodableKey,
612 C: ToEncodableCert<K>,
613 {
614 let subject_key_spec = spec.subject_key_specifier();
615 let Some(key) =
617 self.get_from_store::<K>(subject_key_spec, &K::Key::item_type(), self.all_stores())?
618 else {
619 return Ok(None);
620 };
621
622 let cert_spec = spec
623 .arti_path()
624 .map_err(into_bad_api_usage!("invalid key certificate specifier"))?;
625
626 let Some(cert) = self.get_from_store_raw::<C::ParsedCert>(
627 &cert_spec,
628 &<C::ParsedCert as ItemType>::item_type(),
629 self.all_stores(),
630 )?
631 else {
632 return Err(KeystoreCorruptionError::MissingCertificate.into());
633 };
634
635 let signed_with = self.get_cert_signing_key::<K, C>(signing_key_spec)?;
637 let cert = C::validate(cert, &key, &signed_with)?;
638
639 Ok(Some((key, cert)))
640 }
641
642 #[cfg(feature = "experimental-api")]
678 pub fn get_or_generate_key_and_cert<K, C>(
679 &self,
680 spec: &dyn KeyCertificateSpecifier,
681 signing_key_spec: &dyn KeySpecifier,
682 make_certificate: impl FnOnce(&K, &<C as ToEncodableCert<K>>::SigningKey) -> C,
683 selector: KeystoreSelector,
684 rng: &mut dyn KeygenRng,
685 ) -> Result<(K, C)>
686 where
687 K: ToEncodableKey,
688 K::Key: Keygen,
689 C: ToEncodableCert<K>,
690 {
691 let subject_key_spec = spec.subject_key_specifier();
692 let subject_key_arti_path = subject_key_spec
693 .arti_path()
694 .map_err(|_| bad_api_usage!("subject key does not have an ArtiPath?!"))?;
695
696 let cert_specifier =
697 ArtiPath::from_path_and_denotators(subject_key_arti_path, &spec.cert_denotators())
698 .map_err(into_bad_api_usage!("invalid certificate specifier"))?;
699
700 let maybe_cert = self.get_from_store_raw::<C::ParsedCert>(
701 &cert_specifier,
702 &C::ParsedCert::item_type(),
703 self.all_stores(),
704 )?;
705
706 let maybe_subject_key = self.get::<K>(subject_key_spec)?;
707
708 match (&maybe_cert, &maybe_subject_key) {
709 (Some(_), None) => {
710 return Err(KeystoreCorruptionError::MissingSubjectKey.into());
711 }
712 _ => {
713 }
715 }
716 let subject_key = match maybe_subject_key {
717 Some(key) => key,
718 _ => {
719 let subject_keypair_spec =
720 subject_key_spec.keypair_specifier().ok_or_else(|| {
721 internal!(
722 "KeyCertificateSpecifier has no keypair specifier for the subject key?!"
723 )
724 })?;
725 self.generate(&*subject_keypair_spec, selector, rng, false)?
726 }
727 };
728
729 let signed_with = self.get_cert_signing_key::<K, C>(signing_key_spec)?;
730 let cert = match maybe_cert {
731 Some(cert) => C::validate(cert, &subject_key, &signed_with)?,
732 None => {
733 let cert = make_certificate(&subject_key, &signed_with);
734
735 let () = self.insert_cert(cert.clone(), &cert_specifier, selector)?;
736
737 cert
738 }
739 };
740
741 Ok((subject_key, cert))
742 }
743
744 fn all_stores(&self) -> impl Iterator<Item = &BoxedKeystore> + Clone {
746 iter::once(&self.primary_store).chain(self.secondary_stores.iter())
747 }
748
749 fn select_keystore(&self, selector: &KeystoreSelector) -> Result<&BoxedKeystore> {
754 match selector {
755 KeystoreSelector::Id(keystore_id) => self.find_keystore(keystore_id),
756 KeystoreSelector::Primary => Ok(&self.primary_store),
757 }
758 }
759
760 fn find_keystore(&self, id: &KeystoreId) -> Result<&BoxedKeystore> {
765 self.all_stores()
766 .find(|keystore| keystore.id() == id)
767 .ok_or_else(|| crate::Error::KeystoreNotFound(id.clone()))
768 }
769
770 #[cfg(feature = "experimental-api")]
775 fn get_cert_signing_key<K, C>(
776 &self,
777 signing_key_spec: &dyn KeySpecifier,
778 ) -> Result<C::SigningKey>
779 where
780 K: ToEncodableKey,
781 C: ToEncodableCert<K>,
782 {
783 let Some(signing_key) = self.get_from_store::<C::SigningKey>(
784 signing_key_spec,
785 &<C::SigningKey as ToEncodableKey>::Key::item_type(),
786 self.all_stores(),
787 )?
788 else {
789 return Err(KeystoreCorruptionError::MissingSigningKey.into());
790 };
791
792 Ok(signing_key)
793 }
794
795 fn insert_cert<K, C>(
802 &self,
803 cert: C,
804 cert_spec: &dyn KeySpecifier,
805 selector: KeystoreSelector,
806 ) -> Result<()>
807 where
808 K: ToEncodableKey,
809 K::Key: Keygen,
810 C: ToEncodableCert<K>,
811 {
812 let cert = cert.to_encodable_cert();
813 let store = self.select_keystore(&selector)?;
814
815 let () = store.insert(&cert, cert_spec)?;
816 Ok(())
817 }
818}
819
820#[cfg(test)]
821mod tests {
822 #![allow(clippy::bool_assert_comparison)]
824 #![allow(clippy::clone_on_copy)]
825 #![allow(clippy::dbg_macro)]
826 #![allow(clippy::mixed_attributes_style)]
827 #![allow(clippy::print_stderr)]
828 #![allow(clippy::print_stdout)]
829 #![allow(clippy::single_char_pattern)]
830 #![allow(clippy::unwrap_used)]
831 #![allow(clippy::unchecked_time_subtraction)]
832 #![allow(clippy::useless_vec)]
833 #![allow(clippy::needless_pass_by_value)]
834 #![allow(clippy::string_slice)] use super::*;
837 use crate::keystore::arti::err::{ArtiNativeKeystoreError, MalformedPathError};
838 use crate::raw::RawEntryId;
839 use crate::test_utils::{TestDerivedKeySpecifier, TestDerivedKeypairSpecifier};
840 use crate::{
841 ArtiPath, ArtiPathUnavailableError, Error, KeyPath, KeystoreEntryResult, KeystoreError,
842 UnrecognizedEntry, UnrecognizedEntryError,
843 };
844 use std::path::PathBuf;
845 use std::result::Result as StdResult;
846 use std::str::FromStr;
847 use std::sync::{Arc, RwLock};
848 use tor_basic_utils::test_rng::testing_rng;
849 use tor_cert::CertifiedKey;
850 use tor_cert::Ed25519Cert;
851 use tor_checkable::TimeValidityError;
852 use tor_error::{ErrorKind, HasKind};
853 use tor_key_forge::{
854 CertData, CertType, EncodableItem, EncodedEd25519Cert, ErasedKey, InvalidCertError,
855 KeyType, KeystoreItem,
856 };
857 use tor_llcrypto::pk::ed25519::{self, Ed25519PublicKey as _};
858 use tor_llcrypto::rng::FakeEntropicRng;
859 use web_time_compat::{Duration, SystemTime, SystemTimeExt};
860
861 #[cfg(feature = "experimental-api")]
862 use {
863 crate::CertSpecifierPattern,
864 crate::test_utils::{TestCertSpecifier, TestCertSpecifierPattern},
865 };
866
867 #[derive(Clone, Debug, PartialEq)]
869 struct KeyMetadata {
870 item_id: String,
872 retrieved_from: Option<KeystoreId>,
876 is_generated: bool,
878 }
879
880 #[derive(Clone, Debug, PartialEq)]
882 struct CertMetadata {
883 subject_key_id: String,
885 signing_key_id: String,
887 retrieved_from: Option<KeystoreId>,
891 is_generated: bool,
894 }
895
896 #[derive(Clone, Debug, PartialEq, derive_more::From)]
898 enum ItemMetadata {
899 Key(KeyMetadata),
901 Cert(CertMetadata),
903 }
904
905 impl ItemMetadata {
906 fn item_id(&self) -> &str {
911 match self {
912 ItemMetadata::Key(k) => &k.item_id,
913 ItemMetadata::Cert(c) => &c.subject_key_id,
914 }
915 }
916
917 fn retrieved_from(&self) -> Option<&KeystoreId> {
919 match self {
920 ItemMetadata::Key(k) => k.retrieved_from.as_ref(),
921 ItemMetadata::Cert(c) => c.retrieved_from.as_ref(),
922 }
923 }
924
925 fn is_generated(&self) -> bool {
927 match self {
928 ItemMetadata::Key(k) => k.is_generated,
929 ItemMetadata::Cert(c) => c.is_generated,
930 }
931 }
932
933 fn set_retrieved_from(&mut self, id: KeystoreId) {
935 match self {
936 ItemMetadata::Key(meta) => meta.retrieved_from = Some(id),
937 ItemMetadata::Cert(meta) => meta.retrieved_from = Some(id),
938 }
939 }
940
941 fn as_key(&self) -> Option<&KeyMetadata> {
943 match self {
944 ItemMetadata::Key(meta) => Some(meta),
945 _ => None,
946 }
947 }
948
949 fn as_cert(&self) -> Option<&CertMetadata> {
951 match self {
952 ItemMetadata::Cert(meta) => Some(meta),
953 _ => None,
954 }
955 }
956 }
957
958 #[derive(Clone, Debug)]
960 struct TestItem {
961 item: KeystoreItem,
963 meta: ItemMetadata,
965 }
966
967 struct TestCert(TestItem);
969
970 impl ItemType for TestCert {
971 fn item_type() -> KeystoreItemType
972 where
973 Self: Sized,
974 {
975 CertType::Ed25519TorCert.into()
976 }
977 }
978
979 #[derive(Clone, Debug)]
981 struct AlwaysValidCert(TestItem);
982
983 #[derive(Clone, Debug)]
985 struct AlwaysExpiredCert(TestItem);
986
987 #[derive(Clone, Debug)]
989 struct TestPublicKey {
990 key: KeystoreItem,
992 meta: ItemMetadata,
994 }
995
996 impl From<TestItem> for TestPublicKey {
997 fn from(tk: TestItem) -> TestPublicKey {
998 TestPublicKey {
999 key: tk.item,
1000 meta: tk.meta,
1001 }
1002 }
1003 }
1004
1005 impl TestItem {
1006 fn new(item_id: &str) -> Self {
1008 let mut rng = testing_rng();
1009 TestItem {
1010 item: ed25519::Keypair::generate(&mut rng)
1011 .as_keystore_item()
1012 .unwrap(),
1013 meta: ItemMetadata::Key(KeyMetadata {
1014 item_id: item_id.to_string(),
1015 retrieved_from: None,
1016 is_generated: false,
1017 }),
1018 }
1019 }
1020 }
1021
1022 impl Keygen for TestItem {
1023 fn generate(mut rng: &mut dyn KeygenRng) -> tor_key_forge::Result<Self>
1024 where
1025 Self: Sized,
1026 {
1027 Ok(TestItem {
1028 item: ed25519::Keypair::generate(&mut rng).as_keystore_item()?,
1029 meta: ItemMetadata::Key(KeyMetadata {
1030 item_id: "generated_test_key".to_string(),
1031 retrieved_from: None,
1032 is_generated: true,
1033 }),
1034 })
1035 }
1036 }
1037
1038 impl ItemType for TestItem {
1039 fn item_type() -> KeystoreItemType
1040 where
1041 Self: Sized,
1042 {
1043 KeyType::Ed25519Keypair.into()
1045 }
1046 }
1047
1048 impl EncodableItem for TestItem {
1049 fn as_keystore_item(&self) -> tor_key_forge::Result<KeystoreItem> {
1050 Ok(self.item.clone())
1051 }
1052 }
1053
1054 impl ToEncodableKey for TestItem {
1055 type Key = Self;
1056 type KeyPair = Self;
1057
1058 fn to_encodable_key(self) -> Self::Key {
1059 self
1060 }
1061
1062 fn from_encodable_key(key: Self::Key) -> Self {
1063 key
1064 }
1065 }
1066
1067 impl ItemType for TestPublicKey {
1068 fn item_type() -> KeystoreItemType
1069 where
1070 Self: Sized,
1071 {
1072 KeyType::Ed25519PublicKey.into()
1073 }
1074 }
1075
1076 impl EncodableItem for TestPublicKey {
1077 fn as_keystore_item(&self) -> tor_key_forge::Result<KeystoreItem> {
1078 Ok(self.key.clone())
1079 }
1080 }
1081
1082 impl ToEncodableKey for TestPublicKey {
1083 type Key = Self;
1084 type KeyPair = TestItem;
1085
1086 fn to_encodable_key(self) -> Self::Key {
1087 self
1088 }
1089
1090 fn from_encodable_key(key: Self::Key) -> Self {
1091 key
1092 }
1093 }
1094
1095 impl ToEncodableCert<TestItem> for AlwaysValidCert {
1096 type ParsedCert = TestCert;
1097 type EncodableCert = TestItem;
1098 type SigningKey = TestItem;
1099
1100 fn validate(
1101 cert: Self::ParsedCert,
1102 _subject: &TestItem,
1103 _signed_with: &Self::SigningKey,
1104 ) -> StdResult<Self, InvalidCertError> {
1105 Ok(Self(cert.0))
1107 }
1108
1109 fn to_encodable_cert(self) -> Self::EncodableCert {
1111 self.0
1112 }
1113 }
1114
1115 impl ToEncodableCert<TestItem> for AlwaysExpiredCert {
1116 type ParsedCert = TestCert;
1117 type EncodableCert = TestItem;
1118 type SigningKey = TestItem;
1119
1120 fn validate(
1121 _cert: Self::ParsedCert,
1122 _subject: &TestItem,
1123 _signed_with: &Self::SigningKey,
1124 ) -> StdResult<Self, InvalidCertError> {
1125 Err(InvalidCertError::TimeValidity(TimeValidityError::Expired(
1126 Duration::from_secs(60),
1127 )))
1128 }
1129
1130 fn to_encodable_cert(self) -> Self::EncodableCert {
1132 self.0
1133 }
1134 }
1135
1136 #[derive(thiserror::Error, Debug, Clone, derive_more::Display)]
1137 enum MockKeystoreError {
1138 NotFound,
1139 }
1140
1141 impl KeystoreError for MockKeystoreError {}
1142
1143 impl HasKind for MockKeystoreError {
1144 fn kind(&self) -> ErrorKind {
1145 tor_error::ErrorKind::Other
1147 }
1148 }
1149
1150 fn build_raw_id_path<T: ToString>(key_path: &T, key_type: &KeystoreItemType) -> RawEntryId {
1151 let mut path = key_path.to_string();
1152 path.push('.');
1153 path.push_str(&key_type.arti_extension());
1154 RawEntryId::Path(PathBuf::from(&path))
1155 }
1156
1157 struct Keystore {
1158 inner: RwLock<Vec<KeystoreEntryResult<(ArtiPath, KeystoreItemType, TestItem)>>>,
1159 id: KeystoreId,
1160 }
1161
1162 impl Keystore {
1163 fn new(id: &str) -> Self {
1164 let id = KeystoreId::from_str(id).unwrap();
1165
1166 Self {
1167 inner: Default::default(),
1168 id,
1169 }
1170 }
1171
1172 fn new_boxed(id: &str) -> BoxedKeystore {
1173 Box::new(Self::new(id))
1174 }
1175 }
1176
1177 impl crate::Keystore for Keystore {
1178 fn contains(
1179 &self,
1180 key_spec: &dyn KeySpecifier,
1181 item_type: &KeystoreItemType,
1182 ) -> Result<bool> {
1183 let wanted_arti_path = key_spec.arti_path().unwrap();
1184 Ok(self.inner.read().unwrap().iter().any(|res| match res {
1185 Ok((spec, ty, _)) => spec == &wanted_arti_path && ty == item_type,
1186 Err(_) => false,
1187 }))
1188 }
1189
1190 fn id(&self) -> &KeystoreId {
1191 &self.id
1192 }
1193
1194 fn get(
1195 &self,
1196 key_spec: &dyn KeySpecifier,
1197 item_type: &KeystoreItemType,
1198 ) -> Result<Option<ErasedKey>> {
1199 let key_spec = key_spec.arti_path().unwrap();
1200
1201 Ok(self.inner.read().unwrap().iter().find_map(|res| {
1202 if let Ok((arti_path, ty, k)) = res {
1203 if arti_path == &key_spec && ty == item_type {
1204 let mut k = k.clone();
1205 k.meta.set_retrieved_from(self.id().clone());
1206
1207 match item_type {
1208 KeystoreItemType::Key(_) => {
1209 return Some(Box::new(k) as Box<dyn ItemType>);
1210 }
1211 KeystoreItemType::Cert(_) => {
1212 return Some(Box::new(TestCert(k)) as Box<dyn ItemType>);
1216 }
1217 _ => panic!("unknown item type?!"),
1218 }
1219 }
1220 }
1221 None
1222 }))
1223 }
1224
1225 #[cfg(feature = "onion-service-cli-extra")]
1226 fn raw_entry_id(&self, raw_id: &str) -> Result<RawEntryId> {
1227 Ok(RawEntryId::Path(PathBuf::from(raw_id.to_string())))
1228 }
1229
1230 fn insert(&self, key: &dyn EncodableItem, key_spec: &dyn KeySpecifier) -> Result<()> {
1231 let key = key.downcast_ref::<TestItem>().unwrap();
1232
1233 let item = key.as_keystore_item()?;
1234 let item_type = item.item_type()?;
1235
1236 self.inner
1237 .write()
1238 .unwrap()
1239 .insert(
1244 0,
1245 Ok((key_spec.arti_path().unwrap(), item_type, key.clone())),
1246 );
1247
1248 Ok(())
1249 }
1250
1251 fn remove(
1252 &self,
1253 key_spec: &dyn KeySpecifier,
1254 item_type: &KeystoreItemType,
1255 ) -> Result<Option<()>> {
1256 let wanted_arti_path = key_spec.arti_path().unwrap();
1257 let index = self.inner.read().unwrap().iter().position(|res| {
1258 if let Ok((arti_path, ty, _)) = res {
1259 arti_path == &wanted_arti_path && ty == item_type
1260 } else {
1261 false
1262 }
1263 });
1264 let Some(index) = index else {
1265 return Ok(None);
1266 };
1267 let _ = self.inner.write().unwrap().remove(index);
1268
1269 Ok(Some(()))
1270 }
1271
1272 #[cfg(feature = "onion-service-cli-extra")]
1273 fn remove_unchecked(&self, entry_id: &RawEntryId) -> Result<()> {
1274 let index = self.inner.read().unwrap().iter().position(|res| match res {
1275 Ok((spec, ty, _)) => {
1276 let id = build_raw_id_path(spec, ty);
1277 entry_id == &id
1278 }
1279 Err(e) => e.entry().raw_id() == entry_id,
1280 });
1281 let Some(index) = index else {
1282 return Err(Error::Keystore(Arc::new(MockKeystoreError::NotFound)));
1283 };
1284 let _ = self.inner.write().unwrap().remove(index);
1285 Ok(())
1286 }
1287
1288 fn list(&self) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
1289 Ok(self
1290 .inner
1291 .read()
1292 .unwrap()
1293 .iter()
1294 .map(|res| match res {
1295 Ok((arti_path, ty, _)) => {
1296 let raw_id = RawEntryId::Path(PathBuf::from(&arti_path.to_string()));
1297
1298 Ok(KeystoreEntry::new(
1299 KeyPath::Arti(arti_path.clone()),
1300 ty.clone(),
1301 self.id(),
1302 raw_id,
1303 ))
1304 }
1305 Err(e) => Err(e.clone()),
1306 })
1307 .collect())
1308 }
1309 }
1310
1311 fn add_unrecognized_entries(keystore: &mut Keystore, count: usize) {
1313 for i in 0..count {
1314 let invalid_key_path = PathBuf::from(&format!("unrecognized_entry{}", i));
1315 let raw_id = RawEntryId::Path(invalid_key_path.clone());
1316 let entry = UnrecognizedEntry::new(raw_id, keystore.id.clone());
1317 let entry = UnrecognizedEntryError::new(
1318 entry,
1319 Arc::new(ArtiNativeKeystoreError::MalformedPath {
1320 path: invalid_key_path,
1321 err: MalformedPathError::NoExtension,
1322 }),
1323 );
1324 keystore.inner.write().unwrap().push(Err(entry));
1325 }
1326 }
1327
1328 macro_rules! impl_specifier {
1329 ($name:tt, $id:expr) => {
1330 struct $name;
1331
1332 impl KeySpecifier for $name {
1333 fn arti_path(&self) -> StdResult<ArtiPath, ArtiPathUnavailableError> {
1334 Ok(ArtiPath::new($id.into()).map_err(|e| tor_error::internal!("{e}"))?)
1335 }
1336
1337 fn ctor_path(&self) -> Option<crate::CTorPath> {
1338 None
1339 }
1340
1341 fn keypair_specifier(&self) -> Option<Box<dyn KeySpecifier>> {
1342 None
1343 }
1344 }
1345 };
1346 }
1347
1348 impl_specifier!(TestKeySpecifier1, "spec1");
1349 impl_specifier!(TestKeySpecifier2, "spec2");
1350 impl_specifier!(TestKeySpecifier3, "spec3");
1351 impl_specifier!(TestKeySpecifier4, "spec4");
1352
1353 impl_specifier!(TestPublicKeySpecifier1, "pub-spec1");
1354
1355 fn entry_descriptor(
1357 specifier: impl KeySpecifier,
1358 key_type: KeystoreItemType,
1359 keystore_id: &KeystoreId,
1360 ) -> KeystoreEntry {
1361 let arti_path = specifier.arti_path().unwrap();
1362 let raw_id = RawEntryId::Path(PathBuf::from(arti_path.as_ref()));
1363 KeystoreEntry {
1364 key_path: arti_path.into(),
1365 key_type,
1366 keystore_id,
1367 raw_id,
1368 }
1369 }
1370
1371 #[test]
1372 #[allow(clippy::cognitive_complexity)]
1373 fn insert_and_get() {
1374 let mut builder = KeyMgrBuilder::default().primary_store(Keystore::new_boxed("keystore1"));
1375
1376 builder.secondary_stores().extend([
1377 Keystore::new_boxed("keystore2"),
1378 Keystore::new_boxed("keystore3"),
1379 ]);
1380
1381 let mgr = builder.build().unwrap();
1382
1383 let old_key = mgr
1385 .insert(
1386 TestItem::new("coot"),
1387 &TestKeySpecifier1,
1388 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1389 true,
1390 )
1391 .unwrap();
1392
1393 assert!(old_key.is_none());
1394 let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1395 assert_eq!(key.meta.item_id(), "coot");
1396 assert_eq!(
1397 key.meta.retrieved_from(),
1398 Some(&KeystoreId::from_str("keystore2").unwrap())
1399 );
1400 assert_eq!(key.meta.is_generated(), false);
1401
1402 let old_key = mgr
1404 .insert(
1405 TestItem::new("gull"),
1406 &TestKeySpecifier1,
1407 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1408 true,
1409 )
1410 .unwrap()
1411 .unwrap();
1412 assert_eq!(old_key.meta.item_id(), "coot");
1413 assert_eq!(
1414 old_key.meta.retrieved_from(),
1415 Some(&KeystoreId::from_str("keystore2").unwrap())
1416 );
1417 assert_eq!(old_key.meta.is_generated(), false);
1418 let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1420 assert_eq!(key.meta.item_id(), "gull");
1421 assert_eq!(
1422 key.meta.retrieved_from(),
1423 Some(&KeystoreId::from_str("keystore2").unwrap())
1424 );
1425 assert_eq!(key.meta.is_generated(), false);
1426
1427 let err = mgr
1429 .insert(
1430 TestItem::new("gull"),
1431 &TestKeySpecifier1,
1432 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1433 false,
1434 )
1435 .unwrap_err();
1436 assert!(matches!(err, crate::Error::KeyAlreadyExists));
1437
1438 let old_key = mgr
1440 .insert(
1441 TestItem::new("penguin"),
1442 &TestKeySpecifier2,
1443 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1444 false,
1445 )
1446 .unwrap();
1447 assert!(old_key.is_none());
1448
1449 let old_key = mgr
1451 .insert(
1452 TestItem::new("moorhen"),
1453 &TestKeySpecifier3,
1454 KeystoreSelector::Primary,
1455 true,
1456 )
1457 .unwrap();
1458 assert!(old_key.is_none());
1459 let key = mgr.get::<TestItem>(&TestKeySpecifier3).unwrap().unwrap();
1460 assert_eq!(key.meta.item_id(), "moorhen");
1461 assert_eq!(
1462 key.meta.retrieved_from(),
1463 Some(&KeystoreId::from_str("keystore1").unwrap())
1464 );
1465 assert_eq!(key.meta.is_generated(), false);
1466
1467 assert!(mgr.get::<TestItem>(&TestKeySpecifier4).unwrap().is_none());
1469
1470 for store in ["keystore3", "keystore2", "keystore1"] {
1474 let old_key = mgr
1475 .insert(
1476 TestItem::new("cormorant"),
1477 &TestKeySpecifier4,
1478 KeystoreSelector::Id(&KeystoreId::from_str(store).unwrap()),
1479 true,
1480 )
1481 .unwrap();
1482 assert!(old_key.is_none());
1483
1484 let key = mgr.get::<TestItem>(&TestKeySpecifier4).unwrap().unwrap();
1486 assert_eq!(key.meta.item_id(), "cormorant");
1487 assert_eq!(
1488 key.meta.retrieved_from(),
1489 Some(&KeystoreId::from_str(store).unwrap())
1490 );
1491 assert_eq!(key.meta.is_generated(), false);
1492 }
1493
1494 let key = mgr.get::<TestItem>(&TestKeySpecifier4).unwrap().unwrap();
1497 assert_eq!(key.meta.item_id(), "cormorant");
1498 assert_eq!(
1499 key.meta.retrieved_from(),
1500 Some(&KeystoreId::from_str("keystore1").unwrap())
1501 );
1502 assert_eq!(key.meta.is_generated(), false);
1503 }
1504
1505 #[test]
1506 #[cfg(feature = "onion-service-cli-extra")]
1507 fn get_from() {
1508 let mut builder = KeyMgrBuilder::default().primary_store(Keystore::new_boxed("keystore1"));
1509
1510 builder.secondary_stores().extend([
1511 Keystore::new_boxed("keystore2"),
1512 Keystore::new_boxed("keystore3"),
1513 ]);
1514
1515 let mgr = builder.build().unwrap();
1516
1517 let keystore1_id = KeystoreId::from_str("keystore1").unwrap();
1518 let keystore2_id = KeystoreId::from_str("keystore2").unwrap();
1519 let key_id_1 = "mantis shrimp";
1520 let key_id_2 = "tardigrade";
1521
1522 let _ = mgr
1524 .insert(
1525 TestItem::new(key_id_1),
1526 &TestKeySpecifier1,
1527 KeystoreSelector::Id(&keystore1_id),
1528 true,
1529 )
1530 .unwrap();
1531
1532 let _ = mgr
1534 .insert(
1535 TestItem::new(key_id_2),
1536 &TestKeySpecifier1,
1537 KeystoreSelector::Id(&keystore2_id),
1538 true,
1539 )
1540 .unwrap();
1541
1542 let key = mgr
1544 .get_from::<TestItem>(&TestKeySpecifier1, &keystore2_id)
1545 .unwrap()
1546 .unwrap();
1547
1548 assert_eq!(key.meta.item_id(), key_id_2);
1549 assert_eq!(key.meta.retrieved_from(), Some(&keystore2_id));
1550 }
1551
1552 #[test]
1553 fn get_from_keypair() {
1554 const KEYSTORE_ID1: &str = "keystore1";
1555 const KEYSTORE_ID2: &str = "keystore2";
1556
1557 let mut builder = KeyMgrBuilder::default().primary_store(Keystore::new_boxed(KEYSTORE_ID1));
1558 builder
1559 .secondary_stores()
1560 .extend([Keystore::new_boxed(KEYSTORE_ID2)]);
1561 let mgr = builder.build().unwrap();
1562
1563 let keystore2 = KeystoreId::from_str(KEYSTORE_ID2).unwrap();
1564
1565 let _ = mgr
1567 .insert(
1568 TestItem::new("nightjar"),
1569 &TestDerivedKeypairSpecifier,
1570 KeystoreSelector::Id(&keystore2),
1571 true,
1572 )
1573 .unwrap();
1574
1575 macro_rules! boxed {
1576 ($closure:expr) => {
1577 Box::new($closure) as _
1578 };
1579 }
1580
1581 #[allow(clippy::type_complexity)]
1582 let getters: &[(&'static str, Box<dyn Fn() -> Result<Option<TestPublicKey>>>)] = &[
1583 (
1584 "get",
1585 boxed!(|| mgr.get::<TestPublicKey>(&TestDerivedKeySpecifier)),
1586 ),
1587 #[cfg(feature = "onion-service-cli-extra")]
1588 (
1589 "get_from",
1590 boxed!(|| mgr.get_from::<TestPublicKey>(&TestDerivedKeySpecifier, &keystore2)),
1591 ),
1592 (
1593 "remove",
1594 boxed!(|| mgr.remove::<TestPublicKey>(
1595 &TestDerivedKeySpecifier,
1596 KeystoreSelector::Id(&keystore2)
1597 )),
1598 ),
1599 ];
1600
1601 for (test_name, getter) in getters {
1602 let key = getter().unwrap().expect(test_name);
1605
1606 assert_eq!(key.meta.item_id(), "nightjar", "{test_name}");
1607 assert_eq!(key.meta.retrieved_from(), Some(&keystore2), "{test_name}");
1608 }
1609 }
1610
1611 #[test]
1612 fn remove() {
1613 let mut builder = KeyMgrBuilder::default().primary_store(Keystore::new_boxed("keystore1"));
1614
1615 builder.secondary_stores().extend([
1616 Keystore::new_boxed("keystore2"),
1617 Keystore::new_boxed("keystore3"),
1618 ]);
1619
1620 let mgr = builder.build().unwrap();
1621
1622 assert!(
1623 !mgr.secondary_stores[0]
1624 .contains(&TestKeySpecifier1, &TestItem::item_type())
1625 .unwrap()
1626 );
1627
1628 mgr.insert(
1630 TestItem::new("coot"),
1631 &TestKeySpecifier1,
1632 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1633 true,
1634 )
1635 .unwrap();
1636 let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1637 assert_eq!(key.meta.item_id(), "coot");
1638 assert_eq!(
1639 key.meta.retrieved_from(),
1640 Some(&KeystoreId::from_str("keystore2").unwrap())
1641 );
1642 assert_eq!(key.meta.is_generated(), false);
1643
1644 assert!(
1646 mgr.remove::<TestItem>(
1647 &TestKeySpecifier1,
1648 KeystoreSelector::Id(&KeystoreId::from_str("not_an_id_we_know_of").unwrap())
1649 )
1650 .is_err()
1651 );
1652 assert!(
1654 mgr.secondary_stores[0]
1655 .contains(&TestKeySpecifier1, &TestItem::item_type())
1656 .unwrap()
1657 );
1658
1659 assert!(
1661 mgr.remove::<TestItem>(&TestKeySpecifier1, KeystoreSelector::Primary)
1662 .unwrap()
1663 .is_none()
1664 );
1665
1666 assert!(
1668 mgr.secondary_stores[0]
1669 .contains(&TestKeySpecifier1, &TestItem::item_type())
1670 .unwrap()
1671 );
1672
1673 let removed_key = mgr
1675 .remove::<TestItem>(
1676 &TestKeySpecifier1,
1677 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1678 )
1679 .unwrap()
1680 .unwrap();
1681 assert_eq!(removed_key.meta.item_id(), "coot");
1682 assert_eq!(
1683 removed_key.meta.retrieved_from(),
1684 Some(&KeystoreId::from_str("keystore2").unwrap())
1685 );
1686 assert_eq!(removed_key.meta.is_generated(), false);
1687
1688 assert!(
1690 !mgr.secondary_stores[0]
1691 .contains(&TestKeySpecifier1, &TestItem::item_type())
1692 .unwrap()
1693 );
1694 }
1695
1696 #[test]
1697 fn keygen() {
1698 let mut rng = FakeEntropicRng(testing_rng());
1699 let mgr = KeyMgrBuilder::default()
1700 .primary_store(Keystore::new_boxed("keystore1"))
1701 .build()
1702 .unwrap();
1703
1704 mgr.insert(
1705 TestItem::new("coot"),
1706 &TestKeySpecifier1,
1707 KeystoreSelector::Primary,
1708 true,
1709 )
1710 .unwrap();
1711
1712 assert!(
1714 mgr.get::<TestPublicKey>(&TestPublicKeySpecifier1)
1715 .unwrap()
1716 .is_none()
1717 );
1718
1719 let err = mgr
1721 .generate::<TestItem>(
1722 &TestKeySpecifier1,
1723 KeystoreSelector::Primary,
1724 &mut rng,
1725 false,
1726 )
1727 .unwrap_err();
1728
1729 assert!(matches!(err, crate::Error::KeyAlreadyExists));
1730
1731 let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1733 assert_eq!(key.meta.item_id(), "coot");
1734 assert_eq!(
1735 key.meta.retrieved_from(),
1736 Some(&KeystoreId::from_str("keystore1").unwrap())
1737 );
1738 assert_eq!(key.meta.is_generated(), false);
1739
1740 assert!(
1742 mgr.get::<TestPublicKey>(&TestPublicKeySpecifier1)
1743 .unwrap()
1744 .is_none()
1745 );
1746
1747 let generated_key = mgr
1749 .generate::<TestItem>(
1750 &TestKeySpecifier1,
1751 KeystoreSelector::Primary,
1752 &mut rng,
1753 true,
1754 )
1755 .unwrap();
1756
1757 assert_eq!(generated_key.meta.item_id(), "generated_test_key");
1758 assert_eq!(generated_key.meta.retrieved_from(), None);
1761 assert_eq!(generated_key.meta.is_generated(), true);
1762
1763 let retrieved_key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1765 assert_eq!(retrieved_key.meta.item_id(), "generated_test_key");
1766 assert_eq!(
1767 retrieved_key.meta.retrieved_from(),
1768 Some(&KeystoreId::from_str("keystore1").unwrap())
1769 );
1770 assert_eq!(retrieved_key.meta.is_generated(), true);
1771
1772 assert!(
1774 mgr.get::<TestPublicKey>(&TestPublicKeySpecifier1)
1775 .unwrap()
1776 .is_none()
1777 );
1778 }
1779
1780 #[test]
1781 fn get_or_generate() {
1782 let mut rng = FakeEntropicRng(testing_rng());
1783 let mut builder = KeyMgrBuilder::default().primary_store(Keystore::new_boxed("keystore1"));
1784
1785 builder.secondary_stores().extend([
1786 Keystore::new_boxed("keystore2"),
1787 Keystore::new_boxed("keystore3"),
1788 ]);
1789
1790 let mgr = builder.build().unwrap();
1791
1792 let keystore2 = KeystoreId::from_str("keystore2").unwrap();
1793 let entry_desc1 = entry_descriptor(TestKeySpecifier1, TestItem::item_type(), &keystore2);
1794 assert!(mgr.get_entry::<TestItem>(&entry_desc1).unwrap().is_none());
1795
1796 mgr.insert(
1797 TestItem::new("coot"),
1798 &TestKeySpecifier1,
1799 KeystoreSelector::Id(&keystore2),
1800 true,
1801 )
1802 .unwrap();
1803
1804 let key = mgr
1806 .get_or_generate::<TestItem>(&TestKeySpecifier1, KeystoreSelector::Primary, &mut rng)
1807 .unwrap();
1808 assert_eq!(key.meta.item_id(), "coot");
1809 assert_eq!(
1810 key.meta.retrieved_from(),
1811 Some(&KeystoreId::from_str("keystore2").unwrap())
1812 );
1813 assert_eq!(key.meta.is_generated(), false);
1814
1815 assert_eq!(
1816 mgr.get_entry::<TestItem>(&entry_desc1)
1817 .unwrap()
1818 .map(|k| k.meta),
1819 Some(ItemMetadata::Key(KeyMetadata {
1820 item_id: "coot".to_string(),
1821 retrieved_from: Some(keystore2.clone()),
1822 is_generated: false,
1823 }))
1824 );
1825
1826 let keystore3 = KeystoreId::from_str("keystore3").unwrap();
1829 let generated_key = mgr
1830 .get_or_generate::<TestItem>(
1831 &TestKeySpecifier2,
1832 KeystoreSelector::Id(&keystore3),
1833 &mut rng,
1834 )
1835 .unwrap();
1836 assert_eq!(generated_key.meta.item_id(), "generated_test_key");
1837 assert_eq!(generated_key.meta.retrieved_from(), None);
1840 assert_eq!(generated_key.meta.is_generated(), true);
1841
1842 let retrieved_key = mgr.get::<TestItem>(&TestKeySpecifier2).unwrap().unwrap();
1844 assert_eq!(retrieved_key.meta.item_id(), "generated_test_key");
1845 assert_eq!(
1846 retrieved_key.meta.retrieved_from(),
1847 Some(&KeystoreId::from_str("keystore3").unwrap())
1848 );
1849 assert_eq!(retrieved_key.meta.is_generated(), true);
1850
1851 let entry_desc2 = entry_descriptor(TestKeySpecifier2, TestItem::item_type(), &keystore3);
1852 assert_eq!(
1853 mgr.get_entry::<TestItem>(&entry_desc2)
1854 .unwrap()
1855 .map(|k| k.meta),
1856 Some(ItemMetadata::Key(KeyMetadata {
1857 item_id: "generated_test_key".to_string(),
1858 retrieved_from: Some(keystore3.clone()),
1859 is_generated: true,
1860 }))
1861 );
1862
1863 let arti_pat = KeyPathPattern::Arti("*".to_string());
1864 let matching = mgr.list_matching(&arti_pat).unwrap();
1865
1866 assert_eq!(matching.len(), 2);
1867 assert!(matching.contains(&entry_desc1));
1868 assert!(matching.contains(&entry_desc2));
1869
1870 assert_eq!(mgr.remove_entry(&entry_desc2).unwrap(), Some(()));
1871 assert!(mgr.get_entry::<TestItem>(&entry_desc2).unwrap().is_none());
1872 assert!(mgr.remove_entry(&entry_desc2).unwrap().is_none());
1873 }
1874
1875 #[test]
1876 fn list_matching_ignores_unrecognized_keys() {
1877 let mut keystore = Keystore::new("keystore1");
1878 add_unrecognized_entries(&mut keystore, 1);
1879 let builder = KeyMgrBuilder::default().primary_store(Box::new(keystore));
1880
1881 let mgr = builder.build().unwrap();
1882
1883 let keystore1 = KeystoreId::from_str("keystore1").unwrap();
1884 mgr.insert(
1885 TestItem::new("whale shark"),
1886 &TestKeySpecifier1,
1887 KeystoreSelector::Id(&keystore1),
1888 true,
1889 )
1890 .unwrap();
1891
1892 let arti_pat = KeyPathPattern::Arti("*".to_string());
1893 let valid_key_path = KeyPath::Arti(TestKeySpecifier1.arti_path().unwrap());
1894 let matching = mgr.list_matching(&arti_pat).unwrap();
1895 assert_eq!(matching.len(), 1);
1897 assert_eq!(matching.first().unwrap().key_path(), &valid_key_path);
1898 }
1899
1900 #[cfg(feature = "onion-service-cli-extra")]
1901 #[test]
1902 fn keys_subcommands() {
1905 let mut keystore = Keystore::new("keystore1");
1906 add_unrecognized_entries(&mut keystore, 1);
1907 let mut builder = KeyMgrBuilder::default().primary_store(Box::new(keystore));
1908 builder.secondary_stores().extend([
1909 Keystore::new_boxed("keystore2"),
1910 Keystore::new_boxed("keystore3"),
1911 ]);
1912
1913 let mgr = builder.build().unwrap();
1914 let keystore1id = KeystoreId::from_str("keystore1").unwrap();
1915 let keystore2id = KeystoreId::from_str("keystore2").unwrap();
1916 let keystore3id = KeystoreId::from_str("keystore3").unwrap();
1917
1918 let _ = mgr
1920 .insert(
1921 TestItem::new("pangolin"),
1922 &TestKeySpecifier1,
1923 KeystoreSelector::Id(&keystore1id),
1924 true,
1925 )
1926 .unwrap();
1927
1928 let _ = mgr
1930 .insert(
1931 TestItem::new("coot"),
1932 &TestKeySpecifier2,
1933 KeystoreSelector::Id(&keystore2id),
1934 true,
1935 )
1936 .unwrap();
1937
1938 let _ = mgr
1940 .insert(
1941 TestItem::new("penguin"),
1942 &TestKeySpecifier3,
1943 KeystoreSelector::Id(&keystore3id),
1944 true,
1945 )
1946 .unwrap();
1947
1948 let assert_key = |path, ty, expected_path: &ArtiPath, expected_type| {
1949 assert_eq!(ty, expected_type);
1950 assert_eq!(path, &KeyPath::Arti(expected_path.clone()));
1951 };
1952 let item_type = TestItem::new("axolotl").item.item_type().unwrap();
1953 let unrecognized_entry_id = RawEntryId::Path(PathBuf::from("unrecognized_entry0"));
1954
1955 let entries = mgr.list().unwrap();
1957
1958 let expected_items = [
1959 (keystore1id, TestKeySpecifier1.arti_path().unwrap()),
1960 (keystore2id, TestKeySpecifier2.arti_path().unwrap()),
1961 (keystore3id, TestKeySpecifier3.arti_path().unwrap()),
1962 ];
1963
1964 let mut recognized_entries = 0;
1966 let mut unrecognized_entries = 0;
1967 for entry in entries.iter() {
1968 match entry {
1969 Ok(e) => {
1970 if let Some((_, expected_arti_path)) = expected_items
1971 .iter()
1972 .find(|(keystore_id, _)| keystore_id == e.keystore_id())
1973 {
1974 assert_key(e.key_path(), e.key_type(), expected_arti_path, &item_type);
1975 recognized_entries += 1;
1976 continue;
1977 }
1978
1979 panic!("Unexpected key encountered {:?}", e);
1980 }
1981 Err(u) => {
1982 assert_eq!(u.entry().raw_id(), &unrecognized_entry_id);
1983 unrecognized_entries += 1;
1984 }
1985 }
1986 }
1987 assert_eq!(recognized_entries, 3);
1988 assert_eq!(unrecognized_entries, 1);
1989
1990 let keystores = mgr.list_keystores().iter().len();
1992
1993 assert_eq!(keystores, 3);
1994
1995 let primary_keystore_id = KeystoreId::from_str("keystore1").unwrap();
1997 let entries = mgr.list_by_id(&primary_keystore_id).unwrap();
1998
1999 let mut recognized_entries = 0;
2001 let mut unrecognized_entries = 0;
2002 let mut all_entries = vec![];
2004 for entry in entries.iter() {
2005 match entry {
2006 Ok(entry) => {
2007 assert_key(
2008 entry.key_path(),
2009 entry.key_type(),
2010 &TestKeySpecifier1.arti_path().unwrap(),
2011 &item_type,
2012 );
2013 recognized_entries += 1;
2014 let raw_id = build_raw_id_path(entry.key_path(), entry.key_type());
2015 let keystore_id = primary_keystore_id.clone();
2016 all_entries.push((raw_id, keystore_id));
2017 }
2018 Err(u) => {
2019 let raw_id = u.entry().raw_id().clone();
2020 assert_eq!(raw_id, unrecognized_entry_id);
2021 unrecognized_entries += 1;
2022 let keystore_id = u.entry().keystore_id().clone();
2023 all_entries.push((raw_id, keystore_id));
2024 }
2025 }
2026 }
2027 assert_eq!(recognized_entries, 1);
2028 assert_eq!(unrecognized_entries, 1);
2029
2030 for (raw_id, keystore_id) in all_entries {
2032 mgr.remove_unchecked(&raw_id.to_string(), &keystore_id)
2033 .unwrap();
2034 }
2035
2036 let entries = mgr.list_by_id(&primary_keystore_id).unwrap();
2038 assert_eq!(entries.len(), 0);
2039 }
2040
2041 #[cfg(feature = "experimental-api")]
2043 #[derive(Clone, Copy, Debug, PartialEq)]
2044 enum GenerateItem {
2045 Yes,
2046 No,
2047 }
2048
2049 fn make_certificate(subject_key: &TestItem, signed_with: &TestItem) -> AlwaysValidCert {
2050 let subject_id = subject_key.meta.as_key().unwrap().item_id.clone();
2051 let signing_id = signed_with.meta.as_key().unwrap().item_id.clone();
2052
2053 let meta = ItemMetadata::Cert(CertMetadata {
2054 subject_key_id: subject_id,
2055 signing_key_id: signing_id,
2056 retrieved_from: None,
2057 is_generated: true,
2058 });
2059
2060 let mut rng = FakeEntropicRng(testing_rng());
2066 let keypair = ed25519::Keypair::generate(&mut rng);
2067 let encoded_cert = Ed25519Cert::builder()
2068 .cert_type(tor_cert::CertType::IDENTITY_V_SIGNING)
2069 .expiration(SystemTime::get() + Duration::from_secs(180))
2070 .signing_key(keypair.public_key().into())
2071 .cert_key(CertifiedKey::Ed25519(keypair.public_key().into()))
2072 .encode_and_sign(&keypair)
2073 .unwrap();
2074 let test_cert = CertData::TorEd25519Cert(encoded_cert);
2075 AlwaysValidCert(TestItem {
2076 item: KeystoreItem::Cert(test_cert),
2077 meta,
2078 })
2079 }
2080
2081 #[cfg(feature = "experimental-api")]
2082 macro_rules! run_certificate_test {
2083 (
2084 generate_subject_key = $generate_subject_key:expr,
2085 generate_signing_key = $generate_signing_key:expr,
2086 $($expected_err:tt)?
2087 ) => {{
2088 use GenerateItem::*;
2089
2090 let mut rng = FakeEntropicRng(testing_rng());
2091 let mut builder = KeyMgrBuilder::default().primary_store(Keystore::new_boxed("keystore1"));
2092
2093 builder
2094 .secondary_stores()
2095 .extend([Keystore::new_boxed("keystore2"), Keystore::new_boxed("keystore3")]);
2096
2097 let mgr = builder.build().unwrap();
2098
2099 let spec = crate::test_utils::TestCertSpecifier {
2100 subject_key_spec: TestDerivedKeySpecifier,
2101 denotator: "foo".into(),
2102 };
2103
2104 if $generate_subject_key == Yes {
2105 let _ = mgr
2106 .generate::<TestItem>(
2107 &TestKeySpecifier1,
2108 KeystoreSelector::Primary,
2109 &mut rng,
2110 false,
2111 )
2112 .unwrap();
2113 }
2114
2115 if $generate_signing_key == Yes {
2116 let _ = mgr
2117 .generate::<TestItem>(
2118 &TestKeySpecifier2,
2119 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
2120 &mut rng,
2121 false,
2122 )
2123 .unwrap();
2124 }
2125
2126
2127 let signing_key_spec = TestKeySpecifier2;
2128 let res = mgr
2129 .get_or_generate_key_and_cert::<TestItem, AlwaysValidCert>(
2130 &spec,
2131 &signing_key_spec,
2132 &make_certificate,
2133 KeystoreSelector::Primary,
2134 &mut rng,
2135 );
2136
2137 #[allow(unused_assignments)]
2138 #[allow(unused_mut)]
2139 let mut has_error = false;
2140 $(
2141 has_error = true;
2142 let err = res.clone().unwrap_err();
2143 assert!(
2144 matches!(
2145 err,
2146 crate::Error::Corruption(KeystoreCorruptionError::$expected_err)
2147 ),
2148 "unexpected error: {err:?}",
2149 );
2150 )?
2151
2152 if !has_error {
2153 let (key, cert) = res.unwrap();
2154
2155 let expected_subj_key_id = if $generate_subject_key == Yes {
2156 "generated_test_key"
2157 } else {
2158 "generated_test_key"
2159 };
2160
2161 assert_eq!(key.meta.item_id(), expected_subj_key_id);
2162 assert_eq!(
2163 cert.0.meta.as_cert().unwrap().subject_key_id,
2164 expected_subj_key_id
2165 );
2166 assert_eq!(
2167 cert.0.meta.as_cert().unwrap().signing_key_id,
2168 "generated_test_key"
2169 );
2170 assert_eq!(cert.0.meta.is_generated(), true);
2171 }
2172 }}
2173 }
2174
2175 #[test]
2176 #[cfg(feature = "experimental-api")]
2177 #[rustfmt::skip] #[allow(clippy::cognitive_complexity)] fn get_certificate() {
2180 run_certificate_test!(
2181 generate_subject_key = No,
2182 generate_signing_key = No,
2183 MissingSigningKey
2184 );
2185
2186 run_certificate_test!(
2187 generate_subject_key = Yes,
2188 generate_signing_key = No,
2189 MissingSigningKey
2190 );
2191
2192 run_certificate_test!(
2193 generate_subject_key = No,
2194 generate_signing_key = Yes,
2195 );
2196
2197 run_certificate_test!(
2198 generate_subject_key = Yes,
2199 generate_signing_key = Yes,
2200 );
2201 }
2202
2203 #[test]
2204 #[cfg(feature = "experimental-api")]
2205 fn get_cert_entry() {
2206 let mut rng = FakeEntropicRng(testing_rng());
2207 let builder = KeyMgrBuilder::default().primary_store(Keystore::new_boxed("keystore1"));
2208 let mgr = builder.build().unwrap();
2209
2210 let _ = mgr
2212 .generate::<TestItem>(
2213 &TestKeySpecifier1,
2214 KeystoreSelector::Primary,
2215 &mut rng,
2216 false,
2217 )
2218 .unwrap();
2219
2220 let _ = mgr
2222 .generate::<TestItem>(
2223 &TestKeySpecifier2,
2224 KeystoreSelector::Primary,
2225 &mut rng,
2226 false,
2227 )
2228 .unwrap();
2229
2230 for cert_deno in 0..10 {
2232 let cert_spec = TestCertSpecifier {
2233 subject_key_spec: TestDerivedKeySpecifier,
2234 denotator: cert_deno.to_string(),
2235 };
2236
2237 let res = mgr.get_or_generate_key_and_cert::<TestItem, AlwaysValidCert>(
2238 &cert_spec,
2239 &TestKeySpecifier2,
2240 &make_certificate,
2241 KeystoreSelector::Primary,
2242 &mut rng,
2243 );
2244
2245 assert!(res.is_ok());
2246 }
2247
2248 let any_pat = TestCertSpecifierPattern::new_any().arti_pattern().unwrap();
2250
2251 assert_eq!(
2253 any_pat,
2254 KeyPathPattern::Arti("test/simple_key+@*".to_string())
2255 );
2256 let certs = mgr.list_matching(&any_pat).unwrap();
2257
2258 assert_eq!(certs.len(), 10);
2260
2261 let all_paths = certs
2263 .iter()
2264 .map(|entry| entry.key_path().arti().unwrap().as_str().to_string())
2265 .sorted()
2266 .collect::<Vec<_>>();
2267
2268 let expected_paths = (0..10)
2269 .map(|i| format!("test/simple_key+@{i}"))
2270 .collect::<Vec<_>>();
2271 assert_eq!(all_paths, expected_paths);
2272
2273 for entry in certs {
2274 let always_valid_cert = mgr
2275 .get_cert_entry::<TestCertSpecifier, TestItem, AlwaysValidCert>(
2276 &entry,
2277 &TestKeySpecifier2,
2278 )
2279 .unwrap();
2280
2281 assert!(always_valid_cert.is_some());
2283 }
2284
2285 const EXPIRED_DENO: &str = "expired";
2287
2288 let cert_spec = TestCertSpecifier {
2290 subject_key_spec: TestDerivedKeySpecifier,
2291 denotator: EXPIRED_DENO.to_string(),
2292 };
2293
2294 let meta = CertMetadata {
2296 subject_key_id: "foo".to_string(),
2297 signing_key_id: "bar".to_string(),
2298 retrieved_from: None,
2299 is_generated: false,
2300 };
2301 let test_cert =
2302 CertData::TorEd25519Cert(EncodedEd25519Cert::dangerously_from_bytes(b"foobar"));
2303 let cert = AlwaysExpiredCert(TestItem {
2304 item: KeystoreItem::Cert(test_cert),
2305 meta: ItemMetadata::Cert(meta),
2306 });
2307
2308 let res = mgr.insert_cert::<TestItem, AlwaysExpiredCert>(
2309 cert,
2310 &cert_spec,
2311 KeystoreSelector::Primary,
2312 );
2313 assert!(res.is_ok());
2314
2315 let pat = KeyPathPattern::Arti(format!("test/simple_key+@{EXPIRED_DENO}"));
2317 let certs = mgr.list_matching(&pat).unwrap();
2318 assert_eq!(certs.len(), 1);
2319 let entry = &certs[0];
2320
2321 let err = mgr
2322 .get_cert_entry::<TestCertSpecifier, TestItem, AlwaysExpiredCert>(
2323 entry,
2324 &TestKeySpecifier2,
2325 )
2326 .unwrap_err();
2327
2328 assert!(
2330 matches!(err, Error::InvalidCert(InvalidCertError::TimeValidity(_))),
2331 "{err:?}"
2332 );
2333 }
2334}