1use crate::raw::{RawEntryId, RawKeystoreEntry};
6use crate::{
7 ArtiPath, BoxedKeystore, KeyCertificateSpecifier, KeyPath, 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#[derive(derive_builder::Builder)]
44#[builder(pattern = "owned", build_fn(private, name = "build_unvalidated"))]
45pub struct KeyMgr {
46 primary_store: BoxedKeystore,
48 #[builder(default, setter(custom))]
50 secondary_stores: Vec<BoxedKeystore>,
51 #[builder(default, setter(skip))]
56 key_info_extractors: Vec<&'static dyn KeyPathInfoExtractor>,
57}
58
59#[derive(Clone, Debug, PartialEq, amplify::Getters)]
67pub struct KeystoreEntry<'a> {
68 key_path: KeyPath,
70 key_type: KeystoreItemType,
72 #[getter(as_copy)]
74 keystore_id: &'a KeystoreId,
75 #[getter(skip)]
78 raw_id: RawEntryId,
79}
80
81impl<'a> KeystoreEntry<'a> {
82 pub(crate) fn new(
84 key_path: KeyPath,
85 key_type: KeystoreItemType,
86 keystore_id: &'a KeystoreId,
87 raw_id: RawEntryId,
88 ) -> Self {
89 Self {
90 key_path,
91 key_type,
92 keystore_id,
93 raw_id,
94 }
95 }
96
97 #[cfg(feature = "onion-service-cli-extra")]
99 pub fn raw_entry(&self) -> RawKeystoreEntry {
100 RawKeystoreEntry::new(self.raw_id.clone(), self.keystore_id.clone())
101 }
102}
103
104impl<'a> From<KeystoreEntry<'a>> for KeystoreEntryResult<KeystoreEntry<'a>> {
109 fn from(val: KeystoreEntry<'a>) -> Self {
110 Ok(val)
111 }
112}
113
114impl KeyMgrBuilder {
115 pub fn build(self) -> StdResult<KeyMgr, KeyMgrBuilderError> {
117 use itertools::Itertools as _;
118
119 let mut keymgr = self.build_unvalidated()?;
120
121 if !keymgr.all_stores().map(|s| s.id()).all_unique() {
122 return Err(KeyMgrBuilderError::ValidationError(
123 "the keystore IDs are not pairwise unique".into(),
124 ));
125 }
126
127 keymgr.key_info_extractors = inventory::iter::<&'static dyn KeyPathInfoExtractor>
128 .into_iter()
129 .copied()
130 .collect();
131
132 Ok(keymgr)
133 }
134}
135
136impl KeyMgrBuilder {
141 pub fn secondary_stores(&mut self) -> &mut Vec<BoxedKeystore> {
147 self.secondary_stores.get_or_insert(Default::default())
148 }
149
150 pub fn set_secondary_stores(mut self, list: Vec<BoxedKeystore>) -> Self {
152 self.secondary_stores = Some(list);
153 self
154 }
155
156 pub fn opt_secondary_stores(&self) -> &Option<Vec<BoxedKeystore>> {
160 &self.secondary_stores
161 }
162
163 pub fn opt_secondary_stores_mut(&mut self) -> &mut Option<Vec<BoxedKeystore>> {
167 &mut self.secondary_stores
168 }
169}
170
171inventory::collect!(&'static dyn crate::KeyPathInfoExtractor);
172
173impl KeyMgr {
174 pub fn get<K: ToEncodableKey>(&self, key_spec: &dyn KeySpecifier) -> Result<Option<K>> {
181 let result = self.get_from_store(key_spec, &K::Key::item_type(), self.all_stores())?;
182 if result.is_none() {
183 if let Some(key_pair_spec) = key_spec.keypair_specifier() {
186 return Ok(self.get::<K::KeyPair>(&*key_pair_spec)?.map(|k| k.into()));
187 }
188 }
189 Ok(result)
190 }
191
192 pub fn get_entry<K: ToEncodableKey>(&self, entry: &KeystoreEntry) -> Result<Option<K>> {
200 let selector = entry.keystore_id().into();
201 let store = self.select_keystore(&selector)?;
202 self.get_from_store(entry.key_path(), entry.key_type(), [store].into_iter())
203 }
204
205 pub fn get_or_generate<K>(
217 &self,
218 key_spec: &dyn KeySpecifier,
219 selector: KeystoreSelector,
220 rng: &mut dyn KeygenRng,
221 ) -> Result<K>
222 where
223 K: ToEncodableKey,
224 K::Key: Keygen,
225 {
226 match self.get(key_spec)? {
227 Some(k) => Ok(k),
228 None => self.generate(key_spec, selector, rng, false),
229 }
230 }
231
232 #[cfg(feature = "onion-service-cli-extra")]
242 pub fn get_from<K: ToEncodableKey>(
243 &self,
244 key_spec: &dyn KeySpecifier,
245 keystore_id: &KeystoreId,
246 ) -> Result<Option<K>> {
247 let store = std::iter::once(self.find_keystore(keystore_id)?);
248 self.get_from_store(key_spec, &K::Key::item_type(), store)
249 }
250
251 #[cfg(feature = "onion-service-cli-extra")]
261 pub fn validate_entry_integrity(&self, entry: &KeystoreEntry) -> Result<()> {
262 let selector = entry.keystore_id().into();
263 let store = self.select_keystore(&selector)?;
264 let _ = store.get(entry.key_path(), entry.key_type())?;
266
267 let path = entry.key_path();
268 let _ = self
270 .describe(path)
271 .ok_or_else(|| KeystoreCorruptionError::Unrecognized(path.clone()))?;
272
273 Ok(())
274 }
275
276 pub fn generate<K>(
297 &self,
298 key_spec: &dyn KeySpecifier,
299 selector: KeystoreSelector,
300 rng: &mut dyn KeygenRng,
301 overwrite: bool,
302 ) -> Result<K>
303 where
304 K: ToEncodableKey,
305 K::Key: Keygen,
306 {
307 let store = self.select_keystore(&selector)?;
308
309 if overwrite || !store.contains(key_spec, &K::Key::item_type())? {
310 let key = K::Key::generate(rng)?;
311 store.insert(&key, key_spec)?;
312
313 Ok(K::from_encodable_key(key))
314 } else {
315 Err(crate::Error::KeyAlreadyExists)
316 }
317 }
318
319 pub fn insert<K: ToEncodableKey>(
332 &self,
333 key: K,
334 key_spec: &dyn KeySpecifier,
335 selector: KeystoreSelector,
336 overwrite: bool,
337 ) -> Result<Option<K>> {
338 let key = key.to_encodable_key();
339 let store = self.select_keystore(&selector)?;
340 let key_type = K::Key::item_type();
341 let old_key: Option<K> = self.get_from_store(key_spec, &key_type, [store].into_iter())?;
342
343 if old_key.is_some() && !overwrite {
344 Err(crate::Error::KeyAlreadyExists)
345 } else {
346 let () = store.insert(&key, key_spec)?;
347 Ok(old_key)
348 }
349 }
350
351 pub fn remove<K: ToEncodableKey>(
362 &self,
363 key_spec: &dyn KeySpecifier,
364 selector: KeystoreSelector,
365 ) -> Result<Option<K>> {
366 let store = self.select_keystore(&selector)?;
367 let key_type = K::Key::item_type();
368 let old_key: Option<K> = self.get_from_store(key_spec, &key_type, [store].into_iter())?;
369
370 store.remove(key_spec, &key_type)?;
371
372 Ok(old_key)
373 }
374
375 pub fn remove_entry(&self, entry: &KeystoreEntry) -> Result<Option<()>> {
387 let selector = entry.keystore_id().into();
388 let store = self.select_keystore(&selector)?;
389
390 store.remove(entry.key_path(), entry.key_type())
391 }
392
393 #[cfg(feature = "onion-service-cli-extra")]
401 pub fn remove_unchecked(&self, raw_id: &str, keystore_id: &KeystoreId) -> Result<()> {
402 let selector = KeystoreSelector::from(keystore_id);
403 let store = self.select_keystore(&selector)?;
404 let raw_id = store.raw_entry_id(raw_id)?;
405 let store = self.select_keystore(&selector)?;
406 store.remove_unchecked(&raw_id)
407 }
408
409 pub fn list_matching(&self, pat: &KeyPathPattern) -> Result<Vec<KeystoreEntry>> {
417 self.all_stores()
418 .map(|store| -> Result<Vec<_>> {
419 Ok(store
420 .list()?
421 .into_iter()
422 .filter_map(|entry| entry.ok())
423 .filter(|entry| entry.key_path().matches(pat))
424 .collect::<Vec<_>>())
425 })
426 .flatten_ok()
427 .collect::<Result<Vec<_>>>()
428 }
429
430 #[cfg(feature = "onion-service-cli-extra")]
432 pub fn list_by_id(&self, id: &KeystoreId) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
433 self.find_keystore(id)?.list()
434 }
435
436 #[cfg(feature = "onion-service-cli-extra")]
438 pub fn list(&self) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
439 self.all_stores()
440 .map(|store| -> Result<Vec<_>> { store.list() })
441 .flatten_ok()
442 .collect::<Result<Vec<_>>>()
443 }
444
445 #[cfg(feature = "onion-service-cli-extra")]
447 pub fn list_keystores(&self) -> Vec<KeystoreId> {
448 self.all_stores()
449 .map(|store| store.id().to_owned())
450 .collect()
451 }
452
453 pub fn describe(&self, path: &KeyPath) -> Option<KeyPathInfo> {
462 for info_extractor in &self.key_info_extractors {
463 if let Ok(info) = info_extractor.describe(path) {
464 return Some(info);
465 }
466 }
467
468 None
469 }
470
471 fn get_from_store_raw<'a, K: ItemType>(
477 &self,
478 key_spec: &dyn KeySpecifier,
479 key_type: &KeystoreItemType,
480 stores: impl Iterator<Item = &'a BoxedKeystore>,
481 ) -> Result<Option<K>> {
482 let static_key_type = K::item_type();
483 if key_type != &static_key_type {
484 return Err(internal!(
485 "key type {:?} does not match the key type {:?} of requested key K::Key",
486 key_type,
487 static_key_type
488 )
489 .into());
490 }
491
492 for store in stores {
493 let key = match store.get(key_spec, &K::item_type()) {
494 Ok(None) => {
495 continue;
497 }
498 Ok(Some(k)) => k,
499 Err(e) => {
500 return Err(e);
502 }
503 };
504
505 let key: K = key
507 .downcast::<K>()
508 .map(|k| *k)
509 .map_err(|_| internal!("failed to downcast key to requested type"))?;
510
511 return Ok(Some(key));
512 }
513
514 Ok(None)
515 }
516
517 fn get_from_store<'a, K: ToEncodableKey>(
521 &self,
522 key_spec: &dyn KeySpecifier,
523 key_type: &KeystoreItemType,
524 stores: impl Iterator<Item = &'a BoxedKeystore>,
525 ) -> Result<Option<K>> {
526 let Some(key) = self.get_from_store_raw::<K::Key>(key_spec, key_type, stores)? else {
527 return Ok(None);
528 };
529
530 Ok(Some(K::from_encodable_key(key)))
531 }
532
533 #[cfg(feature = "experimental-api")]
549 pub fn get_key_and_cert<K, C>(
550 &self,
551 spec: &dyn KeyCertificateSpecifier,
552 ) -> Result<Option<(K, C)>>
553 where
554 K: ToEncodableKey,
555 C: ToEncodableCert<K>,
556 {
557 let subject_key_spec = spec.subject_key_specifier();
558 let Some(key) =
560 self.get_from_store::<K>(subject_key_spec, &K::Key::item_type(), self.all_stores())?
561 else {
562 return Ok(None);
563 };
564
565 let subject_key_arti_path = subject_key_spec
566 .arti_path()
567 .map_err(|_| bad_api_usage!("subject key does not have an ArtiPath?!"))?;
568 let cert_spec =
569 ArtiPath::from_path_and_denotators(subject_key_arti_path, &spec.cert_denotators())
570 .map_err(into_bad_api_usage!("invalid certificate specifier"))?;
571
572 let Some(cert) = self.get_from_store_raw::<C::ParsedCert>(
573 &cert_spec,
574 &<C::ParsedCert as ItemType>::item_type(),
575 self.all_stores(),
576 )?
577 else {
578 return Err(KeystoreCorruptionError::MissingCertificate.into());
579 };
580
581 let signed_with = self.get_cert_signing_key::<K, C>(spec)?;
583 let cert = C::validate(cert, &key, &signed_with)?;
584
585 Ok(Some((key, cert)))
586 }
587
588 #[cfg(feature = "experimental-api")]
624 pub fn get_or_generate_key_and_cert<K, C>(
625 &self,
626 spec: &dyn KeyCertificateSpecifier,
627 make_certificate: impl FnOnce(&K, &<C as ToEncodableCert<K>>::SigningKey) -> C,
628 selector: KeystoreSelector,
629 rng: &mut dyn KeygenRng,
630 ) -> Result<(K, C)>
631 where
632 K: ToEncodableKey,
633 K::Key: Keygen,
634 C: ToEncodableCert<K>,
635 {
636 let subject_key_spec = spec.subject_key_specifier();
637 let subject_key_arti_path = subject_key_spec
638 .arti_path()
639 .map_err(|_| bad_api_usage!("subject key does not have an ArtiPath?!"))?;
640
641 let cert_specifier =
642 ArtiPath::from_path_and_denotators(subject_key_arti_path, &spec.cert_denotators())
643 .map_err(into_bad_api_usage!("invalid certificate specifier"))?;
644
645 let maybe_cert = self.get_from_store_raw::<C::ParsedCert>(
646 &cert_specifier,
647 &C::ParsedCert::item_type(),
648 self.all_stores(),
649 )?;
650
651 let maybe_subject_key = self.get::<K>(subject_key_spec)?;
652
653 match (&maybe_cert, &maybe_subject_key) {
654 (Some(_), None) => {
655 return Err(KeystoreCorruptionError::MissingSubjectKey.into());
656 }
657 _ => {
658 }
660 }
661 let subject_key = match maybe_subject_key {
662 Some(key) => key,
663 _ => self.generate(subject_key_spec, selector, rng, false)?,
664 };
665
666 let signed_with = self.get_cert_signing_key::<K, C>(spec)?;
667 let cert = match maybe_cert {
668 Some(cert) => C::validate(cert, &subject_key, &signed_with)?,
669 None => {
670 let cert = make_certificate(&subject_key, &signed_with);
671
672 let () = self.insert_cert(cert.clone(), &cert_specifier, selector)?;
673
674 cert
675 }
676 };
677
678 Ok((subject_key, cert))
679 }
680
681 fn all_stores(&self) -> impl Iterator<Item = &BoxedKeystore> {
683 iter::once(&self.primary_store).chain(self.secondary_stores.iter())
684 }
685
686 fn select_keystore(&self, selector: &KeystoreSelector) -> Result<&BoxedKeystore> {
691 match selector {
692 KeystoreSelector::Id(keystore_id) => self.find_keystore(keystore_id),
693 KeystoreSelector::Primary => Ok(&self.primary_store),
694 }
695 }
696
697 fn find_keystore(&self, id: &KeystoreId) -> Result<&BoxedKeystore> {
702 self.all_stores()
703 .find(|keystore| keystore.id() == id)
704 .ok_or_else(|| crate::Error::KeystoreNotFound(id.clone()))
705 }
706
707 #[cfg(feature = "experimental-api")]
712 fn get_cert_signing_key<K, C>(
713 &self,
714 spec: &dyn KeyCertificateSpecifier,
715 ) -> Result<C::SigningKey>
716 where
717 K: ToEncodableKey,
718 C: ToEncodableCert<K>,
719 {
720 let Some(signing_key_spec) = spec.signing_key_specifier() else {
721 return Err(bad_api_usage!(
722 "signing key specifier is None, but external signing key was not provided?"
723 )
724 .into());
725 };
726
727 let Some(signing_key) = self.get_from_store::<C::SigningKey>(
728 signing_key_spec,
729 &<C::SigningKey as ToEncodableKey>::Key::item_type(),
730 self.all_stores(),
731 )?
732 else {
733 return Err(KeystoreCorruptionError::MissingSigningKey.into());
734 };
735
736 Ok(signing_key)
737 }
738
739 fn insert_cert<K, C>(
746 &self,
747 cert: C,
748 cert_spec: &dyn KeySpecifier,
749 selector: KeystoreSelector,
750 ) -> Result<()>
751 where
752 K: ToEncodableKey,
753 K::Key: Keygen,
754 C: ToEncodableCert<K>,
755 {
756 let cert = cert.to_encodable_cert();
757 let store = self.select_keystore(&selector)?;
758
759 let () = store.insert(&cert, cert_spec)?;
760 Ok(())
761 }
762}
763
764#[cfg(test)]
765mod tests {
766 #![allow(clippy::bool_assert_comparison)]
768 #![allow(clippy::clone_on_copy)]
769 #![allow(clippy::dbg_macro)]
770 #![allow(clippy::mixed_attributes_style)]
771 #![allow(clippy::print_stderr)]
772 #![allow(clippy::print_stdout)]
773 #![allow(clippy::single_char_pattern)]
774 #![allow(clippy::unwrap_used)]
775 #![allow(clippy::unchecked_time_subtraction)]
776 #![allow(clippy::useless_vec)]
777 #![allow(clippy::needless_pass_by_value)]
778 use super::*;
780 use crate::keystore::arti::err::{ArtiNativeKeystoreError, MalformedPathError};
781 use crate::raw::{RawEntryId, RawKeystoreEntry};
782 use crate::{
783 ArtiPath, ArtiPathUnavailableError, Error, KeyPath, KeystoreEntryResult, KeystoreError,
784 UnrecognizedEntryError,
785 };
786 use std::path::PathBuf;
787 use std::result::Result as StdResult;
788 use std::str::FromStr;
789 use std::sync::{Arc, RwLock};
790 use std::time::{Duration, SystemTime};
791 use tor_basic_utils::test_rng::testing_rng;
792 use tor_cert::CertifiedKey;
793 use tor_cert::Ed25519Cert;
794 use tor_error::{ErrorKind, HasKind};
795 use tor_key_forge::{
796 CertData, EncodableItem, ErasedKey, InvalidCertError, KeyType, KeystoreItem,
797 };
798 use tor_llcrypto::pk::ed25519::{self, Ed25519PublicKey as _};
799 use tor_llcrypto::rng::FakeEntropicRng;
800
801 #[derive(Clone, Debug, PartialEq)]
803 struct KeyMetadata {
804 item_id: String,
806 retrieved_from: Option<KeystoreId>,
810 is_generated: bool,
812 }
813
814 #[derive(Clone, Debug, PartialEq)]
816 struct CertMetadata {
817 subject_key_id: String,
819 signing_key_id: String,
821 retrieved_from: Option<KeystoreId>,
825 is_generated: bool,
828 }
829
830 #[derive(Clone, Debug, PartialEq, derive_more::From)]
832 enum ItemMetadata {
833 Key(KeyMetadata),
835 Cert(CertMetadata),
837 }
838
839 impl ItemMetadata {
840 fn item_id(&self) -> &str {
845 match self {
846 ItemMetadata::Key(k) => &k.item_id,
847 ItemMetadata::Cert(c) => &c.subject_key_id,
848 }
849 }
850
851 fn retrieved_from(&self) -> Option<&KeystoreId> {
853 match self {
854 ItemMetadata::Key(k) => k.retrieved_from.as_ref(),
855 ItemMetadata::Cert(c) => c.retrieved_from.as_ref(),
856 }
857 }
858
859 fn is_generated(&self) -> bool {
861 match self {
862 ItemMetadata::Key(k) => k.is_generated,
863 ItemMetadata::Cert(c) => c.is_generated,
864 }
865 }
866
867 fn set_retrieved_from(&mut self, id: KeystoreId) {
869 match self {
870 ItemMetadata::Key(meta) => meta.retrieved_from = Some(id),
871 ItemMetadata::Cert(meta) => meta.retrieved_from = Some(id),
872 }
873 }
874
875 fn as_key(&self) -> Option<&KeyMetadata> {
877 match self {
878 ItemMetadata::Key(meta) => Some(meta),
879 _ => None,
880 }
881 }
882
883 fn as_cert(&self) -> Option<&CertMetadata> {
885 match self {
886 ItemMetadata::Cert(meta) => Some(meta),
887 _ => None,
888 }
889 }
890 }
891
892 #[derive(Clone, Debug)]
894 struct TestItem {
895 item: KeystoreItem,
897 meta: ItemMetadata,
899 }
900
901 #[derive(Clone, Debug)]
903 struct AlwaysValidCert(TestItem);
904
905 #[derive(Clone, Debug)]
907 struct TestPublicKey {
908 key: KeystoreItem,
910 }
911
912 impl From<TestItem> for TestPublicKey {
913 fn from(tk: TestItem) -> TestPublicKey {
914 TestPublicKey { key: tk.item }
915 }
916 }
917
918 impl TestItem {
919 fn new(item_id: &str) -> Self {
921 let mut rng = testing_rng();
922 TestItem {
923 item: ed25519::Keypair::generate(&mut rng)
924 .as_keystore_item()
925 .unwrap(),
926 meta: ItemMetadata::Key(KeyMetadata {
927 item_id: item_id.to_string(),
928 retrieved_from: None,
929 is_generated: false,
930 }),
931 }
932 }
933 }
934
935 impl Keygen for TestItem {
936 fn generate(mut rng: &mut dyn KeygenRng) -> tor_key_forge::Result<Self>
937 where
938 Self: Sized,
939 {
940 Ok(TestItem {
941 item: ed25519::Keypair::generate(&mut rng).as_keystore_item()?,
942 meta: ItemMetadata::Key(KeyMetadata {
943 item_id: "generated_test_key".to_string(),
944 retrieved_from: None,
945 is_generated: true,
946 }),
947 })
948 }
949 }
950
951 impl ItemType for TestItem {
952 fn item_type() -> KeystoreItemType
953 where
954 Self: Sized,
955 {
956 KeyType::Ed25519Keypair.into()
958 }
959 }
960
961 impl EncodableItem for TestItem {
962 fn as_keystore_item(&self) -> tor_key_forge::Result<KeystoreItem> {
963 Ok(self.item.clone())
964 }
965 }
966
967 impl ToEncodableKey for TestItem {
968 type Key = Self;
969 type KeyPair = Self;
970
971 fn to_encodable_key(self) -> Self::Key {
972 self
973 }
974
975 fn from_encodable_key(key: Self::Key) -> Self {
976 key
977 }
978 }
979
980 impl ItemType for TestPublicKey {
981 fn item_type() -> KeystoreItemType
982 where
983 Self: Sized,
984 {
985 KeyType::Ed25519PublicKey.into()
986 }
987 }
988
989 impl EncodableItem for TestPublicKey {
990 fn as_keystore_item(&self) -> tor_key_forge::Result<KeystoreItem> {
991 Ok(self.key.clone())
992 }
993 }
994
995 impl ToEncodableKey for TestPublicKey {
996 type Key = Self;
997 type KeyPair = TestItem;
998
999 fn to_encodable_key(self) -> Self::Key {
1000 self
1001 }
1002
1003 fn from_encodable_key(key: Self::Key) -> Self {
1004 key
1005 }
1006 }
1007
1008 impl ToEncodableCert<TestItem> for AlwaysValidCert {
1009 type ParsedCert = TestItem;
1010 type EncodableCert = TestItem;
1011 type SigningKey = TestItem;
1012
1013 fn validate(
1014 cert: Self::ParsedCert,
1015 _subject: &TestItem,
1016 _signed_with: &Self::SigningKey,
1017 ) -> StdResult<Self, InvalidCertError> {
1018 Ok(Self(cert))
1020 }
1021
1022 fn to_encodable_cert(self) -> Self::EncodableCert {
1024 self.0
1025 }
1026 }
1027
1028 #[derive(thiserror::Error, Debug, Clone, derive_more::Display)]
1029 enum MockKeystoreError {
1030 NotFound,
1031 }
1032
1033 impl KeystoreError for MockKeystoreError {}
1034
1035 impl HasKind for MockKeystoreError {
1036 fn kind(&self) -> ErrorKind {
1037 tor_error::ErrorKind::Other
1039 }
1040 }
1041
1042 fn build_raw_id_path<T: ToString>(key_path: &T, key_type: &KeystoreItemType) -> RawEntryId {
1043 let mut path = key_path.to_string();
1044 path.push('.');
1045 path.push_str(&key_type.arti_extension());
1046 RawEntryId::Path(PathBuf::from(&path))
1047 }
1048
1049 macro_rules! impl_keystore {
1050 ($name:tt, $id:expr $(,$unrec:expr)?) => {
1051 struct $name {
1052 inner: RwLock<
1053 Vec<StdResult<(ArtiPath, KeystoreItemType, TestItem), UnrecognizedEntryError>>,
1054 >,
1055 id: KeystoreId,
1056 }
1057
1058 impl Default for $name {
1059 fn default() -> Self {
1060 let id = KeystoreId::from_str($id).unwrap();
1061 let inner: RwLock<
1062 Vec<
1063 StdResult<
1064 (ArtiPath, KeystoreItemType, TestItem),
1065 UnrecognizedEntryError,
1066 >,
1067 >,
1068 > = Default::default();
1069 $(
1072 for i in 0..$unrec {
1073 let invalid_key_path =
1074 PathBuf::from(&format!("unrecognized_entry{}", i));
1075 let raw_id = RawEntryId::Path(invalid_key_path.clone());
1076 let entry = RawKeystoreEntry::new(raw_id, id.clone()).into();
1077 let entry = UnrecognizedEntryError::new(
1078 entry,
1079 Arc::new(ArtiNativeKeystoreError::MalformedPath {
1080 path: invalid_key_path,
1081 err: MalformedPathError::NoExtension,
1082 }),
1083 );
1084 inner.write().unwrap().push(Err(entry));
1085 }
1086 )?
1087 Self {
1088 inner,
1089 id,
1090 }
1091 }
1092 }
1093
1094 #[allow(dead_code)] impl $name {
1096 fn new_boxed() -> BoxedKeystore {
1097 Box::<Self>::default()
1098 }
1099 }
1100
1101 impl crate::Keystore for $name {
1102 fn contains(
1103 &self,
1104 key_spec: &dyn KeySpecifier,
1105 item_type: &KeystoreItemType,
1106 ) -> Result<bool> {
1107 let wanted_arti_path = key_spec.arti_path().unwrap();
1108 Ok(self
1109 .inner
1110 .read()
1111 .unwrap()
1112 .iter()
1113 .find(|res| match res {
1114 Ok((spec, ty, _)) => spec == &wanted_arti_path && ty == item_type,
1115 Err(_) => false,
1116 })
1117 .is_some())
1118 }
1119
1120 fn id(&self) -> &KeystoreId {
1121 &self.id
1122 }
1123
1124 fn get(
1125 &self,
1126 key_spec: &dyn KeySpecifier,
1127 item_type: &KeystoreItemType,
1128 ) -> Result<Option<ErasedKey>> {
1129 let key_spec = key_spec.arti_path().unwrap();
1130
1131 Ok(self.inner.read().unwrap().iter().find_map(|res| {
1132 match res {
1133 Ok((arti_path, ty, k)) => {
1134 if arti_path == &key_spec && ty == item_type {
1135 let mut k = k.clone();
1136 k.meta.set_retrieved_from(self.id().clone());
1137 return Some(Box::new(k) as Box<dyn ItemType>);
1138 }
1139 }
1140 Err(_) => {}
1141 }
1142 None
1143 }))
1144 }
1145
1146 #[cfg(feature = "onion-service-cli-extra")]
1147 fn raw_entry_id(&self, raw_id: &str) -> Result<RawEntryId> {
1148 Ok(RawEntryId::Path(
1149 PathBuf::from(raw_id.to_string()),
1150 ))
1151 }
1152
1153 fn insert(
1154 &self,
1155 key: &dyn EncodableItem,
1156 key_spec: &dyn KeySpecifier,
1157 ) -> Result<()> {
1158 let key = key.downcast_ref::<TestItem>().unwrap();
1159
1160 let item = key.as_keystore_item()?;
1161 let meta = key.meta.clone();
1162
1163 let item_type = item.item_type()?;
1164 let key = TestItem { item, meta };
1165
1166 self.inner
1167 .write()
1168 .unwrap()
1169 .insert(0, (Ok((key_spec.arti_path().unwrap(), item_type, key))));
1174
1175 Ok(())
1176 }
1177
1178 fn remove(
1179 &self,
1180 key_spec: &dyn KeySpecifier,
1181 item_type: &KeystoreItemType,
1182 ) -> Result<Option<()>> {
1183 let wanted_arti_path = key_spec.arti_path().unwrap();
1184 let index = self.inner.read().unwrap().iter().position(|res| {
1185 if let Ok((arti_path, ty, _)) = res {
1186 arti_path == &wanted_arti_path && ty == item_type
1187 } else {
1188 false
1189 }
1190 });
1191 let Some(index) = index else {
1192 return Ok(None);
1193 };
1194 let _ = self.inner.write().unwrap().remove(index);
1195
1196 Ok(Some(()))
1197 }
1198
1199 #[cfg(feature = "onion-service-cli-extra")]
1200 fn remove_unchecked(&self, entry_id: &RawEntryId) -> Result<()> {
1201 let index = self.inner.read().unwrap().iter().position(|res| match res {
1202 Ok((spec, ty, _)) => {
1203 let id = build_raw_id_path(spec, ty);
1204 entry_id == &id
1205 }
1206 Err(e) => {
1207 e.entry().raw_id() == entry_id
1208 }
1209 });
1210 let Some(index) = index else {
1211 return Err(Error::Keystore(Arc::new(MockKeystoreError::NotFound)));
1212 };
1213 let _ = self.inner.write().unwrap().remove(index);
1214 Ok(())
1215 }
1216
1217 fn list(&self) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
1218 Ok(self
1219 .inner
1220 .read()
1221 .unwrap()
1222 .iter()
1223 .map(|res| match res {
1224 Ok((arti_path, ty, _)) => {
1225 let raw_id = RawEntryId::Path(
1226 PathBuf::from(
1227 &arti_path.to_string(),
1228 )
1229 );
1230
1231 Ok(KeystoreEntry::new(KeyPath::Arti(arti_path.clone()), ty.clone(), self.id(), raw_id))
1232 }
1233 Err(e) => Err(e.clone()),
1234 })
1235 .collect())
1236 }
1237 }
1238 };
1239 }
1240
1241 macro_rules! impl_specifier {
1242 ($name:tt, $id:expr) => {
1243 struct $name;
1244
1245 impl KeySpecifier for $name {
1246 fn arti_path(&self) -> StdResult<ArtiPath, ArtiPathUnavailableError> {
1247 Ok(ArtiPath::new($id.into()).map_err(|e| tor_error::internal!("{e}"))?)
1248 }
1249
1250 fn ctor_path(&self) -> Option<crate::CTorPath> {
1251 None
1252 }
1253
1254 fn keypair_specifier(&self) -> Option<Box<dyn KeySpecifier>> {
1255 None
1256 }
1257 }
1258 };
1259 }
1260
1261 impl_keystore!(Keystore1, "keystore1");
1262 impl_keystore!(Keystore2, "keystore2");
1263 impl_keystore!(Keystore3, "keystore3");
1264 impl_keystore!(KeystoreUnrec1, "keystore_unrec1", 1);
1265
1266 impl_specifier!(TestKeySpecifier1, "spec1");
1267 impl_specifier!(TestKeySpecifier2, "spec2");
1268 impl_specifier!(TestKeySpecifier3, "spec3");
1269 impl_specifier!(TestKeySpecifier4, "spec4");
1270
1271 impl_specifier!(TestPublicKeySpecifier1, "pub-spec1");
1272
1273 fn entry_descriptor(specifier: impl KeySpecifier, keystore_id: &KeystoreId) -> KeystoreEntry {
1275 let arti_path = specifier.arti_path().unwrap();
1276 let raw_id = RawEntryId::Path(PathBuf::from(arti_path.as_ref()));
1277 KeystoreEntry {
1278 key_path: arti_path.into(),
1279 key_type: TestItem::item_type(),
1280 keystore_id,
1281 raw_id,
1282 }
1283 }
1284
1285 #[test]
1286 #[allow(clippy::cognitive_complexity)]
1287 fn insert_and_get() {
1288 let mut builder = KeyMgrBuilder::default().primary_store(Box::<Keystore1>::default());
1289
1290 builder
1291 .secondary_stores()
1292 .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
1293
1294 let mgr = builder.build().unwrap();
1295
1296 let old_key = mgr
1298 .insert(
1299 TestItem::new("coot"),
1300 &TestKeySpecifier1,
1301 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1302 true,
1303 )
1304 .unwrap();
1305
1306 assert!(old_key.is_none());
1307 let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1308 assert_eq!(key.meta.item_id(), "coot");
1309 assert_eq!(
1310 key.meta.retrieved_from(),
1311 Some(&KeystoreId::from_str("keystore2").unwrap())
1312 );
1313 assert_eq!(key.meta.is_generated(), false);
1314
1315 let old_key = mgr
1317 .insert(
1318 TestItem::new("gull"),
1319 &TestKeySpecifier1,
1320 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1321 true,
1322 )
1323 .unwrap()
1324 .unwrap();
1325 assert_eq!(old_key.meta.item_id(), "coot");
1326 assert_eq!(
1327 old_key.meta.retrieved_from(),
1328 Some(&KeystoreId::from_str("keystore2").unwrap())
1329 );
1330 assert_eq!(old_key.meta.is_generated(), false);
1331 let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1333 assert_eq!(key.meta.item_id(), "gull");
1334 assert_eq!(
1335 key.meta.retrieved_from(),
1336 Some(&KeystoreId::from_str("keystore2").unwrap())
1337 );
1338 assert_eq!(key.meta.is_generated(), false);
1339
1340 let err = mgr
1342 .insert(
1343 TestItem::new("gull"),
1344 &TestKeySpecifier1,
1345 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1346 false,
1347 )
1348 .unwrap_err();
1349 assert!(matches!(err, crate::Error::KeyAlreadyExists));
1350
1351 let old_key = mgr
1353 .insert(
1354 TestItem::new("penguin"),
1355 &TestKeySpecifier2,
1356 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1357 false,
1358 )
1359 .unwrap();
1360 assert!(old_key.is_none());
1361
1362 let old_key = mgr
1364 .insert(
1365 TestItem::new("moorhen"),
1366 &TestKeySpecifier3,
1367 KeystoreSelector::Primary,
1368 true,
1369 )
1370 .unwrap();
1371 assert!(old_key.is_none());
1372 let key = mgr.get::<TestItem>(&TestKeySpecifier3).unwrap().unwrap();
1373 assert_eq!(key.meta.item_id(), "moorhen");
1374 assert_eq!(
1375 key.meta.retrieved_from(),
1376 Some(&KeystoreId::from_str("keystore1").unwrap())
1377 );
1378 assert_eq!(key.meta.is_generated(), false);
1379
1380 assert!(mgr.get::<TestItem>(&TestKeySpecifier4).unwrap().is_none());
1382
1383 for store in ["keystore3", "keystore2", "keystore1"] {
1387 let old_key = mgr
1388 .insert(
1389 TestItem::new("cormorant"),
1390 &TestKeySpecifier4,
1391 KeystoreSelector::Id(&KeystoreId::from_str(store).unwrap()),
1392 true,
1393 )
1394 .unwrap();
1395 assert!(old_key.is_none());
1396
1397 let key = mgr.get::<TestItem>(&TestKeySpecifier4).unwrap().unwrap();
1399 assert_eq!(key.meta.item_id(), "cormorant");
1400 assert_eq!(
1401 key.meta.retrieved_from(),
1402 Some(&KeystoreId::from_str(store).unwrap())
1403 );
1404 assert_eq!(key.meta.is_generated(), false);
1405 }
1406
1407 let key = mgr.get::<TestItem>(&TestKeySpecifier4).unwrap().unwrap();
1410 assert_eq!(key.meta.item_id(), "cormorant");
1411 assert_eq!(
1412 key.meta.retrieved_from(),
1413 Some(&KeystoreId::from_str("keystore1").unwrap())
1414 );
1415 assert_eq!(key.meta.is_generated(), false);
1416 }
1417
1418 #[test]
1419 #[cfg(feature = "onion-service-cli-extra")]
1420 fn get_from() {
1421 let mut builder = KeyMgrBuilder::default().primary_store(Box::<Keystore1>::default());
1422
1423 builder
1424 .secondary_stores()
1425 .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
1426
1427 let mgr = builder.build().unwrap();
1428
1429 let keystore1_id = KeystoreId::from_str("keystore1").unwrap();
1430 let keystore2_id = KeystoreId::from_str("keystore2").unwrap();
1431 let key_id_1 = "mantis shrimp";
1432 let key_id_2 = "tardigrade";
1433
1434 let _ = mgr
1436 .insert(
1437 TestItem::new(key_id_1),
1438 &TestKeySpecifier1,
1439 KeystoreSelector::Id(&keystore1_id),
1440 true,
1441 )
1442 .unwrap();
1443
1444 let _ = mgr
1446 .insert(
1447 TestItem::new(key_id_2),
1448 &TestKeySpecifier1,
1449 KeystoreSelector::Id(&keystore2_id),
1450 true,
1451 )
1452 .unwrap();
1453
1454 let key = mgr
1456 .get_from::<TestItem>(&TestKeySpecifier1, &keystore2_id)
1457 .unwrap()
1458 .unwrap();
1459
1460 assert_eq!(key.meta.item_id(), key_id_2);
1461 assert_eq!(key.meta.retrieved_from(), Some(&keystore2_id));
1462 }
1463
1464 #[test]
1465 fn remove() {
1466 let mut builder = KeyMgrBuilder::default().primary_store(Box::<Keystore1>::default());
1467
1468 builder
1469 .secondary_stores()
1470 .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
1471
1472 let mgr = builder.build().unwrap();
1473
1474 assert!(
1475 !mgr.secondary_stores[0]
1476 .contains(&TestKeySpecifier1, &TestItem::item_type())
1477 .unwrap()
1478 );
1479
1480 mgr.insert(
1482 TestItem::new("coot"),
1483 &TestKeySpecifier1,
1484 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1485 true,
1486 )
1487 .unwrap();
1488 let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1489 assert_eq!(key.meta.item_id(), "coot");
1490 assert_eq!(
1491 key.meta.retrieved_from(),
1492 Some(&KeystoreId::from_str("keystore2").unwrap())
1493 );
1494 assert_eq!(key.meta.is_generated(), false);
1495
1496 assert!(
1498 mgr.remove::<TestItem>(
1499 &TestKeySpecifier1,
1500 KeystoreSelector::Id(&KeystoreId::from_str("not_an_id_we_know_of").unwrap())
1501 )
1502 .is_err()
1503 );
1504 assert!(
1506 mgr.secondary_stores[0]
1507 .contains(&TestKeySpecifier1, &TestItem::item_type())
1508 .unwrap()
1509 );
1510
1511 assert!(
1513 mgr.remove::<TestItem>(&TestKeySpecifier1, KeystoreSelector::Primary)
1514 .unwrap()
1515 .is_none()
1516 );
1517
1518 assert!(
1520 mgr.secondary_stores[0]
1521 .contains(&TestKeySpecifier1, &TestItem::item_type())
1522 .unwrap()
1523 );
1524
1525 let removed_key = mgr
1527 .remove::<TestItem>(
1528 &TestKeySpecifier1,
1529 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1530 )
1531 .unwrap()
1532 .unwrap();
1533 assert_eq!(removed_key.meta.item_id(), "coot");
1534 assert_eq!(
1535 removed_key.meta.retrieved_from(),
1536 Some(&KeystoreId::from_str("keystore2").unwrap())
1537 );
1538 assert_eq!(removed_key.meta.is_generated(), false);
1539
1540 assert!(
1542 !mgr.secondary_stores[0]
1543 .contains(&TestKeySpecifier1, &TestItem::item_type())
1544 .unwrap()
1545 );
1546 }
1547
1548 #[test]
1549 fn keygen() {
1550 let mut rng = FakeEntropicRng(testing_rng());
1551 let mgr = KeyMgrBuilder::default()
1552 .primary_store(Box::<Keystore1>::default())
1553 .build()
1554 .unwrap();
1555
1556 mgr.insert(
1557 TestItem::new("coot"),
1558 &TestKeySpecifier1,
1559 KeystoreSelector::Primary,
1560 true,
1561 )
1562 .unwrap();
1563
1564 assert!(
1566 mgr.get::<TestPublicKey>(&TestPublicKeySpecifier1)
1567 .unwrap()
1568 .is_none()
1569 );
1570
1571 let err = mgr
1573 .generate::<TestItem>(
1574 &TestKeySpecifier1,
1575 KeystoreSelector::Primary,
1576 &mut rng,
1577 false,
1578 )
1579 .unwrap_err();
1580
1581 assert!(matches!(err, crate::Error::KeyAlreadyExists));
1582
1583 let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1585 assert_eq!(key.meta.item_id(), "coot");
1586 assert_eq!(
1587 key.meta.retrieved_from(),
1588 Some(&KeystoreId::from_str("keystore1").unwrap())
1589 );
1590 assert_eq!(key.meta.is_generated(), false);
1591
1592 assert!(
1594 mgr.get::<TestPublicKey>(&TestPublicKeySpecifier1)
1595 .unwrap()
1596 .is_none()
1597 );
1598
1599 let generated_key = mgr
1601 .generate::<TestItem>(
1602 &TestKeySpecifier1,
1603 KeystoreSelector::Primary,
1604 &mut rng,
1605 true,
1606 )
1607 .unwrap();
1608
1609 assert_eq!(generated_key.meta.item_id(), "generated_test_key");
1610 assert_eq!(generated_key.meta.retrieved_from(), None);
1613 assert_eq!(generated_key.meta.is_generated(), true);
1614
1615 let retrieved_key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1617 assert_eq!(retrieved_key.meta.item_id(), "generated_test_key");
1618 assert_eq!(
1619 retrieved_key.meta.retrieved_from(),
1620 Some(&KeystoreId::from_str("keystore1").unwrap())
1621 );
1622 assert_eq!(retrieved_key.meta.is_generated(), true);
1623
1624 assert!(
1626 mgr.get::<TestPublicKey>(&TestPublicKeySpecifier1)
1627 .unwrap()
1628 .is_none()
1629 );
1630 }
1631
1632 #[test]
1633 fn get_or_generate() {
1634 let mut rng = FakeEntropicRng(testing_rng());
1635 let mut builder = KeyMgrBuilder::default().primary_store(Box::<Keystore1>::default());
1636
1637 builder
1638 .secondary_stores()
1639 .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
1640
1641 let mgr = builder.build().unwrap();
1642
1643 let keystore2 = KeystoreId::from_str("keystore2").unwrap();
1644 let entry_desc1 = entry_descriptor(TestKeySpecifier1, &keystore2);
1645 assert!(mgr.get_entry::<TestItem>(&entry_desc1).unwrap().is_none());
1646
1647 mgr.insert(
1648 TestItem::new("coot"),
1649 &TestKeySpecifier1,
1650 KeystoreSelector::Id(&keystore2),
1651 true,
1652 )
1653 .unwrap();
1654
1655 let key = mgr
1657 .get_or_generate::<TestItem>(&TestKeySpecifier1, KeystoreSelector::Primary, &mut rng)
1658 .unwrap();
1659 assert_eq!(key.meta.item_id(), "coot");
1660 assert_eq!(
1661 key.meta.retrieved_from(),
1662 Some(&KeystoreId::from_str("keystore2").unwrap())
1663 );
1664 assert_eq!(key.meta.is_generated(), false);
1665
1666 assert_eq!(
1667 mgr.get_entry::<TestItem>(&entry_desc1)
1668 .unwrap()
1669 .map(|k| k.meta),
1670 Some(ItemMetadata::Key(KeyMetadata {
1671 item_id: "coot".to_string(),
1672 retrieved_from: Some(keystore2.clone()),
1673 is_generated: false,
1674 }))
1675 );
1676
1677 let keystore3 = KeystoreId::from_str("keystore3").unwrap();
1680 let generated_key = mgr
1681 .get_or_generate::<TestItem>(
1682 &TestKeySpecifier2,
1683 KeystoreSelector::Id(&keystore3),
1684 &mut rng,
1685 )
1686 .unwrap();
1687 assert_eq!(generated_key.meta.item_id(), "generated_test_key");
1688 assert_eq!(generated_key.meta.retrieved_from(), None);
1691 assert_eq!(generated_key.meta.is_generated(), true);
1692
1693 let retrieved_key = mgr.get::<TestItem>(&TestKeySpecifier2).unwrap().unwrap();
1695 assert_eq!(retrieved_key.meta.item_id(), "generated_test_key");
1696 assert_eq!(
1697 retrieved_key.meta.retrieved_from(),
1698 Some(&KeystoreId::from_str("keystore3").unwrap())
1699 );
1700 assert_eq!(retrieved_key.meta.is_generated(), true);
1701
1702 let entry_desc2 = entry_descriptor(TestKeySpecifier2, &keystore3);
1703 assert_eq!(
1704 mgr.get_entry::<TestItem>(&entry_desc2)
1705 .unwrap()
1706 .map(|k| k.meta),
1707 Some(ItemMetadata::Key(KeyMetadata {
1708 item_id: "generated_test_key".to_string(),
1709 retrieved_from: Some(keystore3.clone()),
1710 is_generated: true,
1711 }))
1712 );
1713
1714 let arti_pat = KeyPathPattern::Arti("*".to_string());
1715 let matching = mgr.list_matching(&arti_pat).unwrap();
1716
1717 assert_eq!(matching.len(), 2);
1718 assert!(matching.contains(&entry_desc1));
1719 assert!(matching.contains(&entry_desc2));
1720
1721 assert_eq!(mgr.remove_entry(&entry_desc2).unwrap(), Some(()));
1722 assert!(mgr.get_entry::<TestItem>(&entry_desc2).unwrap().is_none());
1723 assert!(mgr.remove_entry(&entry_desc2).unwrap().is_none());
1724 }
1725
1726 #[test]
1727 fn list_matching_ignores_unrecognized_keys() {
1728 let builder = KeyMgrBuilder::default().primary_store(Box::new(KeystoreUnrec1::default()));
1729
1730 let mgr = builder.build().unwrap();
1731
1732 let unrec_1 = KeystoreId::from_str("keystore_unrec1").unwrap();
1733 mgr.insert(
1734 TestItem::new("whale shark"),
1735 &TestKeySpecifier1,
1736 KeystoreSelector::Id(&unrec_1),
1737 true,
1738 )
1739 .unwrap();
1740
1741 let arti_pat = KeyPathPattern::Arti("*".to_string());
1742 let valid_key_path = KeyPath::Arti(TestKeySpecifier1.arti_path().unwrap());
1743 let matching = mgr.list_matching(&arti_pat).unwrap();
1744 assert_eq!(matching.len(), 1);
1746 assert_eq!(matching.first().unwrap().key_path(), &valid_key_path);
1747 }
1748
1749 #[cfg(feature = "onion-service-cli-extra")]
1750 #[test]
1751 fn keys_subcommands() {
1754 let mut builder =
1755 KeyMgrBuilder::default().primary_store(Box::new(KeystoreUnrec1::default()));
1756 builder
1757 .secondary_stores()
1758 .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
1759
1760 let mgr = builder.build().unwrap();
1761 let ks_unrec1id = KeystoreId::from_str("keystore_unrec1").unwrap();
1762 let keystore2id = KeystoreId::from_str("keystore2").unwrap();
1763 let keystore3id = KeystoreId::from_str("keystore3").unwrap();
1764
1765 let _ = mgr
1767 .insert(
1768 TestItem::new("pangolin"),
1769 &TestKeySpecifier1,
1770 KeystoreSelector::Id(&ks_unrec1id),
1771 true,
1772 )
1773 .unwrap();
1774
1775 let _ = mgr
1777 .insert(
1778 TestItem::new("coot"),
1779 &TestKeySpecifier2,
1780 KeystoreSelector::Id(&keystore2id),
1781 true,
1782 )
1783 .unwrap();
1784
1785 let _ = mgr
1787 .insert(
1788 TestItem::new("penguin"),
1789 &TestKeySpecifier3,
1790 KeystoreSelector::Id(&keystore3id),
1791 true,
1792 )
1793 .unwrap();
1794
1795 let assert_key = |path, ty, expected_path: &ArtiPath, expected_type| {
1796 assert_eq!(ty, expected_type);
1797 assert_eq!(path, &KeyPath::Arti(expected_path.clone()));
1798 };
1799 let item_type = TestItem::new("axolotl").item.item_type().unwrap();
1800 let unrecognized_entry_id = RawEntryId::Path(PathBuf::from("unrecognized_entry0"));
1801
1802 let entries = mgr.list().unwrap();
1804
1805 let expected_items = [
1806 (ks_unrec1id, TestKeySpecifier1.arti_path().unwrap()),
1807 (keystore2id, TestKeySpecifier2.arti_path().unwrap()),
1808 (keystore3id, TestKeySpecifier3.arti_path().unwrap()),
1809 ];
1810
1811 let mut recognized_entries = 0;
1813 let mut unrecognized_entries = 0;
1814 for entry in entries.iter() {
1815 match entry {
1816 Ok(e) => {
1817 if let Some((_, expected_arti_path)) = expected_items
1818 .iter()
1819 .find(|(keystore_id, _)| keystore_id == e.keystore_id())
1820 {
1821 assert_key(e.key_path(), e.key_type(), expected_arti_path, &item_type);
1822 recognized_entries += 1;
1823 continue;
1824 }
1825
1826 panic!("Unexpected key encountered {:?}", e);
1827 }
1828 Err(u) => {
1829 assert_eq!(u.entry().raw_id(), &unrecognized_entry_id);
1830 unrecognized_entries += 1;
1831 }
1832 }
1833 }
1834 assert_eq!(recognized_entries, 3);
1835 assert_eq!(unrecognized_entries, 1);
1836
1837 let keystores = mgr.list_keystores().iter().len();
1839
1840 assert_eq!(keystores, 3);
1841
1842 let primary_keystore_id = KeystoreId::from_str("keystore_unrec1").unwrap();
1844 let entries = mgr.list_by_id(&primary_keystore_id).unwrap();
1845
1846 let mut recognized_entries = 0;
1848 let mut unrecognized_entries = 0;
1849 let mut all_entries = vec![];
1851 for entry in entries.iter() {
1852 match entry {
1853 Ok(entry) => {
1854 assert_key(
1855 entry.key_path(),
1856 entry.key_type(),
1857 &TestKeySpecifier1.arti_path().unwrap(),
1858 &item_type,
1859 );
1860 recognized_entries += 1;
1861 all_entries.push(RawKeystoreEntry::new(
1862 build_raw_id_path(entry.key_path(), entry.key_type()),
1863 primary_keystore_id.clone(),
1864 ));
1865 }
1866 Err(u) => {
1867 assert_eq!(u.entry().raw_id(), &unrecognized_entry_id);
1868 unrecognized_entries += 1;
1869 all_entries.push(u.entry().into());
1870 }
1871 }
1872 }
1873 assert_eq!(recognized_entries, 1);
1874 assert_eq!(unrecognized_entries, 1);
1875
1876 for entry in all_entries {
1878 mgr.remove_unchecked(&entry.raw_id().to_string(), entry.keystore_id())
1879 .unwrap();
1880 }
1881
1882 let entries = mgr.list_by_id(&primary_keystore_id).unwrap();
1884 assert_eq!(entries.len(), 0);
1885 }
1886
1887 #[cfg(feature = "experimental-api")]
1889 #[derive(Clone, Copy, Debug, PartialEq)]
1890 enum GenerateItem {
1891 Yes,
1892 No,
1893 }
1894
1895 #[cfg(feature = "experimental-api")]
1896 macro_rules! run_certificate_test {
1897 (
1898 generate_subject_key = $generate_subject_key:expr,
1899 generate_signing_key = $generate_signing_key:expr,
1900 $($expected_err:tt)?
1901 ) => {{
1902 use GenerateItem::*;
1903
1904 let mut rng = FakeEntropicRng(testing_rng());
1905 let mut builder = KeyMgrBuilder::default().primary_store(Box::<Keystore1>::default());
1906
1907 builder
1908 .secondary_stores()
1909 .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
1910
1911 let mgr = builder.build().unwrap();
1912
1913 let spec = crate::test_utils::TestCertSpecifier {
1914 subject_key_spec: TestKeySpecifier1,
1915 signing_key_spec: TestKeySpecifier2,
1916 denotator: vec!["foo".into()],
1917 };
1918
1919 if $generate_subject_key == Yes {
1920 let _ = mgr
1921 .generate::<TestItem>(
1922 &TestKeySpecifier1,
1923 KeystoreSelector::Primary,
1924 &mut rng,
1925 false,
1926 )
1927 .unwrap();
1928 }
1929
1930 if $generate_signing_key == Yes {
1931 let _ = mgr
1932 .generate::<TestItem>(
1933 &TestKeySpecifier2,
1934 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1935 &mut rng,
1936 false,
1937 )
1938 .unwrap();
1939 }
1940
1941 let make_certificate = move |subject_key: &TestItem, signed_with: &TestItem| {
1942 let subject_id = subject_key.meta.as_key().unwrap().item_id.clone();
1943 let signing_id = signed_with.meta.as_key().unwrap().item_id.clone();
1944
1945 let meta = ItemMetadata::Cert(CertMetadata {
1946 subject_key_id: subject_id,
1947 signing_key_id: signing_id,
1948 retrieved_from: None,
1949 is_generated: true,
1950 });
1951
1952 let mut rng = FakeEntropicRng(testing_rng());
1958 let keypair = ed25519::Keypair::generate(&mut rng);
1959 let encoded_cert = Ed25519Cert::constructor()
1960 .cert_type(tor_cert::CertType::IDENTITY_V_SIGNING)
1961 .expiration(SystemTime::now() + Duration::from_secs(180))
1962 .signing_key(keypair.public_key().into())
1963 .cert_key(CertifiedKey::Ed25519(keypair.public_key().into()))
1964 .encode_and_sign(&keypair)
1965 .unwrap();
1966 let test_cert = CertData::TorEd25519Cert(encoded_cert);
1967 AlwaysValidCert(TestItem {
1968 item: KeystoreItem::Cert(test_cert),
1969 meta,
1970 })
1971 };
1972
1973 let res = mgr
1974 .get_or_generate_key_and_cert::<TestItem, AlwaysValidCert>(
1975 &spec,
1976 &make_certificate,
1977 KeystoreSelector::Primary,
1978 &mut rng,
1979 );
1980
1981 #[allow(unused_assignments)]
1982 #[allow(unused_mut)]
1983 let mut has_error = false;
1984 $(
1985 has_error = true;
1986 let err = res.clone().unwrap_err();
1987 assert!(
1988 matches!(
1989 err,
1990 crate::Error::Corruption(KeystoreCorruptionError::$expected_err)
1991 ),
1992 "unexpected error: {err:?}",
1993 );
1994 )?
1995
1996 if !has_error {
1997 let (key, cert) = res.unwrap();
1998
1999 let expected_subj_key_id = if $generate_subject_key == Yes {
2000 "generated_test_key"
2001 } else {
2002 "generated_test_key"
2003 };
2004
2005 assert_eq!(key.meta.item_id(), expected_subj_key_id);
2006 assert_eq!(
2007 cert.0.meta.as_cert().unwrap().subject_key_id,
2008 expected_subj_key_id
2009 );
2010 assert_eq!(
2011 cert.0.meta.as_cert().unwrap().signing_key_id,
2012 "generated_test_key"
2013 );
2014 assert_eq!(cert.0.meta.is_generated(), true);
2015 }
2016 }}
2017 }
2018
2019 #[test]
2020 #[cfg(feature = "experimental-api")]
2021 #[rustfmt::skip] #[allow(clippy::cognitive_complexity)] fn get_certificate() {
2024 run_certificate_test!(
2025 generate_subject_key = No,
2026 generate_signing_key = No,
2027 MissingSigningKey
2028 );
2029
2030 run_certificate_test!(
2031 generate_subject_key = Yes,
2032 generate_signing_key = No,
2033 MissingSigningKey
2034 );
2035
2036 run_certificate_test!(
2037 generate_subject_key = No,
2038 generate_signing_key = Yes,
2039 );
2040
2041 run_certificate_test!(
2042 generate_subject_key = Yes,
2043 generate_signing_key = Yes,
2044 );
2045 }
2046}