1use crate::raw::{RawEntryId, RawKeystoreEntry};
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 #[getter(skip)]
81 raw_id: RawEntryId,
82}
83
84impl<'a> KeystoreEntry<'a> {
85 pub(crate) fn new(
87 key_path: KeyPath,
88 key_type: KeystoreItemType,
89 keystore_id: &'a KeystoreId,
90 raw_id: RawEntryId,
91 ) -> Self {
92 Self {
93 key_path,
94 key_type,
95 keystore_id,
96 raw_id,
97 }
98 }
99
100 #[cfg(feature = "onion-service-cli-extra")]
102 pub fn raw_entry(&self) -> RawKeystoreEntry {
103 RawKeystoreEntry::new(self.raw_id.clone(), self.keystore_id.clone())
104 }
105}
106
107impl<'a> From<KeystoreEntry<'a>> for KeystoreEntryResult<KeystoreEntry<'a>> {
112 fn from(val: KeystoreEntry<'a>) -> Self {
113 Ok(val)
114 }
115}
116
117impl KeyMgrBuilder {
118 pub fn build(self) -> StdResult<KeyMgr, KeyMgrBuilderError> {
120 use itertools::Itertools as _;
121
122 let mut keymgr = self.build_unvalidated()?;
123
124 if !keymgr.all_stores().map(|s| s.id()).all_unique() {
125 return Err(KeyMgrBuilderError::ValidationError(
126 "the keystore IDs are not pairwise unique".into(),
127 ));
128 }
129
130 keymgr.key_info_extractors = inventory::iter::<&'static dyn KeyPathInfoExtractor>
131 .into_iter()
132 .copied()
133 .collect();
134
135 Ok(keymgr)
136 }
137}
138
139impl KeyMgrBuilder {
144 pub fn secondary_stores(&mut self) -> &mut Vec<BoxedKeystore> {
150 self.secondary_stores.get_or_insert(Default::default())
151 }
152
153 pub fn set_secondary_stores(mut self, list: Vec<BoxedKeystore>) -> Self {
155 self.secondary_stores = Some(list);
156 self
157 }
158
159 pub fn opt_secondary_stores(&self) -> &Option<Vec<BoxedKeystore>> {
163 &self.secondary_stores
164 }
165
166 pub fn opt_secondary_stores_mut(&mut self) -> &mut Option<Vec<BoxedKeystore>> {
170 &mut self.secondary_stores
171 }
172}
173
174inventory::collect!(&'static dyn crate::KeyPathInfoExtractor);
175
176impl KeyMgr {
177 pub fn get<K: ToEncodableKey>(&self, key_spec: &dyn KeySpecifier) -> Result<Option<K>> {
184 self.get_from_store(key_spec, &K::Key::item_type(), self.all_stores())
185 }
186
187 pub fn get_entry<K: ToEncodableKey>(&self, entry: &KeystoreEntry) -> Result<Option<K>> {
195 let selector = entry.keystore_id().into();
196 let store = self.select_keystore(&selector)?;
197 self.get_from_store(entry.key_path(), entry.key_type(), [store].into_iter())
198 }
199
200 #[cfg(feature = "experimental-api")]
215 pub fn get_cert_entry<
216 S: KeyCertificateSpecifier + for<'a> TryFrom<&'a KeyPath, Error = KeyPathError>,
217 K: ToEncodableKey,
218 C: ToEncodableCert<K>,
219 >(
220 &self,
221 entry: &KeystoreEntry,
222 signing_key_spec: &dyn KeySpecifier,
223 ) -> Result<Option<C>> {
224 let selector = entry.keystore_id().into();
225 let store = self.select_keystore(&selector)?;
226 let cert_spec = S::try_from(entry.key_path())
227 .map_err(into_bad_api_usage!("wrong cert specifier for entry?!"))?;
228 let subject_key_spec = cert_spec.subject_key_specifier();
229
230 self.get_cert_from_store(
231 entry.key_path(),
232 entry.key_type(),
233 signing_key_spec,
234 subject_key_spec,
235 [store].into_iter(),
236 )
237 }
238
239 pub fn get_or_generate<K>(
251 &self,
252 key_spec: &dyn KeySpecifier,
253 selector: KeystoreSelector,
254 rng: &mut dyn KeygenRng,
255 ) -> Result<K>
256 where
257 K: ToEncodableKey,
258 K::Key: Keygen,
259 {
260 match self.get(key_spec)? {
261 Some(k) => Ok(k),
262 None => self.generate(key_spec, selector, rng, false),
263 }
264 }
265
266 #[cfg(feature = "onion-service-cli-extra")]
276 pub fn get_from<K: ToEncodableKey>(
277 &self,
278 key_spec: &dyn KeySpecifier,
279 keystore_id: &KeystoreId,
280 ) -> Result<Option<K>> {
281 let store = std::iter::once(self.find_keystore(keystore_id)?);
282 self.get_from_store(key_spec, &K::Key::item_type(), store)
283 }
284
285 #[cfg(feature = "onion-service-cli-extra")]
295 pub fn validate_entry_integrity(&self, entry: &KeystoreEntry) -> Result<()> {
296 let selector = entry.keystore_id().into();
297 let store = self.select_keystore(&selector)?;
298 let _ = store.get(entry.key_path(), entry.key_type())?;
300
301 let path = entry.key_path();
302 let _ = self
304 .describe(path)
305 .ok_or_else(|| KeystoreCorruptionError::Unrecognized(path.clone()))?;
306
307 Ok(())
308 }
309
310 pub fn generate<K>(
331 &self,
332 key_spec: &dyn KeySpecifier,
333 selector: KeystoreSelector,
334 rng: &mut dyn KeygenRng,
335 overwrite: bool,
336 ) -> Result<K>
337 where
338 K: ToEncodableKey,
339 K::Key: Keygen,
340 {
341 let store = self.select_keystore(&selector)?;
342
343 if overwrite || !store.contains(key_spec, &K::Key::item_type())? {
344 let key = K::Key::generate(rng)?;
345 store.insert(&key, key_spec)?;
346
347 Ok(K::from_encodable_key(key))
348 } else {
349 Err(crate::Error::KeyAlreadyExists)
350 }
351 }
352
353 pub fn insert<K: ToEncodableKey>(
366 &self,
367 key: K,
368 key_spec: &dyn KeySpecifier,
369 selector: KeystoreSelector,
370 overwrite: bool,
371 ) -> Result<Option<K>> {
372 let key = key.to_encodable_key();
373 let store = self.select_keystore(&selector)?;
374 let key_type = K::Key::item_type();
375 let old_key: Option<K> = self.get_from_store(key_spec, &key_type, [store].into_iter())?;
376
377 if old_key.is_some() && !overwrite {
378 Err(crate::Error::KeyAlreadyExists)
379 } else {
380 let () = store.insert(&key, key_spec)?;
381 Ok(old_key)
382 }
383 }
384
385 pub fn remove<K: ToEncodableKey>(
396 &self,
397 key_spec: &dyn KeySpecifier,
398 selector: KeystoreSelector,
399 ) -> Result<Option<K>> {
400 let store = self.select_keystore(&selector)?;
401 let key_type = K::Key::item_type();
402 let old_key: Option<K> = self.get_from_store(key_spec, &key_type, [store].into_iter())?;
403
404 store.remove(key_spec, &key_type)?;
405
406 Ok(old_key)
407 }
408
409 pub fn remove_entry(&self, entry: &KeystoreEntry) -> Result<Option<()>> {
421 let selector = entry.keystore_id().into();
422 let store = self.select_keystore(&selector)?;
423
424 store.remove(entry.key_path(), entry.key_type())
425 }
426
427 #[cfg(feature = "onion-service-cli-extra")]
435 pub fn remove_unchecked(&self, raw_id: &str, keystore_id: &KeystoreId) -> Result<()> {
436 let selector = KeystoreSelector::from(keystore_id);
437 let store = self.select_keystore(&selector)?;
438 let raw_id = store.raw_entry_id(raw_id)?;
439 let store = self.select_keystore(&selector)?;
440 store.remove_unchecked(&raw_id)
441 }
442
443 pub fn list_matching(&self, pat: &KeyPathPattern) -> Result<Vec<KeystoreEntry>> {
451 self.all_stores()
452 .map(|store| -> Result<Vec<_>> {
453 Ok(store
454 .list()?
455 .into_iter()
456 .filter_map(|entry| entry.ok())
457 .filter(|entry| entry.key_path().matches(pat))
458 .collect::<Vec<_>>())
459 })
460 .flatten_ok()
461 .collect::<Result<Vec<_>>>()
462 }
463
464 #[cfg(feature = "onion-service-cli-extra")]
466 pub fn list_by_id(&self, id: &KeystoreId) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
467 self.find_keystore(id)?.list()
468 }
469
470 #[cfg(feature = "onion-service-cli-extra")]
472 pub fn list(&self) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
473 self.all_stores()
474 .map(|store| -> Result<Vec<_>> { store.list() })
475 .flatten_ok()
476 .collect::<Result<Vec<_>>>()
477 }
478
479 #[cfg(feature = "onion-service-cli-extra")]
481 pub fn list_keystores(&self) -> Vec<KeystoreId> {
482 self.all_stores()
483 .map(|store| store.id().to_owned())
484 .collect()
485 }
486
487 pub fn describe(&self, path: &KeyPath) -> Option<KeyPathInfo> {
496 for info_extractor in &self.key_info_extractors {
497 if let Ok(info) = info_extractor.describe(path) {
498 return Some(info);
499 }
500 }
501
502 None
503 }
504
505 fn get_from_store_raw<'a, K: ItemType>(
511 &self,
512 key_spec: &dyn KeySpecifier,
513 key_type: &KeystoreItemType,
514 stores: impl Iterator<Item = &'a BoxedKeystore>,
515 ) -> Result<Option<K>> {
516 let static_key_type = K::item_type();
517 if key_type != &static_key_type {
518 return Err(internal!(
519 "key type {:?} does not match the key type {:?} of requested key K::Key",
520 key_type,
521 static_key_type
522 )
523 .into());
524 }
525
526 for store in stores {
527 let key = match store.get(key_spec, &K::item_type()) {
528 Ok(None) => {
529 continue;
531 }
532 Ok(Some(k)) => k,
533 Err(e) => {
534 return Err(e);
536 }
537 };
538
539 let key: K = key
541 .downcast::<K>()
542 .map(|k| *k)
543 .map_err(|_| internal!("failed to downcast key to requested type"))?;
544
545 return Ok(Some(key));
546 }
547
548 Ok(None)
549 }
550
551 #[cfg(feature = "experimental-api")]
553 fn get_cert_from_store<'a, K: ToEncodableKey, C: ToEncodableCert<K>>(
554 &self,
555 cert_spec: &dyn KeySpecifier,
556 cert_type: &KeystoreItemType,
557 signing_cert_spec: &dyn KeySpecifier,
558 subject_cert_spec: &dyn KeySpecifier,
559 stores: impl Iterator<Item = &'a BoxedKeystore>,
560 ) -> Result<Option<C>> {
561 let Some(cert) = self.get_from_store_raw::<C::ParsedCert>(cert_spec, cert_type, stores)?
562 else {
563 return Ok(None);
564 };
565
566 let Some(subject) =
568 self.get_from_store::<K>(subject_cert_spec, &K::Key::item_type(), self.all_stores())?
569 else {
570 return Ok(None);
571 };
572 let signed_with = self.get_cert_signing_key::<K, C>(signing_cert_spec)?;
573 let cert = C::validate(cert, &subject, &signed_with)?;
574
575 Ok(Some(cert))
576 }
577
578 fn get_from_store<'a, K: ToEncodableKey>(
582 &self,
583 key_spec: &dyn KeySpecifier,
584 key_type: &KeystoreItemType,
585 stores: impl Iterator<Item = &'a BoxedKeystore> + Clone,
586 ) -> Result<Option<K>> {
587 let Some(key) = self.get_from_store_raw::<K::Key>(key_spec, key_type, stores.clone())?
588 else {
589 let Some(key_pair_spec) = key_spec.keypair_specifier() else {
592 return Ok(None);
593 };
594
595 let key_type = <K::KeyPair as ToEncodableKey>::Key::item_type();
596 return Ok(self
597 .get_from_store::<K::KeyPair>(&*key_pair_spec, &key_type, stores)?
598 .map(|k| k.into()));
599 };
600
601 Ok(Some(K::from_encodable_key(key)))
602 }
603
604 #[cfg(feature = "experimental-api")]
620 pub fn get_key_and_cert<K, C>(
621 &self,
622 spec: &dyn KeyCertificateSpecifier,
623 signing_key_spec: &dyn KeySpecifier,
624 ) -> Result<Option<(K, C)>>
625 where
626 K: ToEncodableKey,
627 C: ToEncodableCert<K>,
628 {
629 let subject_key_spec = spec.subject_key_specifier();
630 let Some(key) =
632 self.get_from_store::<K>(subject_key_spec, &K::Key::item_type(), self.all_stores())?
633 else {
634 return Ok(None);
635 };
636
637 let cert_spec = spec
638 .arti_path()
639 .map_err(into_bad_api_usage!("invalid key certificate specifier"))?;
640
641 let Some(cert) = self.get_from_store_raw::<C::ParsedCert>(
642 &cert_spec,
643 &<C::ParsedCert as ItemType>::item_type(),
644 self.all_stores(),
645 )?
646 else {
647 return Err(KeystoreCorruptionError::MissingCertificate.into());
648 };
649
650 let signed_with = self.get_cert_signing_key::<K, C>(signing_key_spec)?;
652 let cert = C::validate(cert, &key, &signed_with)?;
653
654 Ok(Some((key, cert)))
655 }
656
657 #[cfg(feature = "experimental-api")]
693 pub fn get_or_generate_key_and_cert<K, C>(
694 &self,
695 spec: &dyn KeyCertificateSpecifier,
696 signing_key_spec: &dyn KeySpecifier,
697 make_certificate: impl FnOnce(&K, &<C as ToEncodableCert<K>>::SigningKey) -> C,
698 selector: KeystoreSelector,
699 rng: &mut dyn KeygenRng,
700 ) -> Result<(K, C)>
701 where
702 K: ToEncodableKey,
703 K::Key: Keygen,
704 C: ToEncodableCert<K>,
705 {
706 let subject_key_spec = spec.subject_key_specifier();
707 let subject_key_arti_path = subject_key_spec
708 .arti_path()
709 .map_err(|_| bad_api_usage!("subject key does not have an ArtiPath?!"))?;
710
711 let cert_specifier =
712 ArtiPath::from_path_and_denotators(subject_key_arti_path, &spec.cert_denotators())
713 .map_err(into_bad_api_usage!("invalid certificate specifier"))?;
714
715 let maybe_cert = self.get_from_store_raw::<C::ParsedCert>(
716 &cert_specifier,
717 &C::ParsedCert::item_type(),
718 self.all_stores(),
719 )?;
720
721 let maybe_subject_key = self.get::<K>(subject_key_spec)?;
722
723 match (&maybe_cert, &maybe_subject_key) {
724 (Some(_), None) => {
725 return Err(KeystoreCorruptionError::MissingSubjectKey.into());
726 }
727 _ => {
728 }
730 }
731 let subject_key = match maybe_subject_key {
732 Some(key) => key,
733 _ => {
734 let subject_keypair_spec =
735 subject_key_spec.keypair_specifier().ok_or_else(|| {
736 internal!(
737 "KeyCertificateSpecifier has no keypair specifier for the subject key?!"
738 )
739 })?;
740 self.generate(&*subject_keypair_spec, selector, rng, false)?
741 }
742 };
743
744 let signed_with = self.get_cert_signing_key::<K, C>(signing_key_spec)?;
745 let cert = match maybe_cert {
746 Some(cert) => C::validate(cert, &subject_key, &signed_with)?,
747 None => {
748 let cert = make_certificate(&subject_key, &signed_with);
749
750 let () = self.insert_cert(cert.clone(), &cert_specifier, selector)?;
751
752 cert
753 }
754 };
755
756 Ok((subject_key, cert))
757 }
758
759 fn all_stores(&self) -> impl Iterator<Item = &BoxedKeystore> + Clone {
761 iter::once(&self.primary_store).chain(self.secondary_stores.iter())
762 }
763
764 fn select_keystore(&self, selector: &KeystoreSelector) -> Result<&BoxedKeystore> {
769 match selector {
770 KeystoreSelector::Id(keystore_id) => self.find_keystore(keystore_id),
771 KeystoreSelector::Primary => Ok(&self.primary_store),
772 }
773 }
774
775 fn find_keystore(&self, id: &KeystoreId) -> Result<&BoxedKeystore> {
780 self.all_stores()
781 .find(|keystore| keystore.id() == id)
782 .ok_or_else(|| crate::Error::KeystoreNotFound(id.clone()))
783 }
784
785 #[cfg(feature = "experimental-api")]
790 fn get_cert_signing_key<K, C>(
791 &self,
792 signing_key_spec: &dyn KeySpecifier,
793 ) -> Result<C::SigningKey>
794 where
795 K: ToEncodableKey,
796 C: ToEncodableCert<K>,
797 {
798 let Some(signing_key) = self.get_from_store::<C::SigningKey>(
799 signing_key_spec,
800 &<C::SigningKey as ToEncodableKey>::Key::item_type(),
801 self.all_stores(),
802 )?
803 else {
804 return Err(KeystoreCorruptionError::MissingSigningKey.into());
805 };
806
807 Ok(signing_key)
808 }
809
810 fn insert_cert<K, C>(
817 &self,
818 cert: C,
819 cert_spec: &dyn KeySpecifier,
820 selector: KeystoreSelector,
821 ) -> Result<()>
822 where
823 K: ToEncodableKey,
824 K::Key: Keygen,
825 C: ToEncodableCert<K>,
826 {
827 let cert = cert.to_encodable_cert();
828 let store = self.select_keystore(&selector)?;
829
830 let () = store.insert(&cert, cert_spec)?;
831 Ok(())
832 }
833}
834
835#[cfg(test)]
836mod tests {
837 #![allow(clippy::bool_assert_comparison)]
839 #![allow(clippy::clone_on_copy)]
840 #![allow(clippy::dbg_macro)]
841 #![allow(clippy::mixed_attributes_style)]
842 #![allow(clippy::print_stderr)]
843 #![allow(clippy::print_stdout)]
844 #![allow(clippy::single_char_pattern)]
845 #![allow(clippy::unwrap_used)]
846 #![allow(clippy::unchecked_time_subtraction)]
847 #![allow(clippy::useless_vec)]
848 #![allow(clippy::needless_pass_by_value)]
849 use super::*;
851 use crate::keystore::arti::err::{ArtiNativeKeystoreError, MalformedPathError};
852 use crate::raw::{RawEntryId, RawKeystoreEntry};
853 use crate::test_utils::{TestDerivedKeySpecifier, TestDerivedKeypairSpecifier};
854 use crate::{
855 ArtiPath, ArtiPathUnavailableError, Error, KeyPath, KeystoreEntryResult, KeystoreError,
856 UnrecognizedEntryError,
857 };
858 use std::path::PathBuf;
859 use std::result::Result as StdResult;
860 use std::str::FromStr;
861 use std::sync::{Arc, RwLock};
862 use tor_basic_utils::test_rng::testing_rng;
863 use tor_cert::CertifiedKey;
864 use tor_cert::Ed25519Cert;
865 use tor_checkable::TimeValidityError;
866 use tor_error::{ErrorKind, HasKind};
867 use tor_key_forge::{
868 CertData, CertType, EncodableItem, EncodedEd25519Cert, ErasedKey, InvalidCertError,
869 KeyType, KeystoreItem,
870 };
871 use tor_llcrypto::pk::ed25519::{self, Ed25519PublicKey as _};
872 use tor_llcrypto::rng::FakeEntropicRng;
873 use web_time_compat::{Duration, SystemTime, SystemTimeExt};
874
875 #[cfg(feature = "experimental-api")]
876 use {
877 crate::CertSpecifierPattern,
878 crate::test_utils::{TestCertSpecifier, TestCertSpecifierPattern},
879 };
880
881 #[derive(Clone, Debug, PartialEq)]
883 struct KeyMetadata {
884 item_id: String,
886 retrieved_from: Option<KeystoreId>,
890 is_generated: bool,
892 }
893
894 #[derive(Clone, Debug, PartialEq)]
896 struct CertMetadata {
897 subject_key_id: String,
899 signing_key_id: String,
901 retrieved_from: Option<KeystoreId>,
905 is_generated: bool,
908 }
909
910 #[derive(Clone, Debug, PartialEq, derive_more::From)]
912 enum ItemMetadata {
913 Key(KeyMetadata),
915 Cert(CertMetadata),
917 }
918
919 impl ItemMetadata {
920 fn item_id(&self) -> &str {
925 match self {
926 ItemMetadata::Key(k) => &k.item_id,
927 ItemMetadata::Cert(c) => &c.subject_key_id,
928 }
929 }
930
931 fn retrieved_from(&self) -> Option<&KeystoreId> {
933 match self {
934 ItemMetadata::Key(k) => k.retrieved_from.as_ref(),
935 ItemMetadata::Cert(c) => c.retrieved_from.as_ref(),
936 }
937 }
938
939 fn is_generated(&self) -> bool {
941 match self {
942 ItemMetadata::Key(k) => k.is_generated,
943 ItemMetadata::Cert(c) => c.is_generated,
944 }
945 }
946
947 fn set_retrieved_from(&mut self, id: KeystoreId) {
949 match self {
950 ItemMetadata::Key(meta) => meta.retrieved_from = Some(id),
951 ItemMetadata::Cert(meta) => meta.retrieved_from = Some(id),
952 }
953 }
954
955 fn as_key(&self) -> Option<&KeyMetadata> {
957 match self {
958 ItemMetadata::Key(meta) => Some(meta),
959 _ => None,
960 }
961 }
962
963 fn as_cert(&self) -> Option<&CertMetadata> {
965 match self {
966 ItemMetadata::Cert(meta) => Some(meta),
967 _ => None,
968 }
969 }
970 }
971
972 #[derive(Clone, Debug)]
974 struct TestItem {
975 item: KeystoreItem,
977 meta: ItemMetadata,
979 }
980
981 struct TestCert(TestItem);
983
984 impl ItemType for TestCert {
985 fn item_type() -> KeystoreItemType
986 where
987 Self: Sized,
988 {
989 CertType::Ed25519TorCert.into()
990 }
991 }
992
993 #[derive(Clone, Debug)]
995 struct AlwaysValidCert(TestItem);
996
997 #[derive(Clone, Debug)]
999 struct AlwaysExpiredCert(TestItem);
1000
1001 #[derive(Clone, Debug)]
1003 struct TestPublicKey {
1004 key: KeystoreItem,
1006 meta: ItemMetadata,
1008 }
1009
1010 impl From<TestItem> for TestPublicKey {
1011 fn from(tk: TestItem) -> TestPublicKey {
1012 TestPublicKey {
1013 key: tk.item,
1014 meta: tk.meta,
1015 }
1016 }
1017 }
1018
1019 impl TestItem {
1020 fn new(item_id: &str) -> Self {
1022 let mut rng = testing_rng();
1023 TestItem {
1024 item: ed25519::Keypair::generate(&mut rng)
1025 .as_keystore_item()
1026 .unwrap(),
1027 meta: ItemMetadata::Key(KeyMetadata {
1028 item_id: item_id.to_string(),
1029 retrieved_from: None,
1030 is_generated: false,
1031 }),
1032 }
1033 }
1034 }
1035
1036 impl Keygen for TestItem {
1037 fn generate(mut rng: &mut dyn KeygenRng) -> tor_key_forge::Result<Self>
1038 where
1039 Self: Sized,
1040 {
1041 Ok(TestItem {
1042 item: ed25519::Keypair::generate(&mut rng).as_keystore_item()?,
1043 meta: ItemMetadata::Key(KeyMetadata {
1044 item_id: "generated_test_key".to_string(),
1045 retrieved_from: None,
1046 is_generated: true,
1047 }),
1048 })
1049 }
1050 }
1051
1052 impl ItemType for TestItem {
1053 fn item_type() -> KeystoreItemType
1054 where
1055 Self: Sized,
1056 {
1057 KeyType::Ed25519Keypair.into()
1059 }
1060 }
1061
1062 impl EncodableItem for TestItem {
1063 fn as_keystore_item(&self) -> tor_key_forge::Result<KeystoreItem> {
1064 Ok(self.item.clone())
1065 }
1066 }
1067
1068 impl ToEncodableKey for TestItem {
1069 type Key = Self;
1070 type KeyPair = Self;
1071
1072 fn to_encodable_key(self) -> Self::Key {
1073 self
1074 }
1075
1076 fn from_encodable_key(key: Self::Key) -> Self {
1077 key
1078 }
1079 }
1080
1081 impl ItemType for TestPublicKey {
1082 fn item_type() -> KeystoreItemType
1083 where
1084 Self: Sized,
1085 {
1086 KeyType::Ed25519PublicKey.into()
1087 }
1088 }
1089
1090 impl EncodableItem for TestPublicKey {
1091 fn as_keystore_item(&self) -> tor_key_forge::Result<KeystoreItem> {
1092 Ok(self.key.clone())
1093 }
1094 }
1095
1096 impl ToEncodableKey for TestPublicKey {
1097 type Key = Self;
1098 type KeyPair = TestItem;
1099
1100 fn to_encodable_key(self) -> Self::Key {
1101 self
1102 }
1103
1104 fn from_encodable_key(key: Self::Key) -> Self {
1105 key
1106 }
1107 }
1108
1109 impl ToEncodableCert<TestItem> for AlwaysValidCert {
1110 type ParsedCert = TestCert;
1111 type EncodableCert = TestItem;
1112 type SigningKey = TestItem;
1113
1114 fn validate(
1115 cert: Self::ParsedCert,
1116 _subject: &TestItem,
1117 _signed_with: &Self::SigningKey,
1118 ) -> StdResult<Self, InvalidCertError> {
1119 Ok(Self(cert.0))
1121 }
1122
1123 fn to_encodable_cert(self) -> Self::EncodableCert {
1125 self.0
1126 }
1127 }
1128
1129 impl ToEncodableCert<TestItem> for AlwaysExpiredCert {
1130 type ParsedCert = TestCert;
1131 type EncodableCert = TestItem;
1132 type SigningKey = TestItem;
1133
1134 fn validate(
1135 _cert: Self::ParsedCert,
1136 _subject: &TestItem,
1137 _signed_with: &Self::SigningKey,
1138 ) -> StdResult<Self, InvalidCertError> {
1139 Err(InvalidCertError::TimeValidity(TimeValidityError::Expired(
1140 Duration::from_secs(60),
1141 )))
1142 }
1143
1144 fn to_encodable_cert(self) -> Self::EncodableCert {
1146 self.0
1147 }
1148 }
1149
1150 #[derive(thiserror::Error, Debug, Clone, derive_more::Display)]
1151 enum MockKeystoreError {
1152 NotFound,
1153 }
1154
1155 impl KeystoreError for MockKeystoreError {}
1156
1157 impl HasKind for MockKeystoreError {
1158 fn kind(&self) -> ErrorKind {
1159 tor_error::ErrorKind::Other
1161 }
1162 }
1163
1164 fn build_raw_id_path<T: ToString>(key_path: &T, key_type: &KeystoreItemType) -> RawEntryId {
1165 let mut path = key_path.to_string();
1166 path.push('.');
1167 path.push_str(&key_type.arti_extension());
1168 RawEntryId::Path(PathBuf::from(&path))
1169 }
1170
1171 struct Keystore {
1172 inner: RwLock<Vec<KeystoreEntryResult<(ArtiPath, KeystoreItemType, TestItem)>>>,
1173 id: KeystoreId,
1174 }
1175
1176 impl Keystore {
1177 fn new(id: &str) -> Self {
1178 let id = KeystoreId::from_str(id).unwrap();
1179
1180 Self {
1181 inner: Default::default(),
1182 id,
1183 }
1184 }
1185
1186 fn new_boxed(id: &str) -> BoxedKeystore {
1187 Box::new(Self::new(id))
1188 }
1189 }
1190
1191 impl crate::Keystore for Keystore {
1192 fn contains(
1193 &self,
1194 key_spec: &dyn KeySpecifier,
1195 item_type: &KeystoreItemType,
1196 ) -> Result<bool> {
1197 let wanted_arti_path = key_spec.arti_path().unwrap();
1198 Ok(self.inner.read().unwrap().iter().any(|res| match res {
1199 Ok((spec, ty, _)) => spec == &wanted_arti_path && ty == item_type,
1200 Err(_) => false,
1201 }))
1202 }
1203
1204 fn id(&self) -> &KeystoreId {
1205 &self.id
1206 }
1207
1208 fn get(
1209 &self,
1210 key_spec: &dyn KeySpecifier,
1211 item_type: &KeystoreItemType,
1212 ) -> Result<Option<ErasedKey>> {
1213 let key_spec = key_spec.arti_path().unwrap();
1214
1215 Ok(self.inner.read().unwrap().iter().find_map(|res| {
1216 if let Ok((arti_path, ty, k)) = res {
1217 if arti_path == &key_spec && ty == item_type {
1218 let mut k = k.clone();
1219 k.meta.set_retrieved_from(self.id().clone());
1220
1221 match item_type {
1222 KeystoreItemType::Key(_) => {
1223 return Some(Box::new(k) as Box<dyn ItemType>);
1224 }
1225 KeystoreItemType::Cert(_) => {
1226 return Some(Box::new(TestCert(k)) as Box<dyn ItemType>);
1230 }
1231 _ => panic!("unknown item type?!"),
1232 }
1233 }
1234 }
1235 None
1236 }))
1237 }
1238
1239 #[cfg(feature = "onion-service-cli-extra")]
1240 fn raw_entry_id(&self, raw_id: &str) -> Result<RawEntryId> {
1241 Ok(RawEntryId::Path(PathBuf::from(raw_id.to_string())))
1242 }
1243
1244 fn insert(&self, key: &dyn EncodableItem, key_spec: &dyn KeySpecifier) -> Result<()> {
1245 let key = key.downcast_ref::<TestItem>().unwrap();
1246
1247 let item = key.as_keystore_item()?;
1248 let item_type = item.item_type()?;
1249
1250 self.inner
1251 .write()
1252 .unwrap()
1253 .insert(
1258 0,
1259 Ok((key_spec.arti_path().unwrap(), item_type, key.clone())),
1260 );
1261
1262 Ok(())
1263 }
1264
1265 fn remove(
1266 &self,
1267 key_spec: &dyn KeySpecifier,
1268 item_type: &KeystoreItemType,
1269 ) -> Result<Option<()>> {
1270 let wanted_arti_path = key_spec.arti_path().unwrap();
1271 let index = self.inner.read().unwrap().iter().position(|res| {
1272 if let Ok((arti_path, ty, _)) = res {
1273 arti_path == &wanted_arti_path && ty == item_type
1274 } else {
1275 false
1276 }
1277 });
1278 let Some(index) = index else {
1279 return Ok(None);
1280 };
1281 let _ = self.inner.write().unwrap().remove(index);
1282
1283 Ok(Some(()))
1284 }
1285
1286 #[cfg(feature = "onion-service-cli-extra")]
1287 fn remove_unchecked(&self, entry_id: &RawEntryId) -> Result<()> {
1288 let index = self.inner.read().unwrap().iter().position(|res| match res {
1289 Ok((spec, ty, _)) => {
1290 let id = build_raw_id_path(spec, ty);
1291 entry_id == &id
1292 }
1293 Err(e) => e.entry().raw_id() == entry_id,
1294 });
1295 let Some(index) = index else {
1296 return Err(Error::Keystore(Arc::new(MockKeystoreError::NotFound)));
1297 };
1298 let _ = self.inner.write().unwrap().remove(index);
1299 Ok(())
1300 }
1301
1302 fn list(&self) -> Result<Vec<KeystoreEntryResult<KeystoreEntry>>> {
1303 Ok(self
1304 .inner
1305 .read()
1306 .unwrap()
1307 .iter()
1308 .map(|res| match res {
1309 Ok((arti_path, ty, _)) => {
1310 let raw_id = RawEntryId::Path(PathBuf::from(&arti_path.to_string()));
1311
1312 Ok(KeystoreEntry::new(
1313 KeyPath::Arti(arti_path.clone()),
1314 ty.clone(),
1315 self.id(),
1316 raw_id,
1317 ))
1318 }
1319 Err(e) => Err(e.clone()),
1320 })
1321 .collect())
1322 }
1323 }
1324
1325 fn add_unrecognized_entries(keystore: &mut Keystore, count: usize) {
1327 for i in 0..count {
1328 let invalid_key_path = PathBuf::from(&format!("unrecognized_entry{}", i));
1329 let raw_id = RawEntryId::Path(invalid_key_path.clone());
1330 let entry = RawKeystoreEntry::new(raw_id, keystore.id.clone()).into();
1331 let entry = UnrecognizedEntryError::new(
1332 entry,
1333 Arc::new(ArtiNativeKeystoreError::MalformedPath {
1334 path: invalid_key_path,
1335 err: MalformedPathError::NoExtension,
1336 }),
1337 );
1338 keystore.inner.write().unwrap().push(Err(entry));
1339 }
1340 }
1341
1342 macro_rules! impl_specifier {
1343 ($name:tt, $id:expr) => {
1344 struct $name;
1345
1346 impl KeySpecifier for $name {
1347 fn arti_path(&self) -> StdResult<ArtiPath, ArtiPathUnavailableError> {
1348 Ok(ArtiPath::new($id.into()).map_err(|e| tor_error::internal!("{e}"))?)
1349 }
1350
1351 fn ctor_path(&self) -> Option<crate::CTorPath> {
1352 None
1353 }
1354
1355 fn keypair_specifier(&self) -> Option<Box<dyn KeySpecifier>> {
1356 None
1357 }
1358 }
1359 };
1360 }
1361
1362 impl_specifier!(TestKeySpecifier1, "spec1");
1363 impl_specifier!(TestKeySpecifier2, "spec2");
1364 impl_specifier!(TestKeySpecifier3, "spec3");
1365 impl_specifier!(TestKeySpecifier4, "spec4");
1366
1367 impl_specifier!(TestPublicKeySpecifier1, "pub-spec1");
1368
1369 fn entry_descriptor(
1371 specifier: impl KeySpecifier,
1372 key_type: KeystoreItemType,
1373 keystore_id: &KeystoreId,
1374 ) -> KeystoreEntry {
1375 let arti_path = specifier.arti_path().unwrap();
1376 let raw_id = RawEntryId::Path(PathBuf::from(arti_path.as_ref()));
1377 KeystoreEntry {
1378 key_path: arti_path.into(),
1379 key_type,
1380 keystore_id,
1381 raw_id,
1382 }
1383 }
1384
1385 #[test]
1386 #[allow(clippy::cognitive_complexity)]
1387 fn insert_and_get() {
1388 let mut builder = KeyMgrBuilder::default().primary_store(Keystore::new_boxed("keystore1"));
1389
1390 builder.secondary_stores().extend([
1391 Keystore::new_boxed("keystore2"),
1392 Keystore::new_boxed("keystore3"),
1393 ]);
1394
1395 let mgr = builder.build().unwrap();
1396
1397 let old_key = mgr
1399 .insert(
1400 TestItem::new("coot"),
1401 &TestKeySpecifier1,
1402 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1403 true,
1404 )
1405 .unwrap();
1406
1407 assert!(old_key.is_none());
1408 let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1409 assert_eq!(key.meta.item_id(), "coot");
1410 assert_eq!(
1411 key.meta.retrieved_from(),
1412 Some(&KeystoreId::from_str("keystore2").unwrap())
1413 );
1414 assert_eq!(key.meta.is_generated(), false);
1415
1416 let old_key = mgr
1418 .insert(
1419 TestItem::new("gull"),
1420 &TestKeySpecifier1,
1421 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1422 true,
1423 )
1424 .unwrap()
1425 .unwrap();
1426 assert_eq!(old_key.meta.item_id(), "coot");
1427 assert_eq!(
1428 old_key.meta.retrieved_from(),
1429 Some(&KeystoreId::from_str("keystore2").unwrap())
1430 );
1431 assert_eq!(old_key.meta.is_generated(), false);
1432 let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1434 assert_eq!(key.meta.item_id(), "gull");
1435 assert_eq!(
1436 key.meta.retrieved_from(),
1437 Some(&KeystoreId::from_str("keystore2").unwrap())
1438 );
1439 assert_eq!(key.meta.is_generated(), false);
1440
1441 let err = mgr
1443 .insert(
1444 TestItem::new("gull"),
1445 &TestKeySpecifier1,
1446 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1447 false,
1448 )
1449 .unwrap_err();
1450 assert!(matches!(err, crate::Error::KeyAlreadyExists));
1451
1452 let old_key = mgr
1454 .insert(
1455 TestItem::new("penguin"),
1456 &TestKeySpecifier2,
1457 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1458 false,
1459 )
1460 .unwrap();
1461 assert!(old_key.is_none());
1462
1463 let old_key = mgr
1465 .insert(
1466 TestItem::new("moorhen"),
1467 &TestKeySpecifier3,
1468 KeystoreSelector::Primary,
1469 true,
1470 )
1471 .unwrap();
1472 assert!(old_key.is_none());
1473 let key = mgr.get::<TestItem>(&TestKeySpecifier3).unwrap().unwrap();
1474 assert_eq!(key.meta.item_id(), "moorhen");
1475 assert_eq!(
1476 key.meta.retrieved_from(),
1477 Some(&KeystoreId::from_str("keystore1").unwrap())
1478 );
1479 assert_eq!(key.meta.is_generated(), false);
1480
1481 assert!(mgr.get::<TestItem>(&TestKeySpecifier4).unwrap().is_none());
1483
1484 for store in ["keystore3", "keystore2", "keystore1"] {
1488 let old_key = mgr
1489 .insert(
1490 TestItem::new("cormorant"),
1491 &TestKeySpecifier4,
1492 KeystoreSelector::Id(&KeystoreId::from_str(store).unwrap()),
1493 true,
1494 )
1495 .unwrap();
1496 assert!(old_key.is_none());
1497
1498 let key = mgr.get::<TestItem>(&TestKeySpecifier4).unwrap().unwrap();
1500 assert_eq!(key.meta.item_id(), "cormorant");
1501 assert_eq!(
1502 key.meta.retrieved_from(),
1503 Some(&KeystoreId::from_str(store).unwrap())
1504 );
1505 assert_eq!(key.meta.is_generated(), false);
1506 }
1507
1508 let key = mgr.get::<TestItem>(&TestKeySpecifier4).unwrap().unwrap();
1511 assert_eq!(key.meta.item_id(), "cormorant");
1512 assert_eq!(
1513 key.meta.retrieved_from(),
1514 Some(&KeystoreId::from_str("keystore1").unwrap())
1515 );
1516 assert_eq!(key.meta.is_generated(), false);
1517 }
1518
1519 #[test]
1520 #[cfg(feature = "onion-service-cli-extra")]
1521 fn get_from() {
1522 let mut builder = KeyMgrBuilder::default().primary_store(Keystore::new_boxed("keystore1"));
1523
1524 builder.secondary_stores().extend([
1525 Keystore::new_boxed("keystore2"),
1526 Keystore::new_boxed("keystore3"),
1527 ]);
1528
1529 let mgr = builder.build().unwrap();
1530
1531 let keystore1_id = KeystoreId::from_str("keystore1").unwrap();
1532 let keystore2_id = KeystoreId::from_str("keystore2").unwrap();
1533 let key_id_1 = "mantis shrimp";
1534 let key_id_2 = "tardigrade";
1535
1536 let _ = mgr
1538 .insert(
1539 TestItem::new(key_id_1),
1540 &TestKeySpecifier1,
1541 KeystoreSelector::Id(&keystore1_id),
1542 true,
1543 )
1544 .unwrap();
1545
1546 let _ = mgr
1548 .insert(
1549 TestItem::new(key_id_2),
1550 &TestKeySpecifier1,
1551 KeystoreSelector::Id(&keystore2_id),
1552 true,
1553 )
1554 .unwrap();
1555
1556 let key = mgr
1558 .get_from::<TestItem>(&TestKeySpecifier1, &keystore2_id)
1559 .unwrap()
1560 .unwrap();
1561
1562 assert_eq!(key.meta.item_id(), key_id_2);
1563 assert_eq!(key.meta.retrieved_from(), Some(&keystore2_id));
1564 }
1565
1566 #[test]
1567 fn get_from_keypair() {
1568 const KEYSTORE_ID1: &str = "keystore1";
1569 const KEYSTORE_ID2: &str = "keystore2";
1570
1571 let mut builder = KeyMgrBuilder::default().primary_store(Keystore::new_boxed(KEYSTORE_ID1));
1572 builder
1573 .secondary_stores()
1574 .extend([Keystore::new_boxed(KEYSTORE_ID2)]);
1575 let mgr = builder.build().unwrap();
1576
1577 let keystore2 = KeystoreId::from_str(KEYSTORE_ID2).unwrap();
1578
1579 let _ = mgr
1581 .insert(
1582 TestItem::new("nightjar"),
1583 &TestDerivedKeypairSpecifier,
1584 KeystoreSelector::Id(&keystore2),
1585 true,
1586 )
1587 .unwrap();
1588
1589 macro_rules! boxed {
1590 ($closure:expr) => {
1591 Box::new($closure) as _
1592 };
1593 }
1594
1595 #[allow(clippy::type_complexity)]
1596 let getters: &[(&'static str, Box<dyn Fn() -> Result<Option<TestPublicKey>>>)] = &[
1597 (
1598 "get",
1599 boxed!(|| mgr.get::<TestPublicKey>(&TestDerivedKeySpecifier)),
1600 ),
1601 #[cfg(feature = "onion-service-cli-extra")]
1602 (
1603 "get_from",
1604 boxed!(|| mgr.get_from::<TestPublicKey>(&TestDerivedKeySpecifier, &keystore2)),
1605 ),
1606 (
1607 "remove",
1608 boxed!(|| mgr.remove::<TestPublicKey>(
1609 &TestDerivedKeySpecifier,
1610 KeystoreSelector::Id(&keystore2)
1611 )),
1612 ),
1613 ];
1614
1615 for (test_name, getter) in getters {
1616 let key = getter().unwrap().expect(test_name);
1619
1620 assert_eq!(key.meta.item_id(), "nightjar", "{test_name}");
1621 assert_eq!(key.meta.retrieved_from(), Some(&keystore2), "{test_name}");
1622 }
1623 }
1624
1625 #[test]
1626 fn remove() {
1627 let mut builder = KeyMgrBuilder::default().primary_store(Keystore::new_boxed("keystore1"));
1628
1629 builder.secondary_stores().extend([
1630 Keystore::new_boxed("keystore2"),
1631 Keystore::new_boxed("keystore3"),
1632 ]);
1633
1634 let mgr = builder.build().unwrap();
1635
1636 assert!(
1637 !mgr.secondary_stores[0]
1638 .contains(&TestKeySpecifier1, &TestItem::item_type())
1639 .unwrap()
1640 );
1641
1642 mgr.insert(
1644 TestItem::new("coot"),
1645 &TestKeySpecifier1,
1646 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1647 true,
1648 )
1649 .unwrap();
1650 let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1651 assert_eq!(key.meta.item_id(), "coot");
1652 assert_eq!(
1653 key.meta.retrieved_from(),
1654 Some(&KeystoreId::from_str("keystore2").unwrap())
1655 );
1656 assert_eq!(key.meta.is_generated(), false);
1657
1658 assert!(
1660 mgr.remove::<TestItem>(
1661 &TestKeySpecifier1,
1662 KeystoreSelector::Id(&KeystoreId::from_str("not_an_id_we_know_of").unwrap())
1663 )
1664 .is_err()
1665 );
1666 assert!(
1668 mgr.secondary_stores[0]
1669 .contains(&TestKeySpecifier1, &TestItem::item_type())
1670 .unwrap()
1671 );
1672
1673 assert!(
1675 mgr.remove::<TestItem>(&TestKeySpecifier1, KeystoreSelector::Primary)
1676 .unwrap()
1677 .is_none()
1678 );
1679
1680 assert!(
1682 mgr.secondary_stores[0]
1683 .contains(&TestKeySpecifier1, &TestItem::item_type())
1684 .unwrap()
1685 );
1686
1687 let removed_key = mgr
1689 .remove::<TestItem>(
1690 &TestKeySpecifier1,
1691 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
1692 )
1693 .unwrap()
1694 .unwrap();
1695 assert_eq!(removed_key.meta.item_id(), "coot");
1696 assert_eq!(
1697 removed_key.meta.retrieved_from(),
1698 Some(&KeystoreId::from_str("keystore2").unwrap())
1699 );
1700 assert_eq!(removed_key.meta.is_generated(), false);
1701
1702 assert!(
1704 !mgr.secondary_stores[0]
1705 .contains(&TestKeySpecifier1, &TestItem::item_type())
1706 .unwrap()
1707 );
1708 }
1709
1710 #[test]
1711 fn keygen() {
1712 let mut rng = FakeEntropicRng(testing_rng());
1713 let mgr = KeyMgrBuilder::default()
1714 .primary_store(Keystore::new_boxed("keystore1"))
1715 .build()
1716 .unwrap();
1717
1718 mgr.insert(
1719 TestItem::new("coot"),
1720 &TestKeySpecifier1,
1721 KeystoreSelector::Primary,
1722 true,
1723 )
1724 .unwrap();
1725
1726 assert!(
1728 mgr.get::<TestPublicKey>(&TestPublicKeySpecifier1)
1729 .unwrap()
1730 .is_none()
1731 );
1732
1733 let err = mgr
1735 .generate::<TestItem>(
1736 &TestKeySpecifier1,
1737 KeystoreSelector::Primary,
1738 &mut rng,
1739 false,
1740 )
1741 .unwrap_err();
1742
1743 assert!(matches!(err, crate::Error::KeyAlreadyExists));
1744
1745 let key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1747 assert_eq!(key.meta.item_id(), "coot");
1748 assert_eq!(
1749 key.meta.retrieved_from(),
1750 Some(&KeystoreId::from_str("keystore1").unwrap())
1751 );
1752 assert_eq!(key.meta.is_generated(), false);
1753
1754 assert!(
1756 mgr.get::<TestPublicKey>(&TestPublicKeySpecifier1)
1757 .unwrap()
1758 .is_none()
1759 );
1760
1761 let generated_key = mgr
1763 .generate::<TestItem>(
1764 &TestKeySpecifier1,
1765 KeystoreSelector::Primary,
1766 &mut rng,
1767 true,
1768 )
1769 .unwrap();
1770
1771 assert_eq!(generated_key.meta.item_id(), "generated_test_key");
1772 assert_eq!(generated_key.meta.retrieved_from(), None);
1775 assert_eq!(generated_key.meta.is_generated(), true);
1776
1777 let retrieved_key = mgr.get::<TestItem>(&TestKeySpecifier1).unwrap().unwrap();
1779 assert_eq!(retrieved_key.meta.item_id(), "generated_test_key");
1780 assert_eq!(
1781 retrieved_key.meta.retrieved_from(),
1782 Some(&KeystoreId::from_str("keystore1").unwrap())
1783 );
1784 assert_eq!(retrieved_key.meta.is_generated(), true);
1785
1786 assert!(
1788 mgr.get::<TestPublicKey>(&TestPublicKeySpecifier1)
1789 .unwrap()
1790 .is_none()
1791 );
1792 }
1793
1794 #[test]
1795 fn get_or_generate() {
1796 let mut rng = FakeEntropicRng(testing_rng());
1797 let mut builder = KeyMgrBuilder::default().primary_store(Keystore::new_boxed("keystore1"));
1798
1799 builder.secondary_stores().extend([
1800 Keystore::new_boxed("keystore2"),
1801 Keystore::new_boxed("keystore3"),
1802 ]);
1803
1804 let mgr = builder.build().unwrap();
1805
1806 let keystore2 = KeystoreId::from_str("keystore2").unwrap();
1807 let entry_desc1 = entry_descriptor(TestKeySpecifier1, TestItem::item_type(), &keystore2);
1808 assert!(mgr.get_entry::<TestItem>(&entry_desc1).unwrap().is_none());
1809
1810 mgr.insert(
1811 TestItem::new("coot"),
1812 &TestKeySpecifier1,
1813 KeystoreSelector::Id(&keystore2),
1814 true,
1815 )
1816 .unwrap();
1817
1818 let key = mgr
1820 .get_or_generate::<TestItem>(&TestKeySpecifier1, KeystoreSelector::Primary, &mut rng)
1821 .unwrap();
1822 assert_eq!(key.meta.item_id(), "coot");
1823 assert_eq!(
1824 key.meta.retrieved_from(),
1825 Some(&KeystoreId::from_str("keystore2").unwrap())
1826 );
1827 assert_eq!(key.meta.is_generated(), false);
1828
1829 assert_eq!(
1830 mgr.get_entry::<TestItem>(&entry_desc1)
1831 .unwrap()
1832 .map(|k| k.meta),
1833 Some(ItemMetadata::Key(KeyMetadata {
1834 item_id: "coot".to_string(),
1835 retrieved_from: Some(keystore2.clone()),
1836 is_generated: false,
1837 }))
1838 );
1839
1840 let keystore3 = KeystoreId::from_str("keystore3").unwrap();
1843 let generated_key = mgr
1844 .get_or_generate::<TestItem>(
1845 &TestKeySpecifier2,
1846 KeystoreSelector::Id(&keystore3),
1847 &mut rng,
1848 )
1849 .unwrap();
1850 assert_eq!(generated_key.meta.item_id(), "generated_test_key");
1851 assert_eq!(generated_key.meta.retrieved_from(), None);
1854 assert_eq!(generated_key.meta.is_generated(), true);
1855
1856 let retrieved_key = mgr.get::<TestItem>(&TestKeySpecifier2).unwrap().unwrap();
1858 assert_eq!(retrieved_key.meta.item_id(), "generated_test_key");
1859 assert_eq!(
1860 retrieved_key.meta.retrieved_from(),
1861 Some(&KeystoreId::from_str("keystore3").unwrap())
1862 );
1863 assert_eq!(retrieved_key.meta.is_generated(), true);
1864
1865 let entry_desc2 = entry_descriptor(TestKeySpecifier2, TestItem::item_type(), &keystore3);
1866 assert_eq!(
1867 mgr.get_entry::<TestItem>(&entry_desc2)
1868 .unwrap()
1869 .map(|k| k.meta),
1870 Some(ItemMetadata::Key(KeyMetadata {
1871 item_id: "generated_test_key".to_string(),
1872 retrieved_from: Some(keystore3.clone()),
1873 is_generated: true,
1874 }))
1875 );
1876
1877 let arti_pat = KeyPathPattern::Arti("*".to_string());
1878 let matching = mgr.list_matching(&arti_pat).unwrap();
1879
1880 assert_eq!(matching.len(), 2);
1881 assert!(matching.contains(&entry_desc1));
1882 assert!(matching.contains(&entry_desc2));
1883
1884 assert_eq!(mgr.remove_entry(&entry_desc2).unwrap(), Some(()));
1885 assert!(mgr.get_entry::<TestItem>(&entry_desc2).unwrap().is_none());
1886 assert!(mgr.remove_entry(&entry_desc2).unwrap().is_none());
1887 }
1888
1889 #[test]
1890 fn list_matching_ignores_unrecognized_keys() {
1891 let mut keystore = Keystore::new("keystore1");
1892 add_unrecognized_entries(&mut keystore, 1);
1893 let builder = KeyMgrBuilder::default().primary_store(Box::new(keystore));
1894
1895 let mgr = builder.build().unwrap();
1896
1897 let keystore1 = KeystoreId::from_str("keystore1").unwrap();
1898 mgr.insert(
1899 TestItem::new("whale shark"),
1900 &TestKeySpecifier1,
1901 KeystoreSelector::Id(&keystore1),
1902 true,
1903 )
1904 .unwrap();
1905
1906 let arti_pat = KeyPathPattern::Arti("*".to_string());
1907 let valid_key_path = KeyPath::Arti(TestKeySpecifier1.arti_path().unwrap());
1908 let matching = mgr.list_matching(&arti_pat).unwrap();
1909 assert_eq!(matching.len(), 1);
1911 assert_eq!(matching.first().unwrap().key_path(), &valid_key_path);
1912 }
1913
1914 #[cfg(feature = "onion-service-cli-extra")]
1915 #[test]
1916 fn keys_subcommands() {
1919 let mut keystore = Keystore::new("keystore1");
1920 add_unrecognized_entries(&mut keystore, 1);
1921 let mut builder = KeyMgrBuilder::default().primary_store(Box::new(keystore));
1922 builder.secondary_stores().extend([
1923 Keystore::new_boxed("keystore2"),
1924 Keystore::new_boxed("keystore3"),
1925 ]);
1926
1927 let mgr = builder.build().unwrap();
1928 let keystore1id = KeystoreId::from_str("keystore1").unwrap();
1929 let keystore2id = KeystoreId::from_str("keystore2").unwrap();
1930 let keystore3id = KeystoreId::from_str("keystore3").unwrap();
1931
1932 let _ = mgr
1934 .insert(
1935 TestItem::new("pangolin"),
1936 &TestKeySpecifier1,
1937 KeystoreSelector::Id(&keystore1id),
1938 true,
1939 )
1940 .unwrap();
1941
1942 let _ = mgr
1944 .insert(
1945 TestItem::new("coot"),
1946 &TestKeySpecifier2,
1947 KeystoreSelector::Id(&keystore2id),
1948 true,
1949 )
1950 .unwrap();
1951
1952 let _ = mgr
1954 .insert(
1955 TestItem::new("penguin"),
1956 &TestKeySpecifier3,
1957 KeystoreSelector::Id(&keystore3id),
1958 true,
1959 )
1960 .unwrap();
1961
1962 let assert_key = |path, ty, expected_path: &ArtiPath, expected_type| {
1963 assert_eq!(ty, expected_type);
1964 assert_eq!(path, &KeyPath::Arti(expected_path.clone()));
1965 };
1966 let item_type = TestItem::new("axolotl").item.item_type().unwrap();
1967 let unrecognized_entry_id = RawEntryId::Path(PathBuf::from("unrecognized_entry0"));
1968
1969 let entries = mgr.list().unwrap();
1971
1972 let expected_items = [
1973 (keystore1id, TestKeySpecifier1.arti_path().unwrap()),
1974 (keystore2id, TestKeySpecifier2.arti_path().unwrap()),
1975 (keystore3id, TestKeySpecifier3.arti_path().unwrap()),
1976 ];
1977
1978 let mut recognized_entries = 0;
1980 let mut unrecognized_entries = 0;
1981 for entry in entries.iter() {
1982 match entry {
1983 Ok(e) => {
1984 if let Some((_, expected_arti_path)) = expected_items
1985 .iter()
1986 .find(|(keystore_id, _)| keystore_id == e.keystore_id())
1987 {
1988 assert_key(e.key_path(), e.key_type(), expected_arti_path, &item_type);
1989 recognized_entries += 1;
1990 continue;
1991 }
1992
1993 panic!("Unexpected key encountered {:?}", e);
1994 }
1995 Err(u) => {
1996 assert_eq!(u.entry().raw_id(), &unrecognized_entry_id);
1997 unrecognized_entries += 1;
1998 }
1999 }
2000 }
2001 assert_eq!(recognized_entries, 3);
2002 assert_eq!(unrecognized_entries, 1);
2003
2004 let keystores = mgr.list_keystores().iter().len();
2006
2007 assert_eq!(keystores, 3);
2008
2009 let primary_keystore_id = KeystoreId::from_str("keystore1").unwrap();
2011 let entries = mgr.list_by_id(&primary_keystore_id).unwrap();
2012
2013 let mut recognized_entries = 0;
2015 let mut unrecognized_entries = 0;
2016 let mut all_entries = vec![];
2018 for entry in entries.iter() {
2019 match entry {
2020 Ok(entry) => {
2021 assert_key(
2022 entry.key_path(),
2023 entry.key_type(),
2024 &TestKeySpecifier1.arti_path().unwrap(),
2025 &item_type,
2026 );
2027 recognized_entries += 1;
2028 all_entries.push(RawKeystoreEntry::new(
2029 build_raw_id_path(entry.key_path(), entry.key_type()),
2030 primary_keystore_id.clone(),
2031 ));
2032 }
2033 Err(u) => {
2034 assert_eq!(u.entry().raw_id(), &unrecognized_entry_id);
2035 unrecognized_entries += 1;
2036 all_entries.push(u.entry().into());
2037 }
2038 }
2039 }
2040 assert_eq!(recognized_entries, 1);
2041 assert_eq!(unrecognized_entries, 1);
2042
2043 for entry in all_entries {
2045 mgr.remove_unchecked(&entry.raw_id().to_string(), entry.keystore_id())
2046 .unwrap();
2047 }
2048
2049 let entries = mgr.list_by_id(&primary_keystore_id).unwrap();
2051 assert_eq!(entries.len(), 0);
2052 }
2053
2054 #[cfg(feature = "experimental-api")]
2056 #[derive(Clone, Copy, Debug, PartialEq)]
2057 enum GenerateItem {
2058 Yes,
2059 No,
2060 }
2061
2062 fn make_certificate(subject_key: &TestItem, signed_with: &TestItem) -> AlwaysValidCert {
2063 let subject_id = subject_key.meta.as_key().unwrap().item_id.clone();
2064 let signing_id = signed_with.meta.as_key().unwrap().item_id.clone();
2065
2066 let meta = ItemMetadata::Cert(CertMetadata {
2067 subject_key_id: subject_id,
2068 signing_key_id: signing_id,
2069 retrieved_from: None,
2070 is_generated: true,
2071 });
2072
2073 let mut rng = FakeEntropicRng(testing_rng());
2079 let keypair = ed25519::Keypair::generate(&mut rng);
2080 let encoded_cert = Ed25519Cert::constructor()
2081 .cert_type(tor_cert::CertType::IDENTITY_V_SIGNING)
2082 .expiration(SystemTime::get() + Duration::from_secs(180))
2083 .signing_key(keypair.public_key().into())
2084 .cert_key(CertifiedKey::Ed25519(keypair.public_key().into()))
2085 .encode_and_sign(&keypair)
2086 .unwrap();
2087 let test_cert = CertData::TorEd25519Cert(encoded_cert);
2088 AlwaysValidCert(TestItem {
2089 item: KeystoreItem::Cert(test_cert),
2090 meta,
2091 })
2092 }
2093
2094 #[cfg(feature = "experimental-api")]
2095 macro_rules! run_certificate_test {
2096 (
2097 generate_subject_key = $generate_subject_key:expr,
2098 generate_signing_key = $generate_signing_key:expr,
2099 $($expected_err:tt)?
2100 ) => {{
2101 use GenerateItem::*;
2102
2103 let mut rng = FakeEntropicRng(testing_rng());
2104 let mut builder = KeyMgrBuilder::default().primary_store(Keystore::new_boxed("keystore1"));
2105
2106 builder
2107 .secondary_stores()
2108 .extend([Keystore::new_boxed("keystore2"), Keystore::new_boxed("keystore3")]);
2109
2110 let mgr = builder.build().unwrap();
2111
2112 let spec = crate::test_utils::TestCertSpecifier {
2113 subject_key_spec: TestDerivedKeySpecifier,
2114 denotator: "foo".into(),
2115 };
2116
2117 if $generate_subject_key == Yes {
2118 let _ = mgr
2119 .generate::<TestItem>(
2120 &TestKeySpecifier1,
2121 KeystoreSelector::Primary,
2122 &mut rng,
2123 false,
2124 )
2125 .unwrap();
2126 }
2127
2128 if $generate_signing_key == Yes {
2129 let _ = mgr
2130 .generate::<TestItem>(
2131 &TestKeySpecifier2,
2132 KeystoreSelector::Id(&KeystoreId::from_str("keystore2").unwrap()),
2133 &mut rng,
2134 false,
2135 )
2136 .unwrap();
2137 }
2138
2139
2140 let signing_key_spec = TestKeySpecifier2;
2141 let res = mgr
2142 .get_or_generate_key_and_cert::<TestItem, AlwaysValidCert>(
2143 &spec,
2144 &signing_key_spec,
2145 &make_certificate,
2146 KeystoreSelector::Primary,
2147 &mut rng,
2148 );
2149
2150 #[allow(unused_assignments)]
2151 #[allow(unused_mut)]
2152 let mut has_error = false;
2153 $(
2154 has_error = true;
2155 let err = res.clone().unwrap_err();
2156 assert!(
2157 matches!(
2158 err,
2159 crate::Error::Corruption(KeystoreCorruptionError::$expected_err)
2160 ),
2161 "unexpected error: {err:?}",
2162 );
2163 )?
2164
2165 if !has_error {
2166 let (key, cert) = res.unwrap();
2167
2168 let expected_subj_key_id = if $generate_subject_key == Yes {
2169 "generated_test_key"
2170 } else {
2171 "generated_test_key"
2172 };
2173
2174 assert_eq!(key.meta.item_id(), expected_subj_key_id);
2175 assert_eq!(
2176 cert.0.meta.as_cert().unwrap().subject_key_id,
2177 expected_subj_key_id
2178 );
2179 assert_eq!(
2180 cert.0.meta.as_cert().unwrap().signing_key_id,
2181 "generated_test_key"
2182 );
2183 assert_eq!(cert.0.meta.is_generated(), true);
2184 }
2185 }}
2186 }
2187
2188 #[test]
2189 #[cfg(feature = "experimental-api")]
2190 #[rustfmt::skip] #[allow(clippy::cognitive_complexity)] fn get_certificate() {
2193 run_certificate_test!(
2194 generate_subject_key = No,
2195 generate_signing_key = No,
2196 MissingSigningKey
2197 );
2198
2199 run_certificate_test!(
2200 generate_subject_key = Yes,
2201 generate_signing_key = No,
2202 MissingSigningKey
2203 );
2204
2205 run_certificate_test!(
2206 generate_subject_key = No,
2207 generate_signing_key = Yes,
2208 );
2209
2210 run_certificate_test!(
2211 generate_subject_key = Yes,
2212 generate_signing_key = Yes,
2213 );
2214 }
2215
2216 #[test]
2217 #[cfg(feature = "experimental-api")]
2218 fn get_cert_entry() {
2219 let mut rng = FakeEntropicRng(testing_rng());
2220 let builder = KeyMgrBuilder::default().primary_store(Keystore::new_boxed("keystore1"));
2221 let mgr = builder.build().unwrap();
2222
2223 let _ = mgr
2225 .generate::<TestItem>(
2226 &TestKeySpecifier1,
2227 KeystoreSelector::Primary,
2228 &mut rng,
2229 false,
2230 )
2231 .unwrap();
2232
2233 let _ = mgr
2235 .generate::<TestItem>(
2236 &TestKeySpecifier2,
2237 KeystoreSelector::Primary,
2238 &mut rng,
2239 false,
2240 )
2241 .unwrap();
2242
2243 for cert_deno in 0..10 {
2245 let cert_spec = TestCertSpecifier {
2246 subject_key_spec: TestDerivedKeySpecifier,
2247 denotator: cert_deno.to_string(),
2248 };
2249
2250 let res = mgr.get_or_generate_key_and_cert::<TestItem, AlwaysValidCert>(
2251 &cert_spec,
2252 &TestKeySpecifier2,
2253 &make_certificate,
2254 KeystoreSelector::Primary,
2255 &mut rng,
2256 );
2257
2258 assert!(res.is_ok());
2259 }
2260
2261 let any_pat = TestCertSpecifierPattern::new_any().arti_pattern().unwrap();
2263
2264 assert_eq!(
2266 any_pat,
2267 KeyPathPattern::Arti("test/simple_key+@*".to_string())
2268 );
2269 let certs = mgr.list_matching(&any_pat).unwrap();
2270
2271 assert_eq!(certs.len(), 10);
2273
2274 let all_paths = certs
2276 .iter()
2277 .map(|entry| entry.key_path().arti().unwrap().as_str().to_string())
2278 .sorted()
2279 .collect::<Vec<_>>();
2280
2281 let expected_paths = (0..10)
2282 .map(|i| format!("test/simple_key+@{i}"))
2283 .collect::<Vec<_>>();
2284 assert_eq!(all_paths, expected_paths);
2285
2286 for entry in certs {
2287 let always_valid_cert = mgr
2288 .get_cert_entry::<TestCertSpecifier, TestItem, AlwaysValidCert>(
2289 &entry,
2290 &TestKeySpecifier2,
2291 )
2292 .unwrap();
2293
2294 assert!(always_valid_cert.is_some());
2296 }
2297
2298 const EXPIRED_DENO: &str = "expired";
2300
2301 let cert_spec = TestCertSpecifier {
2303 subject_key_spec: TestDerivedKeySpecifier,
2304 denotator: EXPIRED_DENO.to_string(),
2305 };
2306
2307 let meta = CertMetadata {
2309 subject_key_id: "foo".to_string(),
2310 signing_key_id: "bar".to_string(),
2311 retrieved_from: None,
2312 is_generated: false,
2313 };
2314 let test_cert =
2315 CertData::TorEd25519Cert(EncodedEd25519Cert::dangerously_from_bytes(b"foobar"));
2316 let cert = AlwaysExpiredCert(TestItem {
2317 item: KeystoreItem::Cert(test_cert),
2318 meta: ItemMetadata::Cert(meta),
2319 });
2320
2321 let res = mgr.insert_cert::<TestItem, AlwaysExpiredCert>(
2322 cert,
2323 &cert_spec,
2324 KeystoreSelector::Primary,
2325 );
2326 assert!(res.is_ok());
2327
2328 let pat = KeyPathPattern::Arti(format!("test/simple_key+@{EXPIRED_DENO}"));
2330 let certs = mgr.list_matching(&pat).unwrap();
2331 assert_eq!(certs.len(), 1);
2332 let entry = &certs[0];
2333
2334 let err = mgr
2335 .get_cert_entry::<TestCertSpecifier, TestItem, AlwaysExpiredCert>(
2336 entry,
2337 &TestKeySpecifier2,
2338 )
2339 .unwrap_err();
2340
2341 assert!(
2343 matches!(err, Error::InvalidCert(InvalidCertError::TimeValidity(_))),
2344 "{err:?}"
2345 );
2346 }
2347}