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