1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!("../README.md")]
3#![allow(renamed_and_removed_lints)] #![allow(unknown_lints)] #![warn(missing_docs)]
7#![warn(noop_method_call)]
8#![warn(unreachable_pub)]
9#![warn(clippy::all)]
10#![deny(clippy::await_holding_lock)]
11#![deny(clippy::cargo_common_metadata)]
12#![deny(clippy::cast_lossless)]
13#![deny(clippy::checked_conversions)]
14#![allow(clippy::cognitive_complexity)] #![deny(clippy::debug_assert_with_mut_call)]
16#![deny(clippy::exhaustive_enums)]
17#![deny(clippy::exhaustive_structs)]
18#![deny(clippy::expl_impl_clone_on_copy)]
19#![deny(clippy::fallible_impl_from)]
20#![deny(clippy::implicit_clone)]
21#![deny(clippy::large_stack_arrays)]
22#![warn(clippy::manual_ok_or)]
23#![deny(clippy::missing_docs_in_private_items)]
24#![warn(clippy::needless_borrow)]
25#![warn(clippy::needless_pass_by_value)]
26#![warn(clippy::option_option)]
27#![deny(clippy::print_stderr)]
28#![deny(clippy::print_stdout)]
29#![warn(clippy::rc_buffer)]
30#![deny(clippy::ref_option_ref)]
31#![warn(clippy::semicolon_if_nothing_returned)]
32#![warn(clippy::trait_duplication_in_bounds)]
33#![deny(clippy::unchecked_time_subtraction)]
34#![deny(clippy::unnecessary_wraps)]
35#![warn(clippy::unseparated_literal_suffix)]
36#![deny(clippy::unwrap_used)]
37#![deny(clippy::mod_module_files)]
38#![allow(clippy::let_unit_value)] #![allow(clippy::uninlined_format_args)]
40#![allow(clippy::significant_drop_in_scrutinee)] #![allow(clippy::result_large_err)] #![allow(clippy::needless_raw_string_hashes)] #![allow(clippy::needless_lifetimes)] #![allow(mismatched_lifetime_syntaxes)] #![allow(clippy::collapsible_if)] #![deny(clippy::unused_async)]
47#![deny(clippy::string_slice)] #![allow(clippy::redundant_field_names)]
52#![cfg_attr(
54 not(all(feature = "full", feature = "experimental")),
55 allow(unused, unreachable_pub)
56)]
57
58#[macro_use] mod time_store;
60
61mod internal_prelude;
62
63mod anon_level;
64mod caps;
65pub mod config;
66mod err;
67mod helpers;
68mod ipt_establish;
69mod ipt_lid;
70mod ipt_mgr;
71mod ipt_set;
72mod keys;
73mod pow;
74mod publish;
75mod rend_handshake;
76mod replay;
77mod req;
78pub mod status;
79mod timeout_track;
80
81#[doc(hidden)]
91pub mod timeout_track_for_doctests_unstable_no_semver_guarantees {
92 pub use crate::timeout_track::*;
93}
94#[doc(hidden)]
95pub mod time_store_for_doctests_unstable_no_semver_guarantees {
96 pub use crate::time_store::*;
97}
98
99use std::pin::Pin;
100
101use internal_prelude::*;
102
103pub use anon_level::Anonymity;
106pub use config::OnionServiceConfig;
107pub use err::{ClientError, EstablishSessionError, FatalError, IntroRequestError, StartupError};
108pub use ipt_mgr::IptError;
109use keys::HsTimePeriodKeySpecifier;
110pub use keys::{
111 BlindIdKeypairSpecifier, BlindIdPublicKeySpecifier, DescSigningKeypairSpecifier,
112 HsIdKeypairSpecifier, HsIdPublicKeySpecifier,
113};
114use pow::{NewPowManager, PowManager};
115pub use publish::UploadError as DescUploadError;
116pub use req::{RendRequest, StreamRequest};
117pub use tor_hscrypto::pk::HsId;
118use tor_keymgr::KeystoreEntry;
119pub use tor_persist::hsnickname::{HsNickname, InvalidNickname};
120
121pub use helpers::handle_rend_requests;
122
123#[cfg(feature = "onion-service-cli-extra")]
124use tor_netdir::NetDir;
125
126pub(crate) type LinkSpecs = Vec<tor_linkspec::EncodedLinkSpec>;
130
131pub(crate) type NtorPublicKey = curve25519::PublicKey;
136
137#[must_use = "a hidden service object will terminate the service when dropped"]
145pub struct RunningOnionService {
146 inner: Mutex<SvcInner>,
148 nickname: HsNickname,
150 keymgr: Arc<KeyMgr>,
152}
153
154struct SvcInner {
156 config_tx: postage::watch::Sender<Arc<OnionServiceConfig>>,
158
159 _shutdown_tx: postage::broadcast::Sender<void::Void>,
161
162 status_tx: StatusSender,
165
166 #[allow(clippy::type_complexity)]
168 unlaunched: Option<(
169 Pin<Box<dyn Stream<Item = RendRequest> + Send + Sync>>,
170 Box<dyn Launchable + Send + Sync>,
171 )>,
172}
173
174struct ForLaunch<R: Runtime> {
176 publisher: Publisher<R, publish::Real<R>>,
182
183 ipt_mgr: IptManager<R, crate::ipt_mgr::Real<R>>,
189
190 ipt_mgr_view: IptsManagerView,
194
195 pow_manager: Arc<PowManager<R>>,
197}
198
199trait Launchable: Send + Sync {
202 fn launch(self: Box<Self>) -> Result<(), StartupError>;
204}
205
206impl<R: Runtime> Launchable for ForLaunch<R> {
207 fn launch(self: Box<Self>) -> Result<(), StartupError> {
208 self.ipt_mgr.launch_background_tasks(self.ipt_mgr_view)?;
209 self.publisher.launch()?;
210 self.pow_manager.launch()?;
211
212 Ok(())
213 }
214}
215
216#[derive(PartialEq)]
220#[must_use]
221pub(crate) enum ShutdownStatus {
222 Continue,
224 Terminate,
226}
227
228impl From<oneshot::Canceled> for ShutdownStatus {
229 fn from(_: oneshot::Canceled) -> ShutdownStatus {
230 ShutdownStatus::Terminate
231 }
232}
233
234#[derive(Builder)]
243#[builder(build_fn(private, name = "build_unvalidated", error = "FatalError"))]
244pub struct OnionService {
245 config: OnionServiceConfig,
247 keymgr: Arc<KeyMgr>,
249 state_dir: StateDirectory,
251}
252
253impl OnionService {
254 pub fn builder() -> OnionServiceBuilder {
256 OnionServiceBuilder::default()
257 }
258
259 pub fn launch<R>(
271 self,
272 runtime: R,
273 netdir_provider: Arc<dyn NetDirProvider>,
274 circ_pool: Arc<HsCircPool<R>>,
275 path_resolver: Arc<tor_config_path::CfgPathResolver>,
276 ) -> Result<Option<(Arc<RunningOnionService>, impl Stream<Item = RendRequest>)>, StartupError>
277 where
278 R: Runtime,
279 {
280 let OnionService {
281 config,
282 keymgr,
283 state_dir,
284 } = self;
285
286 let nickname = config.nickname.clone();
287
288 let offline_hsid = false;
292
293 let selector = KeystoreSelector::Primary;
295 maybe_generate_hsid(&keymgr, &config.nickname, offline_hsid, selector)?;
296
297 if !config.enabled() {
298 return Ok(None);
299 }
300
301 if config.restricted_discovery.enabled {
302 info!(
303 nickname=%nickname,
304 "Launching onion service in restricted discovery mode"
305 );
306 } else {
307 info!(
308 nickname=%nickname,
309 "Launching onion service"
310 );
311 }
312
313 let state_handle = state_dir
314 .acquire_instance(&config.nickname)
315 .map_err(StartupError::StateDirectoryInaccessible)?;
316
317 let iptpub_storage_handle = state_handle
320 .storage_handle("iptpub")
321 .map_err(StartupError::StateDirectoryInaccessible)?;
322
323 let status_tx = StatusSender::new(OnionServiceStatus::new_shutdown());
324 let (config_tx, config_rx) = postage::watch::channel_with(Arc::new(config));
325
326 let pow_manager_storage_handle = state_handle
327 .storage_handle("pow_manager")
328 .map_err(StartupError::StateDirectoryInaccessible)?;
329 let pow_nonce_dir = state_handle
330 .raw_subdir("pow_nonces")
331 .map_err(StartupError::StateDirectoryInaccessible)?;
332 let NewPowManager {
333 pow_manager,
334 rend_req_tx,
335 rend_req_rx,
336 publisher_update_rx,
337 } = PowManager::new(
338 runtime.clone(),
339 nickname.clone(),
340 pow_nonce_dir,
341 keymgr.clone(),
342 pow_manager_storage_handle,
343 netdir_provider.clone(),
344 status_tx.clone().into(),
345 config_rx.clone(),
346 )?;
347
348 let (shutdown_tx, shutdown_rx) = broadcast::channel(0);
349
350 let (ipt_mgr_view, publisher_view) =
351 crate::ipt_set::ipts_channel(&runtime, iptpub_storage_handle)?;
352
353 let ipt_mgr = IptManager::new(
354 runtime.clone(),
355 netdir_provider.clone(),
356 nickname.clone(),
357 config_rx.clone(),
358 rend_req_tx,
359 shutdown_rx.clone(),
360 &state_handle,
361 crate::ipt_mgr::Real {
362 circ_pool: circ_pool.clone(),
363 },
364 keymgr.clone(),
365 status_tx.clone().into(),
366 )?;
367
368 let publisher: Publisher<R, publish::Real<R>> = Publisher::new(
369 runtime,
370 nickname.clone(),
371 netdir_provider,
372 circ_pool,
373 publisher_view,
374 config_rx,
375 status_tx.clone().into(),
376 Arc::clone(&keymgr),
377 path_resolver,
378 pow_manager.clone(),
379 publisher_update_rx,
380 );
381
382 let svc = Arc::new(RunningOnionService {
383 nickname,
384 keymgr,
385 inner: Mutex::new(SvcInner {
386 config_tx,
387 _shutdown_tx: shutdown_tx,
388 status_tx,
389 unlaunched: Some((
390 rend_req_rx,
391 Box::new(ForLaunch {
392 publisher,
393 ipt_mgr,
394 ipt_mgr_view,
395 pow_manager,
396 }),
397 )),
398 }),
399 });
400
401 let stream = svc.launch()?;
402 Ok(Some((svc, stream)))
403 }
404
405 pub fn onion_address(&self) -> Option<HsId> {
413 onion_address(&self.keymgr, &self.config.nickname)
414 }
415
416 #[deprecated = "Use the new onion_address method instead"]
420 pub fn onion_name(&self) -> Option<HsId> {
421 self.onion_address()
422 }
423
424 pub fn generate_identity_key(&self, selector: KeystoreSelector) -> Result<HsId, StartupError> {
443 let offline_hsid = false;
447
448 maybe_generate_hsid(&self.keymgr, &self.config.nickname, offline_hsid, selector)
449 }
450
451 #[cfg(feature = "onion-service-cli-extra")]
459 pub fn list_expired_keys(&self, netdir: &NetDir) -> tor_keymgr::Result<Vec<KeystoreEntry>> {
460 list_expired_keys_for_service(
461 &netdir.hs_all_time_periods(),
462 self.config.nickname(),
463 &self.keymgr,
464 )
465 }
466}
467
468impl OnionServiceBuilder {
469 pub fn build(&self) -> Result<OnionService, StartupError> {
471 let svc = self.build_unvalidated()?;
472 Ok(svc)
473 }
474}
475
476impl RunningOnionService {
477 pub fn reconfigure(
483 &self,
484 new_config: OnionServiceConfig,
485 how: Reconfigure,
486 ) -> Result<(), ReconfigureError> {
487 let mut inner = self.inner.lock().expect("lock poisoned");
488 inner.config_tx.try_maybe_send(|cur_config| {
489 let new_config = cur_config.for_transition_to(new_config, how)?;
490 Ok(match how {
491 tor_config::Reconfigure::CheckAllOrNothing => Arc::clone(cur_config),
493 _ => Arc::new(new_config),
495 })
496 })
497
498 }
502
503 pub fn status(&self) -> OnionServiceStatus {
512 self.inner.lock().expect("poisoned lock").status_tx.get()
513 }
514
515 pub fn status_events(&self) -> OnionServiceStatusStream {
518 self.inner
519 .lock()
520 .expect("poisoned lock")
521 .status_tx
522 .subscribe()
523 }
524
525 fn launch(self: &Arc<Self>) -> Result<impl Stream<Item = RendRequest> + use<>, StartupError> {
531 let (rend_req_rx, launch) = {
532 let mut inner = self.inner.lock().expect("poisoned lock");
533 inner
534 .unlaunched
535 .take()
536 .ok_or(StartupError::AlreadyLaunched)?
537 };
538
539 match launch.launch() {
540 Ok(()) => {}
541 Err(e) => {
542 return Err(e);
543 }
544 }
545
546 Ok(rend_req_rx)
552 }
553
554 pub fn onion_address(&self) -> Option<HsId> {
574 onion_address(&self.keymgr, &self.nickname)
575 }
576
577 #[deprecated = "Use the new onion_address method instead"]
581 pub fn onion_name(&self) -> Option<HsId> {
582 self.onion_address()
583 }
584}
585
586fn maybe_generate_hsid(
590 keymgr: &Arc<KeyMgr>,
591 nickname: &HsNickname,
592 offline_hsid: bool,
593 selector: KeystoreSelector,
594) -> Result<HsId, StartupError> {
595 if offline_hsid {
596 unimplemented!("offline hsid mode");
597 }
598
599 let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
600
601 let kp = keymgr
602 .get::<HsIdKey>(&hsid_spec)
603 .map_err(|cause| StartupError::Keystore {
604 action: "read",
605 cause,
606 })?;
607
608 let mut rng = tor_llcrypto::rng::CautiousRng;
609 let (hsid, generated) = match kp {
610 Some(kp) => (kp.id(), false),
611 None => {
612 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
616 let kp = keymgr
617 .generate::<HsIdKeypair>(&hsid_spec, selector, &mut rng, false )
618 .map_err(|cause| StartupError::Keystore {
619 action: "generate",
620 cause,
621 })?;
622
623 (HsIdKey::from(&kp).id(), true)
624 }
625 };
626
627 if generated {
628 info!(
629 "Generated a new identity for service {nickname}: {}",
630 hsid.display_redacted()
631 );
632 } else {
633 info!(
636 "Using existing identity for service {nickname}: {}",
637 hsid.display_redacted()
638 );
639 }
640
641 Ok(hsid)
642}
643
644fn onion_address(keymgr: &KeyMgr, nickname: &HsNickname) -> Option<HsId> {
656 let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
657
658 keymgr
659 .get::<HsIdKey>(&hsid_spec)
660 .ok()?
661 .map(|hsid| hsid.id())
662}
663
664pub fn supported_hsservice_protocols() -> tor_protover::Protocols {
667 use tor_protover::named::*;
668 [
671 HSINTRO_V3,
673 HSINTRO_RATELIM,
674 HSREND_V3,
675 HSDIR_V3,
676 ]
677 .into_iter()
678 .collect()
679}
680
681fn list_expired_keys_for_service<'a>(
685 relevant_periods: &[HsDirParams],
686 nickname: &HsNickname,
687 keymgr: &'a KeyMgr,
688) -> tor_keymgr::Result<Vec<KeystoreEntry<'a>>> {
689 let arti_pat = tor_keymgr::KeyPathPattern::Arti(format!("hss/{}/*", nickname));
690 let possibly_relevant_keys = keymgr.list_matching(&arti_pat)?;
691 let mut expired_keys = Vec::new();
692
693 for entry in possibly_relevant_keys {
694 let key_path = entry.key_path();
695 let mut append_if_expired = |spec: &dyn HsTimePeriodKeySpecifier| {
696 if spec.nickname() != nickname {
697 return Err(internal!(
698 "keymgr gave us key {spec:?} that doesn't match our pattern {arti_pat:?}"
699 )
700 .into());
701 }
702 let is_expired = relevant_periods
703 .iter()
704 .all(|p| &p.time_period() != spec.period());
705
706 if is_expired {
707 expired_keys.push(entry.clone());
708 }
709
710 tor_keymgr::Result::Ok(())
711 };
712
713 macro_rules! append_if_expired {
714 ($K:ty) => {{
715 if let Ok(spec) = <$K>::try_from(key_path) {
716 append_if_expired(&spec)?;
717 }
718 }};
719 }
720
721 append_if_expired!(BlindIdPublicKeySpecifier);
722 append_if_expired!(BlindIdKeypairSpecifier);
723 append_if_expired!(DescSigningKeypairSpecifier);
724 }
725
726 Ok(expired_keys)
727}
728
729#[cfg(test)]
730pub(crate) mod test {
731 #![allow(clippy::bool_assert_comparison)]
733 #![allow(clippy::clone_on_copy)]
734 #![allow(clippy::dbg_macro)]
735 #![allow(clippy::mixed_attributes_style)]
736 #![allow(clippy::print_stderr)]
737 #![allow(clippy::print_stdout)]
738 #![allow(clippy::single_char_pattern)]
739 #![allow(clippy::unwrap_used)]
740 #![allow(clippy::unchecked_time_subtraction)]
741 #![allow(clippy::useless_vec)]
742 #![allow(clippy::needless_pass_by_value)]
743 #![allow(clippy::string_slice)] use super::*;
746
747 use std::fmt::Display;
748 use std::path::Path;
749
750 use fs_mistrust::Mistrust;
751 use test_temp_dir::{TestTempDir, TestTempDirGuard, test_temp_dir};
752
753 use tor_basic_utils::test_rng::testing_rng;
754 use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder};
755 use tor_llcrypto::pk::ed25519;
756 use tor_persist::state_dir::InstanceStateHandle;
757
758 use crate::config::OnionServiceConfigBuilder;
759 use crate::ipt_set::IptSetStorageHandle;
760 use crate::{HsIdKeypairSpecifier, HsIdPublicKeySpecifier};
761
762 const TEST_SVC_NICKNAME: &str = "test-svc";
764
765 #[test]
766 fn protocols() {
767 let pr = supported_hsservice_protocols();
768 let expected = "HSIntro=4-5 HSRend=2 HSDir=2".parse().unwrap();
769 assert_eq!(pr, expected);
770 }
771
772 pub(crate) fn create_keymgr(temp_dir: &TestTempDir) -> TestTempDirGuard<Arc<KeyMgr>> {
774 temp_dir.subdir_used_by("keystore", |keystore_dir| {
775 let keystore = ArtiNativeKeystore::from_path_and_mistrust(
776 keystore_dir,
777 &Mistrust::new_dangerously_trust_everyone(),
778 )
779 .unwrap();
780
781 Arc::new(
782 KeyMgrBuilder::default()
783 .primary_store(Box::new(keystore))
784 .build()
785 .unwrap(),
786 )
787 })
788 }
789
790 #[allow(clippy::let_and_return)] pub(crate) fn mk_state_instance(dir: &Path, nick: impl Display) -> InstanceStateHandle {
792 let nick = HsNickname::new(nick.to_string()).unwrap();
793 let mistrust = fs_mistrust::Mistrust::new_dangerously_trust_everyone();
794 let state_dir = StateDirectory::new(dir, &mistrust).unwrap();
795 let instance = state_dir.acquire_instance(&nick).unwrap();
796 instance
797 }
798
799 pub(crate) fn create_storage_handles(
800 dir: &Path,
801 ) -> (
802 tor_persist::state_dir::InstanceStateHandle,
803 IptSetStorageHandle,
804 ) {
805 let nick = HsNickname::try_from("allium".to_owned()).unwrap();
806 create_storage_handles_from_state_dir(dir, &nick)
807 }
808
809 pub(crate) fn create_storage_handles_from_state_dir(
810 state_dir: &Path,
811 nick: &HsNickname,
812 ) -> (
813 tor_persist::state_dir::InstanceStateHandle,
814 IptSetStorageHandle,
815 ) {
816 let instance = mk_state_instance(state_dir, nick);
817 let iptpub_state_handle = instance.storage_handle("iptpub").unwrap();
818 (instance, iptpub_state_handle)
819 }
820
821 macro_rules! maybe_generate_hsid {
822 ($keymgr:expr, $offline_hsid:expr) => {{
823 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
824 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
825 let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
826
827 assert!($keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
828 assert!($keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
829
830 maybe_generate_hsid(&$keymgr, &nickname, $offline_hsid, Default::default()).unwrap();
831 }};
832 }
833
834 fn create_hsid() -> (HsIdKeypair, HsIdKey) {
836 let mut rng = testing_rng();
837 let keypair = ed25519::Keypair::generate(&mut rng);
838
839 let id_pub = HsIdKey::from(keypair.verifying_key());
840 let id_keypair = HsIdKeypair::from(ed25519::ExpandedKeypair::from(&keypair));
841
842 (id_keypair, id_pub)
843 }
844
845 #[test]
846 fn generate_hsid() {
847 let temp_dir = test_temp_dir!();
848 let keymgr = create_keymgr(&temp_dir);
849
850 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
851 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
852
853 assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
854 maybe_generate_hsid!(keymgr, false );
855 assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_some());
856 }
857
858 #[test]
859 fn hsid_keypair_already_exists() {
860 let temp_dir = test_temp_dir!();
861 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
862 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
863 let keymgr = create_keymgr(&temp_dir);
864
865 let (existing_hsid_keypair, existing_hsid_public) = create_hsid();
867 let existing_keypair: ed25519::ExpandedKeypair = existing_hsid_keypair.into();
868 let existing_hsid_keypair = HsIdKeypair::from(existing_keypair);
869
870 keymgr
871 .insert(
872 existing_hsid_keypair,
873 &hsid_spec,
874 KeystoreSelector::Primary,
875 true,
876 )
877 .unwrap();
878
879 maybe_generate_hsid(
880 &keymgr,
881 &nickname,
882 false, Default::default(),
884 )
885 .unwrap();
886
887 let keypair = keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().unwrap();
888 let pk: HsIdKey = (&keypair).into();
889
890 assert_eq!(pk.as_ref(), existing_hsid_public.as_ref());
891 }
892
893 #[test]
894 #[ignore] fn generate_hsid_offline_hsid() {
896 let temp_dir = test_temp_dir!();
897 let keymgr = create_keymgr(&temp_dir);
898
899 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
900 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
901 let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
902
903 maybe_generate_hsid!(keymgr, true );
904
905 assert!(keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
906 assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
907 }
908
909 #[test]
910 #[ignore] fn generate_hsid_corrupt_keystore() {
912 let temp_dir = test_temp_dir!();
913 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
914 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
915 let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
916
917 let keymgr = create_keymgr(&temp_dir);
918
919 let (hsid_keypair, _hsid_public) = create_hsid();
920 let (_hsid_keypair, hsid_public) = create_hsid();
921
922 keymgr
923 .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
924 .unwrap();
925
926 keymgr
928 .insert(hsid_public, &pub_hsid_spec, KeystoreSelector::Primary, true)
929 .unwrap();
930
931 assert!(
932 maybe_generate_hsid(
933 &keymgr,
934 &nickname,
935 false, Default::default()
937 )
938 .is_err()
939 );
940 }
941
942 #[test]
943 fn onion_address() {
944 let temp_dir = test_temp_dir!();
945 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
946 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
947 let keymgr = create_keymgr(&temp_dir);
948
949 let (hsid_keypair, hsid_public) = create_hsid();
950
951 keymgr
953 .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
954 .unwrap();
955
956 let config = OnionServiceConfigBuilder::default()
957 .nickname(nickname)
958 .build()
959 .unwrap();
960
961 let state_dir = StateDirectory::new(
962 temp_dir.as_path_untracked(),
963 &fs_mistrust::Mistrust::new_dangerously_trust_everyone(),
964 )
965 .unwrap();
966
967 let service = OnionService::builder()
968 .config(config)
969 .keymgr(Arc::clone(&*keymgr))
970 .state_dir(state_dir)
971 .build()
972 .unwrap();
973
974 let hsid = HsId::from(hsid_public);
975 assert_eq!(service.onion_address().unwrap(), hsid);
976
977 drop(temp_dir); }
979}