1use crate::raw::{RawEntryId, RawKeystoreEntry};
6use crate::{
7 ArtiPath, BoxedKeystore, KeyCertificateSpecifier, KeyPath, KeyPathError, KeyPathInfo,
8 KeyPathInfoExtractor, KeyPathPattern, KeySpecifier, KeystoreCorruptionError,
9 KeystoreEntryResult, KeystoreId, 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 #[cfg_attr(docsrs, doc(cfg(feature = "onion-service-cli-extra")))]
100 pub fn raw_entry(&self) -> RawKeystoreEntry {
101 RawKeystoreEntry::new(self.raw_id.clone(), self.keystore_id.clone())
102 }
103}
104
105impl<'a> From<KeystoreEntry<'a>> for KeystoreEntryResult<KeystoreEntry<'a>> {
110 fn from(val: KeystoreEntry<'a>) -> Self {
111 Ok(val)
112 }
113}
114
115impl KeyMgrBuilder {
116 pub fn build(self) -> StdResult<KeyMgr, KeyMgrBuilderError> {
118 use itertools::Itertools as _;
119
120 let mut keymgr = self.build_unvalidated()?;
121
122 if !keymgr.all_stores().map(|s| s.id()).all_unique() {
123 return Err(KeyMgrBuilderError::ValidationError(
124 "the keystore IDs are not pairwise unique".into(),
125 ));
126 }
127
128 keymgr.key_info_extractors = inventory::iter::<&'static dyn KeyPathInfoExtractor>
129 .into_iter()
130 .copied()
131 .collect();
132
133 Ok(keymgr)
134 }
135}
136
137impl KeyMgrBuilder {
142 pub fn secondary_stores(&mut self) -> &mut Vec<BoxedKeystore> {
148 self.secondary_stores.get_or_insert(Default::default())
149 }
150
151 pub fn set_secondary_stores(mut self, list: Vec<BoxedKeystore>) -> Self {
153 self.secondary_stores = Some(list);
154 self
155 }
156
157 pub fn opt_secondary_stores(&self) -> &Option<Vec<BoxedKeystore>> {
161 &self.secondary_stores
162 }
163
164 pub fn opt_secondary_stores_mut(&mut self) -> &mut Option<Vec<BoxedKeystore>> {
168 &mut self.secondary_stores
169 }
170}
171
172inventory::collect!(&'static dyn crate::KeyPathInfoExtractor);
173
174impl KeyMgr {
175 pub fn get<K: ToEncodableKey>(&self, key_spec: &dyn KeySpecifier) -> Result<Option<K>> {
182 let result = self.get_from_store(key_spec, &K::Key::item_type(), self.all_stores())?;
183 if result.is_none() {
184 if let Some(key_pair_spec) = key_spec.keypair_specifier() {
187 return Ok(self.get::<K::KeyPair>(&*key_pair_spec)?.map(|k| k.into()));
188 }
189 }
190 Ok(result)
191 }
192
193 pub fn get_entry<K: ToEncodableKey>(&self, entry: &KeystoreEntry) -> Result<Option<K>> {
201 let selector = entry.keystore_id().into();
202 let store = self.select_keystore(&selector)?;
203 self.get_from_store(entry.key_path(), entry.key_type(), [store].into_iter())
204 }
205
206 pub fn get_or_generate<K>(
218 &self,
219 key_spec: &dyn KeySpecifier,
220 selector: KeystoreSelector,
221 rng: &mut dyn KeygenRng,
222 ) -> Result<K>
223 where
224 K: ToEncodableKey,
225 K::Key: Keygen,
226 {
227 match self.get(key_spec)? {
228 Some(k) => Ok(k),
229 None => self.generate(key_spec, selector, rng, false),
230 }
231 }
232
233 #[cfg(feature = "onion-service-cli-extra")]
239 pub fn get_from<K: ToEncodableKey>(
240 &self,
241 key_spec: &dyn KeySpecifier,
242 keystore_id: &KeystoreId,
243 ) -> Result<Option<K>> {
244 let store = std::iter::once(self.find_keystore(keystore_id)?);
245 self.get_from_store(key_spec, &K::Key::item_type(), store)
246 }
247
248 pub fn generate<K>(
269 &self,
270 key_spec: &dyn KeySpecifier,
271 selector: KeystoreSelector,
272 rng: &mut dyn KeygenRng,
273 overwrite: bool,
274 ) -> Result<K>
275 where
276 K: ToEncodableKey,
277 K::Key: Keygen,
278 {
279 let store = self.select_keystore(&selector)?;
280
281 if overwrite || !store.contains(key_spec, &K::Key::item_type())? {
282 let key = K::Key::generate(rng)?;
283 store.insert(&key, key_spec)?;
284
285 Ok(K::from_encodable_key(key))
286 } else {
287 Err(crate::Error::KeyAlreadyExists)
288 }
289 }
290
291 pub fn insert<K: ToEncodableKey>(
304 &self,
305 key: K,
306 key_spec: &dyn KeySpecifier,
307 selector: KeystoreSelector,
308 overwrite: bool,
309 ) -> Result<Option<K>> {
310 let key = key.to_encodable_key();
311 let store = self.select_keystore(&selector)?;
312 let key_type = K::Key::item_type();
313 let old_key: Option<K> = self.get_from_store(key_spec, &key_type, [store].into_iter())?;
314
315 if old_key.is_some() && !overwrite {
316 Err(crate::Error::KeyAlreadyExists)
317 } else {
318 let () = store.insert(&key, key_spec)?;
319 Ok(old_key)
320 }
321 }
322
323 pub fn remove<K: ToEncodableKey>(
334 &self,
335 key_spec: &dyn KeySpecifier,
336 selector: KeystoreSelector,
337 ) -> Result<Option<K>> {
338 let store = self.select_keystore(&selector)?;
339 let key_type = K::Key::item_type();
340 let old_key: Option<K> = self.get_from_store(key_spec, &key_type, [store].into_iter())?;
341
342 store.remove(key_spec, &key_type)?;
343
344 Ok(old_key)
345 }
346
347 pub fn remove_entry(&self, entry: &KeystoreEntry) -> Result<Option<()>> {
359 let selector = entry.keystore_id().into();
360 let store = self.select_keystore(&selector)?;
361
362 store.remove(entry.key_path(), entry.key_type())
363 }
364
365 #[cfg(feature = "onion-service-cli-extra")]
373 pub fn remove_unchecked(&self, raw_id: &str, keystore_id: &KeystoreId) -> Result<()> {
374 let selector = KeystoreSelector::from(keystore_id);
375 let store = self.select_keystore(&selector)?;
376 let raw_id = store.raw_entry_id(raw_id)?;
377 let store = self.select_keystore(&selector)?;
378 store.remove_unchecked(&raw_id)
379 }
380
381 pub fn list_matching(&self, pat: &KeyPathPattern) -> Result<Vec<KeystoreEntry>> {
389 self.all_stores()
390 .map(|store| -> Result<Vec<_>> {
391 Ok(store
392 .list()?
393 .into_iter()
394 .filter_map(|entry| entry.ok())
395 .filter(|entry| entry.key_path().matches(pat))
396 .collect::<Vec<_>>())
397 })
398 .flatten_ok()
399 .collect::<Result<Vec<_>>>()
400 }
401
402 #[cfg(feature = "onion-service-cli-extra")]
404 pub fn list_by_id(&self, id: &KeystoreId) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
405 self.find_keystore(id)?.list()
406 }
407
408 #[cfg(feature = "onion-service-cli-extra")]
410 pub fn list(&self) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
411 self.all_stores()
412 .map(|store| -> Result<Vec<_>> { store.list() })
413 .flatten_ok()
414 .collect::<Result<Vec<_>>>()
415 }
416
417 #[cfg(feature = "onion-service-cli-extra")]
419 pub fn list_keystores(&self) -> Vec<KeystoreId> {
420 self.all_stores()
421 .map(|store| store.id().to_owned())
422 .collect()
423 }
424
425 pub fn describe(&self, path: &KeyPath) -> StdResult<KeyPathInfo, KeyPathError> {
434 for info_extractor in &self.key_info_extractors {
435 if let Ok(info) = info_extractor.describe(path) {
436 return Ok(info);
437 }
438 }
439
440 Err(KeyPathError::Unrecognized(path.clone()))
441 }
442
443 fn get_from_store_raw<'a, K: ItemType>(
449 &self,
450 key_spec: &dyn KeySpecifier,
451 key_type: &KeystoreItemType,
452 stores: impl Iterator<Item = &'a BoxedKeystore>,
453 ) -> Result<Option<K>> {
454 let static_key_type = K::item_type();
455 if key_type != &static_key_type {
456 return Err(internal!(
457 "key type {:?} does not match the key type {:?} of requested key K::Key",
458 key_type,
459 static_key_type
460 )
461 .into());
462 }
463
464 for store in stores {
465 let key = match store.get(key_spec, &K::item_type()) {
466 Ok(None) => {
467 continue;
469 }
470 Ok(Some(k)) => k,
471 Err(e) => {
472 return Err(e);
474 }
475 };
476
477 let key: K = key
479 .downcast::<K>()
480 .map(|k| *k)
481 .map_err(|_| internal!("failed to downcast key to requested type"))?;
482
483 return Ok(Some(key));
484 }
485
486 Ok(None)
487 }
488
489 fn get_from_store<'a, K: ToEncodableKey>(
493 &self,
494 key_spec: &dyn KeySpecifier,
495 key_type: &KeystoreItemType,
496 stores: impl Iterator<Item = &'a BoxedKeystore>,
497 ) -> Result<Option<K>> {
498 let Some(key) = self.get_from_store_raw::<K::Key>(key_spec, key_type, stores)? else {
499 return Ok(None);
500 };
501
502 Ok(Some(K::from_encodable_key(key)))
503 }
504
505 #[cfg(feature = "experimental-api")]
521 pub fn get_key_and_cert<K, C>(
522 &self,
523 spec: &dyn KeyCertificateSpecifier,
524 ) -> Result<Option<(K, C)>>
525 where
526 K: ToEncodableKey,
527 C: ToEncodableCert<K>,
528 {
529 let subject_key_spec = spec.subject_key_specifier();
530 let Some(key) =
532 self.get_from_store::<K>(subject_key_spec, &K::Key::item_type(), self.all_stores())?
533 else {
534 return Ok(None);
535 };
536
537 let subject_key_arti_path = subject_key_spec
538 .arti_path()
539 .map_err(|_| bad_api_usage!("subject key does not have an ArtiPath?!"))?;
540 let cert_spec =
541 ArtiPath::from_path_and_denotators(subject_key_arti_path, &spec.cert_denotators())
542 .map_err(into_bad_api_usage!("invalid certificate specifier"))?;
543
544 let Some(cert) = self.get_from_store_raw::<C::ParsedCert>(
545 &cert_spec,
546 &<C::ParsedCert as ItemType>::item_type(),
547 self.all_stores(),
548 )?
549 else {
550 return Err(KeystoreCorruptionError::MissingCertificate.into());
551 };
552
553 let signed_with = self.get_cert_signing_key::<K, C>(spec)?;
555 let cert = C::validate(cert, &key, &signed_with)?;
556
557 Ok(Some((key, cert)))
558 }
559
560 #[cfg(feature = "experimental-api")]
596 pub fn get_or_generate_key_and_cert<K, C>(
597 &self,
598 spec: &dyn KeyCertificateSpecifier,
599 make_certificate: impl FnOnce(&K, &<C as ToEncodableCert<K>>::SigningKey) -> C,
600 selector: KeystoreSelector,
601 rng: &mut dyn KeygenRng,
602 ) -> Result<(K, C)>
603 where
604 K: ToEncodableKey,
605 K::Key: Keygen,
606 C: ToEncodableCert<K>,
607 {
608 let subject_key_spec = spec.subject_key_specifier();
609 let subject_key_arti_path = subject_key_spec
610 .arti_path()
611 .map_err(|_| bad_api_usage!("subject key does not have an ArtiPath?!"))?;
612
613 let cert_specifier =
614 ArtiPath::from_path_and_denotators(subject_key_arti_path, &spec.cert_denotators())
615 .map_err(into_bad_api_usage!("invalid certificate specifier"))?;
616
617 let maybe_cert = self.get_from_store_raw::<C::ParsedCert>(
618 &cert_specifier,
619 &C::ParsedCert::item_type(),
620 self.all_stores(),
621 )?;
622
623 let maybe_subject_key = self.get::<K>(subject_key_spec)?;
624
625 match (&maybe_cert, &maybe_subject_key) {
626 (Some(_), None) => {
627 return Err(KeystoreCorruptionError::MissingSubjectKey.into());
628 }
629 _ => {
630 }
632 }
633 let subject_key = match maybe_subject_key {
634 Some(key) => key,
635 _ => self.generate(subject_key_spec, selector, rng, false)?,
636 };
637
638 let signed_with = self.get_cert_signing_key::<K, C>(spec)?;
639 let cert = match maybe_cert {
640 Some(cert) => C::validate(cert, &subject_key, &signed_with)?,
641 None => {
642 let cert = make_certificate(&subject_key, &signed_with);
643
644 let () = self.insert_cert(cert.clone(), &cert_specifier, selector)?;
645
646 cert
647 }
648 };
649
650 Ok((subject_key, cert))
651 }
652
653 fn all_stores(&self) -> impl Iterator<Item = &BoxedKeystore> {
655 iter::once(&self.primary_store).chain(self.secondary_stores.iter())
656 }
657
658 fn select_keystore(&self, selector: &KeystoreSelector) -> Result<&BoxedKeystore> {
663 match selector {
664 KeystoreSelector::Id(keystore_id) => self.find_keystore(keystore_id),
665 KeystoreSelector::Primary => Ok(&self.primary_store),
666 }
667 }
668
669 fn find_keystore(&self, id: &KeystoreId) -> Result<&BoxedKeystore> {
674 self.all_stores()
675 .find(|keystore| keystore.id() == id)
676 .ok_or_else(|| crate::Error::KeystoreNotFound(id.clone()))
677 }
678
679 #[cfg(feature = "experimental-api")]
684 fn get_cert_signing_key<K, C>(
685 &self,
686 spec: &dyn KeyCertificateSpecifier,
687 ) -> Result<C::SigningKey>
688 where
689 K: ToEncodableKey,
690 C: ToEncodableCert<K>,
691 {
692 let Some(signing_key_spec) = spec.signing_key_specifier() else {
693 return Err(bad_api_usage!(
694 "signing key specifier is None, but external signing key was not provided?"
695 )
696 .into());
697 };
698
699 let Some(signing_key) = self.get_from_store::<C::SigningKey>(
700 signing_key_spec,
701 &<C::SigningKey as ToEncodableKey>::Key::item_type(),
702 self.all_stores(),
703 )?
704 else {
705 return Err(KeystoreCorruptionError::MissingSigningKey.into());
706 };
707
708 Ok(signing_key)
709 }
710
711 fn insert_cert<K, C>(
718 &self,
719 cert: C,
720 cert_spec: &dyn KeySpecifier,
721 selector: KeystoreSelector,
722 ) -> Result<()>
723 where
724 K: ToEncodableKey,
725 K::Key: Keygen,
726 C: ToEncodableCert<K>,
727 {
728 let cert = cert.to_encodable_cert();
729 let store = self.select_keystore(&selector)?;
730
731 let () = store.insert(&cert, cert_spec)?;
732 Ok(())
733 }
734}
735
736#[cfg(test)]
737mod tests {
738 #![allow(clippy::bool_assert_comparison)]
740 #![allow(clippy::clone_on_copy)]
741 #![allow(clippy::dbg_macro)]
742 #![allow(clippy::mixed_attributes_style)]
743 #![allow(clippy::print_stderr)]
744 #![allow(clippy::print_stdout)]
745 #![allow(clippy::single_char_pattern)]
746 #![allow(clippy::unwrap_used)]
747 #![allow(clippy::unchecked_duration_subtraction)]
748 #![allow(clippy::useless_vec)]
749 #![allow(clippy::needless_pass_by_value)]
750 use super::*;
752 use crate::keystore::arti::err::{ArtiNativeKeystoreError, MalformedPathError};
753 use crate::raw::{RawEntryId, RawKeystoreEntry};
754 use crate::{
755 ArtiPath, ArtiPathUnavailableError, Error, KeyPath, KeystoreEntryResult, KeystoreError,
756 UnrecognizedEntryError,
757 };
758 use std::path::PathBuf;
759 use std::result::Result as StdResult;
760 use std::str::FromStr;
761 use std::sync::{Arc, RwLock};
762 use std::time::{Duration, SystemTime};
763 use tor_basic_utils::test_rng::testing_rng;
764 use tor_cert::CertifiedKey;
765 use tor_cert::Ed25519Cert;
766 use tor_error::{ErrorKind, HasKind};
767 use tor_key_forge::{
768 CertData, EncodableItem, ErasedKey, InvalidCertError, KeyType, KeystoreItem,
769 };
770 use tor_llcrypto::pk::ed25519::{self, Ed25519PublicKey as _};
771 use tor_llcrypto::rng::FakeEntropicRng;
772
773 #[derive(Clone, Debug, PartialEq)]
775 struct KeyMetadata {
776 item_id: String,
778 retrieved_from: Option<KeystoreId>,
782 is_generated: bool,
784 }
785
786 #[derive(Clone, Debug, PartialEq)]
788 struct CertMetadata {
789 subject_key_id: String,
791 signing_key_id: String,
793 retrieved_from: Option<KeystoreId>,
797 is_generated: bool,
800 }
801
802 #[derive(Clone, Debug, PartialEq, derive_more::From)]
804 enum ItemMetadata {
805 Key(KeyMetadata),
807 Cert(CertMetadata),
809 }
810
811 impl ItemMetadata {
812 fn item_id(&self) -> &str {
817 match self {
818 ItemMetadata::Key(k) => &k.item_id,
819 ItemMetadata::Cert(c) => &c.subject_key_id,
820 }
821 }
822
823 fn retrieved_from(&self) -> Option<&KeystoreId> {
825 match self {
826 ItemMetadata::Key(k) => k.retrieved_from.as_ref(),
827 ItemMetadata::Cert(c) => c.retrieved_from.as_ref(),
828 }
829 }
830
831 fn is_generated(&self) -> bool {
833 match self {
834 ItemMetadata::Key(k) => k.is_generated,
835 ItemMetadata::Cert(c) => c.is_generated,
836 }
837 }
838
839 fn set_retrieved_from(&mut self, id: KeystoreId) {
841 match self {
842 ItemMetadata::Key(meta) => meta.retrieved_from = Some(id),
843 ItemMetadata::Cert(meta) => meta.retrieved_from = Some(id),
844 }
845 }
846
847 fn as_key(&self) -> Option<&KeyMetadata> {
849 match self {
850 ItemMetadata::Key(meta) => Some(meta),
851 _ => None,
852 }
853 }
854
855 fn as_cert(&self) -> Option<&CertMetadata> {
857 match self {
858 ItemMetadata::Cert(meta) => Some(meta),
859 _ => None,
860 }
861 }
862 }
863
864 #[derive(Clone, Debug)]
866 struct TestItem {
867 item: KeystoreItem,
869 meta: ItemMetadata,
871 }
872
873 #[derive(Clone, Debug)]
875 struct AlwaysValidCert(TestItem);
876
877 #[derive(Clone, Debug)]
879 struct TestPublicKey {
880 key: KeystoreItem,
882 }
883
884 impl From<TestItem> for TestPublicKey {
885 fn from(tk: TestItem) -> TestPublicKey {
886 TestPublicKey { key: tk.item }
887 }
888 }
889
890 impl TestItem {
891 fn new(item_id: &str) -> Self {
893 let mut rng = testing_rng();
894 TestItem {
895 item: ed25519::Keypair::generate(&mut rng)
896 .as_keystore_item()
897 .unwrap(),
898 meta: ItemMetadata::Key(KeyMetadata {
899 item_id: item_id.to_string(),
900 retrieved_from: None,
901 is_generated: false,
902 }),
903 }
904 }
905 }
906
907 impl Keygen for TestItem {
908 fn generate(mut rng: &mut dyn KeygenRng) -> tor_key_forge::Result<Self>
909 where
910 Self: Sized,
911 {
912 Ok(TestItem {
913 item: ed25519::Keypair::generate(&mut rng).as_keystore_item()?,
914 meta: ItemMetadata::Key(KeyMetadata {
915 item_id: "generated_test_key".to_string(),
916 retrieved_from: None,
917 is_generated: true,
918 }),
919 })
920 }
921 }
922
923 impl ItemType for TestItem {
924 fn item_type() -> KeystoreItemType
925 where
926 Self: Sized,
927 {
928 KeyType::Ed25519Keypair.into()
930 }
931 }
932
933 impl EncodableItem for TestItem {
934 fn as_keystore_item(&self) -> tor_key_forge::Result<KeystoreItem> {
935 Ok(self.item.clone())
936 }
937 }
938
939 impl ToEncodableKey for TestItem {
940 type Key = Self;
941 type KeyPair = Self;
942
943 fn to_encodable_key(self) -> Self::Key {
944 self
945 }
946
947 fn from_encodable_key(key: Self::Key) -> Self {
948 key
949 }
950 }
951
952 impl ItemType for TestPublicKey {
953 fn item_type() -> KeystoreItemType
954 where
955 Self: Sized,
956 {
957 KeyType::Ed25519PublicKey.into()
958 }
959 }
960
961 impl EncodableItem for TestPublicKey {
962 fn as_keystore_item(&self) -> tor_key_forge::Result<KeystoreItem> {
963 Ok(self.key.clone())
964 }
965 }
966
967 impl ToEncodableKey for TestPublicKey {
968 type Key = Self;
969 type KeyPair = TestItem;
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 ToEncodableCert<TestItem> for AlwaysValidCert {
981 type ParsedCert = TestItem;
982 type EncodableCert = TestItem;
983 type SigningKey = TestItem;
984
985 fn validate(
986 cert: Self::ParsedCert,
987 _subject: &TestItem,
988 _signed_with: &Self::SigningKey,
989 ) -> StdResult<Self, InvalidCertError> {
990 Ok(Self(cert))
992 }
993
994 fn to_encodable_cert(self) -> Self::EncodableCert {
996 self.0
997 }
998 }
999
1000 #[derive(thiserror::Error, Debug, Clone, derive_more::Display)]
1001 enum MockKeystoreError {
1002 NotFound,
1003 }
1004
1005 impl KeystoreError for MockKeystoreError {}
1006
1007 impl HasKind for MockKeystoreError {
1008 fn kind(&self) -> ErrorKind {
1009 tor_error::ErrorKind::Other
1011 }
1012 }
1013
1014 fn build_raw_id_path<T: ToString>(key_path: &T, key_type: &KeystoreItemType) -> RawEntryId {
1015 let mut path = key_path.to_string();
1016 path.push('.');
1017 path.push_str(&key_type.arti_extension());
1018 RawEntryId::Path(PathBuf::from(&path))
1019 }
1020
1021 macro_rules! impl_keystore {
1022 ($name:tt, $id:expr $(,$unrec:expr)?) => {
1023 struct $name {
1024 inner: RwLock<
1025 Vec<StdResult<(ArtiPath, KeystoreItemType, TestItem), UnrecognizedEntryError>>,
1026 >,
1027 id: KeystoreId,
1028 }
1029
1030 impl Default for $name {
1031 fn default() -> Self {
1032 let id = KeystoreId::from_str($id).unwrap();
1033 let inner: RwLock<
1034 Vec<
1035 StdResult<
1036 (ArtiPath, KeystoreItemType, TestItem),
1037 UnrecognizedEntryError,
1038 >,
1039 >,
1040 > = Default::default();
1041 $(
1044 for i in 0..$unrec {
1045 let invalid_key_path =
1046 PathBuf::from(&format!("unrecognized_entry{}", i));
1047 let raw_id = RawEntryId::Path(invalid_key_path.clone());
1048 let entry = RawKeystoreEntry::new(raw_id, id.clone()).into();
1049 let entry = UnrecognizedEntryError::new(
1050 entry,
1051 Arc::new(ArtiNativeKeystoreError::MalformedPath {
1052 path: invalid_key_path,
1053 err: MalformedPathError::NoExtension,
1054 }),
1055 );
1056 inner.write().unwrap().push(Err(entry));
1057 }
1058 )?
1059 Self {
1060 inner,
1061 id,
1062 }
1063 }
1064 }
1065
1066 #[allow(dead_code)] impl $name {
1068 fn new_boxed() -> BoxedKeystore {
1069 Box::<Self>::default()
1070 }
1071 }
1072
1073 impl crate::Keystore for $name {
1074 fn contains(
1075 &self,
1076 key_spec: &dyn KeySpecifier,
1077 item_type: &KeystoreItemType,
1078 ) -> Result<bool> {
1079 let wanted_arti_path = key_spec.arti_path().unwrap();
1080 Ok(self
1081 .inner
1082 .read()
1083 .unwrap()
1084 .iter()
1085 .find(|res| match res {
1086 Ok((spec, ty, _)) => spec == &wanted_arti_path && ty == item_type,
1087 Err(_) => false,
1088 })
1089 .is_some())
1090 }
1091
1092 fn id(&self) -> &KeystoreId {
1093 &self.id
1094 }
1095
1096 fn get(
1097 &self,
1098 key_spec: &dyn KeySpecifier,
1099 item_type: &KeystoreItemType,
1100 ) -> Result<Option<ErasedKey>> {
1101 let key_spec = key_spec.arti_path().unwrap();
1102
1103 Ok(self.inner.read().unwrap().iter().find_map(|res| {
1104 match res {
1105 Ok((arti_path, ty, k)) => {
1106 if arti_path == &key_spec && ty == item_type {
1107 let mut k = k.clone();
1108 k.meta.set_retrieved_from(self.id().clone());
1109 return Some(Box::new(k) as Box<dyn ItemType>);
1110 }
1111 }
1112 Err(_) => {}
1113 }
1114 None
1115 }))
1116 }
1117
1118 #[cfg(feature = "onion-service-cli-extra")]
1119 fn raw_entry_id(&self, raw_id: &str) -> Result<RawEntryId> {
1120 Ok(RawEntryId::Path(
1121 PathBuf::from(raw_id.to_string()),
1122 ))
1123 }
1124
1125 fn insert(
1126 &self,
1127 key: &dyn EncodableItem,
1128 key_spec: &dyn KeySpecifier,
1129 ) -> Result<()> {
1130 let key = key.downcast_ref::<TestItem>().unwrap();
1131
1132 let item = key.as_keystore_item()?;
1133 let meta = key.meta.clone();
1134
1135 let item_type = item.item_type()?;
1136 let key = TestItem { item, meta };
1137
1138 self.inner
1139 .write()
1140 .unwrap()
1141 .insert(0, (Ok((key_spec.arti_path().unwrap(), item_type, key))));
1146
1147 Ok(())
1148 }
1149
1150 fn remove(
1151 &self,
1152 key_spec: &dyn KeySpecifier,
1153 item_type: &KeystoreItemType,
1154 ) -> Result<Option<()>> {
1155 let wanted_arti_path = key_spec.arti_path().unwrap();
1156 let index = self.inner.read().unwrap().iter().position(|res| {
1157 if let Ok((arti_path, ty, _)) = res {
1158 arti_path == &wanted_arti_path && ty == item_type
1159 } else {
1160 false
1161 }
1162 });
1163 let Some(index) = index else {
1164 return Ok(None);
1165 };
1166 let _ = self.inner.write().unwrap().remove(index);
1167
1168 Ok(Some(()))
1169 }
1170
1171 #[cfg(feature = "onion-service-cli-extra")]
1172 fn remove_unchecked(&self, entry_id: &RawEntryId) -> Result<()> {
1173 let index = self.inner.read().unwrap().iter().position(|res| match res {
1174 Ok((spec, ty, _)) => {
1175 let id = build_raw_id_path(spec, ty);
1176 entry_id == &id
1177 }
1178 Err(e) => {
1179 e.entry().raw_id() == entry_id
1180 }
1181 });
1182 let Some(index) = index else {
1183 return Err(Error::Keystore(Arc::new(MockKeystoreError::NotFound)));
1184 };
1185 let _ = self.inner.write().unwrap().remove(index);
1186 Ok(())
1187 }
1188
1189 fn list(&self) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
1190 Ok(self
1191 .inner
1192 .read()
1193 .unwrap()
1194 .iter()
1195 .map(|res| match res {
1196 Ok((arti_path, ty, _)) => {
1197 let raw_id = RawEntryId::Path(
1198 PathBuf::from(
1199 &arti_path.to_string(),
1200 )
1201 );
1202
1203 Ok(KeystoreEntry::new(KeyPath::Arti(arti_path.clone()), ty.clone(), self.id(), raw_id))
1204 }
1205 Err(e) => Err(e.clone()),
1206 })
1207 .collect())
1208 }
1209 }
1210 };
1211 }
1212
1213 macro_rules! impl_specifier {
1214 ($name:tt, $id:expr) => {
1215 struct $name;
1216
1217 impl KeySpecifier for $name {
1218 fn arti_path(&self) -> StdResult<ArtiPath, ArtiPathUnavailableError> {
1219 Ok(ArtiPath::new($id.into()).map_err(|e| tor_error::internal!("{e}"))?)
1220 }
1221
1222 fn ctor_path(&self) -> Option<crate::CTorPath> {
1223 None
1224 }
1225
1226 fn keypair_specifier(&self) -> Option<Box<dyn KeySpecifier>> {
1227 None
1228 }
1229 }
1230 };
1231 }
1232
1233 impl_keystore!(Keystore1, "keystore1");
1234 impl_keystore!(Keystore2, "keystore2");
1235 impl_keystore!(Keystore3, "keystore3");
1236 impl_keystore!(KeystoreUnrec1, "keystore_unrec1", 1);
1237
1238 impl_specifier!(TestKeySpecifier1, "spec1");
1239 impl_specifier!(TestKeySpecifier2, "spec2");
1240 impl_specifier!(TestKeySpecifier3, "spec3");
1241 impl_specifier!(TestKeySpecifier4, "spec4");
1242
1243 impl_specifier!(TestPublicKeySpecifier1, "pub-spec1");
1244
1245 fn entry_descriptor(specifier: impl KeySpecifier, keystore_id: &KeystoreId) -> KeystoreEntry {
1247 let arti_path = specifier.arti_path().unwrap();
1248 let raw_id = RawEntryId::Path(PathBuf::from(arti_path.as_ref()));
1249 KeystoreEntry {
1250 key_path: arti_path.into(),
1251 key_type: TestItem::item_type(),
1252 keystore_id,
1253 raw_id,
1254 }
1255 }
1256
1257 #[test]
1258 #[allow(clippy::cognitive_complexity)]
1259 fn insert_and_get() {
1260 let mut builder = KeyMgrBuilder::default().primary_store(Box::<Keystore1>::default());
1261
1262 builder
1263 .secondary_stores()
1264 .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
1265
1266 let mgr = builder.build().unwrap();
1267
1268 let old_key = mgr
1270 .insert(
1271 TestItem::new("coot"),
1272 &TestKeySpecifier1,
1273 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1274 true,
1275 )
1276 .unwrap();
1277
1278 assert!(old_key.is_none());
1279 let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1280 assert_eq!(key.meta.item_id(), "coot");
1281 assert_eq!(
1282 key.meta.retrieved_from(),
1283 Some(&KeystoreId::from_str("keystore2").unwrap())
1284 );
1285 assert_eq!(key.meta.is_generated(), false);
1286
1287 let old_key = mgr
1289 .insert(
1290 TestItem::new("gull"),
1291 &TestKeySpecifier1,
1292 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1293 true,
1294 )
1295 .unwrap()
1296 .unwrap();
1297 assert_eq!(old_key.meta.item_id(), "coot");
1298 assert_eq!(
1299 old_key.meta.retrieved_from(),
1300 Some(&KeystoreId::from_str("keystore2").unwrap())
1301 );
1302 assert_eq!(old_key.meta.is_generated(), false);
1303 let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1305 assert_eq!(key.meta.item_id(), "gull");
1306 assert_eq!(
1307 key.meta.retrieved_from(),
1308 Some(&KeystoreId::from_str("keystore2").unwrap())
1309 );
1310 assert_eq!(key.meta.is_generated(), false);
1311
1312 let err = mgr
1314 .insert(
1315 TestItem::new("gull"),
1316 &TestKeySpecifier1,
1317 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1318 false,
1319 )
1320 .unwrap_err();
1321 assert!(matches!(err, crate::Error::KeyAlreadyExists));
1322
1323 let old_key = mgr
1325 .insert(
1326 TestItem::new("penguin"),
1327 &TestKeySpecifier2,
1328 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1329 false,
1330 )
1331 .unwrap();
1332 assert!(old_key.is_none());
1333
1334 let old_key = mgr
1336 .insert(
1337 TestItem::new("moorhen"),
1338 &TestKeySpecifier3,
1339 KeystoreSelector::Primary,
1340 true,
1341 )
1342 .unwrap();
1343 assert!(old_key.is_none());
1344 let key = mgr.get::<TestItem>(&TestKeySpecifier3).unwrap().unwrap();
1345 assert_eq!(key.meta.item_id(), "moorhen");
1346 assert_eq!(
1347 key.meta.retrieved_from(),
1348 Some(&KeystoreId::from_str("keystore1").unwrap())
1349 );
1350 assert_eq!(key.meta.is_generated(), false);
1351
1352 assert!(mgr.get::<TestItem>(&TestKeySpecifier4).unwrap().is_none());
1354
1355 for store in ["keystore3", "keystore2", "keystore1"] {
1359 let old_key = mgr
1360 .insert(
1361 TestItem::new("cormorant"),
1362 &TestKeySpecifier4,
1363 KeystoreSelector::Id(&KeystoreId::from_str(store).unwrap()),
1364 true,
1365 )
1366 .unwrap();
1367 assert!(old_key.is_none());
1368
1369 let key = mgr.get::<TestItem>(&TestKeySpecifier4).unwrap().unwrap();
1371 assert_eq!(key.meta.item_id(), "cormorant");
1372 assert_eq!(
1373 key.meta.retrieved_from(),
1374 Some(&KeystoreId::from_str(store).unwrap())
1375 );
1376 assert_eq!(key.meta.is_generated(), false);
1377 }
1378
1379 let key = mgr.get::<TestItem>(&TestKeySpecifier4).unwrap().unwrap();
1382 assert_eq!(key.meta.item_id(), "cormorant");
1383 assert_eq!(
1384 key.meta.retrieved_from(),
1385 Some(&KeystoreId::from_str("keystore1").unwrap())
1386 );
1387 assert_eq!(key.meta.is_generated(), false);
1388 }
1389
1390 #[test]
1391 #[cfg(feature = "onion-service-cli-extra")]
1392 fn get_from() {
1393 let mut builder = KeyMgrBuilder::default().primary_store(Box::<Keystore1>::default());
1394
1395 builder
1396 .secondary_stores()
1397 .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
1398
1399 let mgr = builder.build().unwrap();
1400
1401 let keystore1_id = KeystoreId::from_str("keystore1").unwrap();
1402 let keystore2_id = KeystoreId::from_str("keystore2").unwrap();
1403 let key_id_1 = "mantis shrimp";
1404 let key_id_2 = "tardigrade";
1405
1406 let _ = mgr
1408 .insert(
1409 TestItem::new(key_id_1),
1410 &TestKeySpecifier1,
1411 KeystoreSelector::Id(&keystore1_id),
1412 true,
1413 )
1414 .unwrap();
1415
1416 let _ = mgr
1418 .insert(
1419 TestItem::new(key_id_2),
1420 &TestKeySpecifier1,
1421 KeystoreSelector::Id(&keystore2_id),
1422 true,
1423 )
1424 .unwrap();
1425
1426 let key = mgr
1428 .get_from::<TestItem>(&TestKeySpecifier1, &keystore2_id)
1429 .unwrap()
1430 .unwrap();
1431
1432 assert_eq!(key.meta.item_id(), key_id_2);
1433 assert_eq!(key.meta.retrieved_from(), Some(&keystore2_id));
1434 }
1435
1436 #[test]
1437 fn remove() {
1438 let mut builder = KeyMgrBuilder::default().primary_store(Box::<Keystore1>::default());
1439
1440 builder
1441 .secondary_stores()
1442 .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
1443
1444 let mgr = builder.build().unwrap();
1445
1446 assert!(!mgr.secondary_stores[0]
1447 .contains(&TestKeySpecifier1, &TestItem::item_type())
1448 .unwrap());
1449
1450 mgr.insert(
1452 TestItem::new("coot"),
1453 &TestKeySpecifier1,
1454 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1455 true,
1456 )
1457 .unwrap();
1458 let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1459 assert_eq!(key.meta.item_id(), "coot");
1460 assert_eq!(
1461 key.meta.retrieved_from(),
1462 Some(&KeystoreId::from_str("keystore2").unwrap())
1463 );
1464 assert_eq!(key.meta.is_generated(), false);
1465
1466 assert!(mgr
1468 .remove::<TestItem>(
1469 &TestKeySpecifier1,
1470 KeystoreSelector::Id(&KeystoreId::from_str("not_an_id_we_know_of").unwrap())
1471 )
1472 .is_err());
1473 assert!(mgr.secondary_stores[0]
1475 .contains(&TestKeySpecifier1, &TestItem::item_type())
1476 .unwrap());
1477
1478 assert!(mgr
1480 .remove::<TestItem>(&TestKeySpecifier1, KeystoreSelector::Primary)
1481 .unwrap()
1482 .is_none());
1483
1484 assert!(mgr.secondary_stores[0]
1486 .contains(&TestKeySpecifier1, &TestItem::item_type())
1487 .unwrap());
1488
1489 let removed_key = mgr
1491 .remove::<TestItem>(
1492 &TestKeySpecifier1,
1493 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1494 )
1495 .unwrap()
1496 .unwrap();
1497 assert_eq!(removed_key.meta.item_id(), "coot");
1498 assert_eq!(
1499 removed_key.meta.retrieved_from(),
1500 Some(&KeystoreId::from_str("keystore2").unwrap())
1501 );
1502 assert_eq!(removed_key.meta.is_generated(), false);
1503
1504 assert!(!mgr.secondary_stores[0]
1506 .contains(&TestKeySpecifier1, &TestItem::item_type())
1507 .unwrap());
1508 }
1509
1510 #[test]
1511 fn keygen() {
1512 let mut rng = FakeEntropicRng(testing_rng());
1513 let mgr = KeyMgrBuilder::default()
1514 .primary_store(Box::<Keystore1>::default())
1515 .build()
1516 .unwrap();
1517
1518 mgr.insert(
1519 TestItem::new("coot"),
1520 &TestKeySpecifier1,
1521 KeystoreSelector::Primary,
1522 true,
1523 )
1524 .unwrap();
1525
1526 assert!(mgr
1528 .get::<TestPublicKey>(&TestPublicKeySpecifier1)
1529 .unwrap()
1530 .is_none());
1531
1532 let err = mgr
1534 .generate::<TestItem>(
1535 &TestKeySpecifier1,
1536 KeystoreSelector::Primary,
1537 &mut rng,
1538 false,
1539 )
1540 .unwrap_err();
1541
1542 assert!(matches!(err, crate::Error::KeyAlreadyExists));
1543
1544 let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1546 assert_eq!(key.meta.item_id(), "coot");
1547 assert_eq!(
1548 key.meta.retrieved_from(),
1549 Some(&KeystoreId::from_str("keystore1").unwrap())
1550 );
1551 assert_eq!(key.meta.is_generated(), false);
1552
1553 assert!(mgr
1555 .get::<TestPublicKey>(&TestPublicKeySpecifier1)
1556 .unwrap()
1557 .is_none());
1558
1559 let generated_key = mgr
1561 .generate::<TestItem>(
1562 &TestKeySpecifier1,
1563 KeystoreSelector::Primary,
1564 &mut rng,
1565 true,
1566 )
1567 .unwrap();
1568
1569 assert_eq!(generated_key.meta.item_id(), "generated_test_key");
1570 assert_eq!(generated_key.meta.retrieved_from(), None);
1573 assert_eq!(generated_key.meta.is_generated(), true);
1574
1575 let retrieved_key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1577 assert_eq!(retrieved_key.meta.item_id(), "generated_test_key");
1578 assert_eq!(
1579 retrieved_key.meta.retrieved_from(),
1580 Some(&KeystoreId::from_str("keystore1").unwrap())
1581 );
1582 assert_eq!(retrieved_key.meta.is_generated(), true);
1583
1584 assert!(mgr
1586 .get::<TestPublicKey>(&TestPublicKeySpecifier1)
1587 .unwrap()
1588 .is_none());
1589 }
1590
1591 #[test]
1592 fn get_or_generate() {
1593 let mut rng = FakeEntropicRng(testing_rng());
1594 let mut builder = KeyMgrBuilder::default().primary_store(Box::<Keystore1>::default());
1595
1596 builder
1597 .secondary_stores()
1598 .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
1599
1600 let mgr = builder.build().unwrap();
1601
1602 let keystore2 = KeystoreId::from_str("keystore2").unwrap();
1603 let entry_desc1 = entry_descriptor(TestKeySpecifier1, &keystore2);
1604 assert!(mgr.get_entry::<TestItem>(&entry_desc1).unwrap().is_none());
1605
1606 mgr.insert(
1607 TestItem::new("coot"),
1608 &TestKeySpecifier1,
1609 KeystoreSelector::Id(&keystore2),
1610 true,
1611 )
1612 .unwrap();
1613
1614 let key = mgr
1616 .get_or_generate::<TestItem>(&TestKeySpecifier1, KeystoreSelector::Primary, &mut rng)
1617 .unwrap();
1618 assert_eq!(key.meta.item_id(), "coot");
1619 assert_eq!(
1620 key.meta.retrieved_from(),
1621 Some(&KeystoreId::from_str("keystore2").unwrap())
1622 );
1623 assert_eq!(key.meta.is_generated(), false);
1624
1625 assert_eq!(
1626 mgr.get_entry::<TestItem>(&entry_desc1)
1627 .unwrap()
1628 .map(|k| k.meta),
1629 Some(ItemMetadata::Key(KeyMetadata {
1630 item_id: "coot".to_string(),
1631 retrieved_from: Some(keystore2.clone()),
1632 is_generated: false,
1633 }))
1634 );
1635
1636 let keystore3 = KeystoreId::from_str("keystore3").unwrap();
1639 let generated_key = mgr
1640 .get_or_generate::<TestItem>(
1641 &TestKeySpecifier2,
1642 KeystoreSelector::Id(&keystore3),
1643 &mut rng,
1644 )
1645 .unwrap();
1646 assert_eq!(generated_key.meta.item_id(), "generated_test_key");
1647 assert_eq!(generated_key.meta.retrieved_from(), None);
1650 assert_eq!(generated_key.meta.is_generated(), true);
1651
1652 let retrieved_key = mgr.get::<TestItem>(&TestKeySpecifier2).unwrap().unwrap();
1654 assert_eq!(retrieved_key.meta.item_id(), "generated_test_key");
1655 assert_eq!(
1656 retrieved_key.meta.retrieved_from(),
1657 Some(&KeystoreId::from_str("keystore3").unwrap())
1658 );
1659 assert_eq!(retrieved_key.meta.is_generated(), true);
1660
1661 let entry_desc2 = entry_descriptor(TestKeySpecifier2, &keystore3);
1662 assert_eq!(
1663 mgr.get_entry::<TestItem>(&entry_desc2)
1664 .unwrap()
1665 .map(|k| k.meta),
1666 Some(ItemMetadata::Key(KeyMetadata {
1667 item_id: "generated_test_key".to_string(),
1668 retrieved_from: Some(keystore3.clone()),
1669 is_generated: true,
1670 }))
1671 );
1672
1673 let arti_pat = KeyPathPattern::Arti("*".to_string());
1674 let matching = mgr.list_matching(&arti_pat).unwrap();
1675
1676 assert_eq!(matching.len(), 2);
1677 assert!(matching.contains(&entry_desc1));
1678 assert!(matching.contains(&entry_desc2));
1679
1680 assert_eq!(mgr.remove_entry(&entry_desc2).unwrap(), Some(()));
1681 assert!(mgr.get_entry::<TestItem>(&entry_desc2).unwrap().is_none());
1682 assert!(mgr.remove_entry(&entry_desc2).unwrap().is_none());
1683 }
1684
1685 #[test]
1686 fn list_matching_ignores_unrecognized_keys() {
1687 let builder = KeyMgrBuilder::default().primary_store(Box::new(KeystoreUnrec1::default()));
1688
1689 let mgr = builder.build().unwrap();
1690
1691 let unrec_1 = KeystoreId::from_str("keystore_unrec1").unwrap();
1692 mgr.insert(
1693 TestItem::new("whale shark"),
1694 &TestKeySpecifier1,
1695 KeystoreSelector::Id(&unrec_1),
1696 true,
1697 )
1698 .unwrap();
1699
1700 let arti_pat = KeyPathPattern::Arti("*".to_string());
1701 let valid_key_path = KeyPath::Arti(TestKeySpecifier1.arti_path().unwrap());
1702 let matching = mgr.list_matching(&arti_pat).unwrap();
1703 assert_eq!(matching.len(), 1);
1705 assert_eq!(matching.first().unwrap().key_path(), &valid_key_path);
1706 }
1707
1708 #[cfg(feature = "onion-service-cli-extra")]
1709 #[test]
1710 fn keys_subcommands() {
1713 let mut builder =
1714 KeyMgrBuilder::default().primary_store(Box::new(KeystoreUnrec1::default()));
1715 builder
1716 .secondary_stores()
1717 .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
1718
1719 let mgr = builder.build().unwrap();
1720 let ks_unrec1id = KeystoreId::from_str("keystore_unrec1").unwrap();
1721 let keystore2id = KeystoreId::from_str("keystore2").unwrap();
1722 let keystore3id = KeystoreId::from_str("keystore3").unwrap();
1723
1724 let _ = mgr
1726 .insert(
1727 TestItem::new("pangolin"),
1728 &TestKeySpecifier1,
1729 KeystoreSelector::Id(&ks_unrec1id),
1730 true,
1731 )
1732 .unwrap();
1733
1734 let _ = mgr
1736 .insert(
1737 TestItem::new("coot"),
1738 &TestKeySpecifier2,
1739 KeystoreSelector::Id(&keystore2id),
1740 true,
1741 )
1742 .unwrap();
1743
1744 let _ = mgr
1746 .insert(
1747 TestItem::new("penguin"),
1748 &TestKeySpecifier3,
1749 KeystoreSelector::Id(&keystore3id),
1750 true,
1751 )
1752 .unwrap();
1753
1754 let assert_key = |path, ty, expected_path: &ArtiPath, expected_type| {
1755 assert_eq!(ty, expected_type);
1756 assert_eq!(path, &KeyPath::Arti(expected_path.clone()));
1757 };
1758 let item_type = TestItem::new("axolotl").item.item_type().unwrap();
1759 let unrecognized_entry_id = RawEntryId::Path(PathBuf::from("unrecognized_entry0"));
1760
1761 let entries = mgr.list().unwrap();
1763
1764 let expected_items = [
1765 (ks_unrec1id, TestKeySpecifier1.arti_path().unwrap()),
1766 (keystore2id, TestKeySpecifier2.arti_path().unwrap()),
1767 (keystore3id, TestKeySpecifier3.arti_path().unwrap()),
1768 ];
1769
1770 let mut recognized_entries = 0;
1772 let mut unrecognized_entries = 0;
1773 for entry in entries.iter() {
1774 match entry {
1775 Ok(e) => {
1776 if let Some((_, expected_arti_path)) = expected_items
1777 .iter()
1778 .find(|(keystore_id, _)| keystore_id == e.keystore_id())
1779 {
1780 assert_key(e.key_path(), e.key_type(), expected_arti_path, &item_type);
1781 recognized_entries += 1;
1782 continue;
1783 }
1784
1785 panic!("Unexpected key encountered {:?}", e);
1786 }
1787 Err(u) => {
1788 assert_eq!(u.entry().raw_id(), &unrecognized_entry_id);
1789 unrecognized_entries += 1;
1790 }
1791 }
1792 }
1793 assert_eq!(recognized_entries, 3);
1794 assert_eq!(unrecognized_entries, 1);
1795
1796 let keystores = mgr.list_keystores().iter().len();
1798
1799 assert_eq!(keystores, 3);
1800
1801 let primary_keystore_id = KeystoreId::from_str("keystore_unrec1").unwrap();
1803 let entries = mgr.list_by_id(&primary_keystore_id).unwrap();
1804
1805 let mut recognized_entries = 0;
1807 let mut unrecognized_entries = 0;
1808 let mut all_entries = vec![];
1810 for entry in entries.iter() {
1811 match entry {
1812 Ok(entry) => {
1813 assert_key(
1814 entry.key_path(),
1815 entry.key_type(),
1816 &TestKeySpecifier1.arti_path().unwrap(),
1817 &item_type,
1818 );
1819 recognized_entries += 1;
1820 all_entries.push(RawKeystoreEntry::new(
1821 build_raw_id_path(entry.key_path(), entry.key_type()),
1822 primary_keystore_id.clone(),
1823 ));
1824 }
1825 Err(u) => {
1826 assert_eq!(u.entry().raw_id(), &unrecognized_entry_id);
1827 unrecognized_entries += 1;
1828 all_entries.push(u.entry().into());
1829 }
1830 }
1831 }
1832 assert_eq!(recognized_entries, 1);
1833 assert_eq!(unrecognized_entries, 1);
1834
1835 for entry in all_entries {
1837 mgr.remove_unchecked(&entry.raw_id().to_string(), entry.keystore_id())
1838 .unwrap();
1839 }
1840
1841 let entries = mgr.list_by_id(&primary_keystore_id).unwrap();
1843 assert_eq!(entries.len(), 0);
1844 }
1845
1846 #[cfg(feature = "experimental-api")]
1848 #[derive(Clone, Copy, Debug, PartialEq)]
1849 enum GenerateItem {
1850 Yes,
1851 No,
1852 }
1853
1854 #[cfg(feature = "experimental-api")]
1855 macro_rules! run_certificate_test {
1856 (
1857 generate_subject_key = $generate_subject_key:expr,
1858 generate_signing_key = $generate_signing_key:expr,
1859 $($expected_err:tt)?
1860 ) => {{
1861 use GenerateItem::*;
1862
1863 let mut rng = FakeEntropicRng(testing_rng());
1864 let mut builder = KeyMgrBuilder::default().primary_store(Box::<Keystore1>::default());
1865
1866 builder
1867 .secondary_stores()
1868 .extend([Keystore2::new_boxed(), Keystore3::new_boxed()]);
1869
1870 let mgr = builder.build().unwrap();
1871
1872 let spec = crate::test_utils::TestCertSpecifier {
1873 subject_key_spec: TestKeySpecifier1,
1874 signing_key_spec: TestKeySpecifier2,
1875 denotator: vec!["foo".into()],
1876 };
1877
1878 if $generate_subject_key == Yes {
1879 let _ = mgr
1880 .generate::<TestItem>(
1881 &TestKeySpecifier1,
1882 KeystoreSelector::Primary,
1883 &mut rng,
1884 false,
1885 )
1886 .unwrap();
1887 }
1888
1889 if $generate_signing_key == Yes {
1890 let _ = mgr
1891 .generate::<TestItem>(
1892 &TestKeySpecifier2,
1893 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1894 &mut rng,
1895 false,
1896 )
1897 .unwrap();
1898 }
1899
1900 let make_certificate = move |subject_key: &TestItem, signed_with: &TestItem| {
1901 let subject_id = subject_key.meta.as_key().unwrap().item_id.clone();
1902 let signing_id = signed_with.meta.as_key().unwrap().item_id.clone();
1903
1904 let meta = ItemMetadata::Cert(CertMetadata {
1905 subject_key_id: subject_id,
1906 signing_key_id: signing_id,
1907 retrieved_from: None,
1908 is_generated: true,
1909 });
1910
1911 let mut rng = FakeEntropicRng(testing_rng());
1917 let keypair = ed25519::Keypair::generate(&mut rng);
1918 let encoded_cert = Ed25519Cert::constructor()
1919 .cert_type(tor_cert::CertType::IDENTITY_V_SIGNING)
1920 .expiration(SystemTime::now() + Duration::from_secs(180))
1921 .signing_key(keypair.public_key().into())
1922 .cert_key(CertifiedKey::Ed25519(keypair.public_key().into()))
1923 .encode_and_sign(&keypair)
1924 .unwrap();
1925 let test_cert = CertData::TorEd25519Cert(encoded_cert);
1926 AlwaysValidCert(TestItem {
1927 item: KeystoreItem::Cert(test_cert),
1928 meta,
1929 })
1930 };
1931
1932 let res = mgr
1933 .get_or_generate_key_and_cert::<TestItem, AlwaysValidCert>(
1934 &spec,
1935 &make_certificate,
1936 KeystoreSelector::Primary,
1937 &mut rng,
1938 );
1939
1940 #[allow(unused_assignments)]
1941 #[allow(unused_mut)]
1942 let mut has_error = false;
1943 $(
1944 has_error = true;
1945 let err = res.clone().unwrap_err();
1946 assert!(
1947 matches!(
1948 err,
1949 crate::Error::Corruption(KeystoreCorruptionError::$expected_err)
1950 ),
1951 "unexpected error: {err:?}",
1952 );
1953 )?
1954
1955 if !has_error {
1956 let (key, cert) = res.unwrap();
1957
1958 let expected_subj_key_id = if $generate_subject_key == Yes {
1959 "generated_test_key"
1960 } else {
1961 "generated_test_key"
1962 };
1963
1964 assert_eq!(key.meta.item_id(), expected_subj_key_id);
1965 assert_eq!(
1966 cert.0.meta.as_cert().unwrap().subject_key_id,
1967 expected_subj_key_id
1968 );
1969 assert_eq!(
1970 cert.0.meta.as_cert().unwrap().signing_key_id,
1971 "generated_test_key"
1972 );
1973 assert_eq!(cert.0.meta.is_generated(), true);
1974 }
1975 }}
1976 }
1977
1978 #[test]
1979 #[cfg(feature = "experimental-api")]
1980 #[rustfmt::skip] #[allow(clippy::cognitive_complexity)] fn get_certificate() {
1983 run_certificate_test!(
1984 generate_subject_key = No,
1985 generate_signing_key = No,
1986 MissingSigningKey
1987 );
1988
1989 run_certificate_test!(
1990 generate_subject_key = Yes,
1991 generate_signing_key = No,
1992 MissingSigningKey
1993 );
1994
1995 run_certificate_test!(
1996 generate_subject_key = No,
1997 generate_signing_key = Yes,
1998 );
1999
2000 run_certificate_test!(
2001 generate_subject_key = Yes,
2002 generate_signing_key = Yes,
2003 );
2004 }
2005}