1#![cfg_attr(docsrs, feature(doc_auto_cfg, 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#![warn(clippy::cognitive_complexity)]
15#![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_duration_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)] #![cfg_attr(
49 not(all(feature = "full", feature = "experimental")),
50 allow(unused, unreachable_pub)
51)]
52
53#[macro_use] mod time_store;
55
56mod internal_prelude;
57
58mod anon_level;
59pub mod config;
60mod err;
61mod helpers;
62mod ipt_establish;
63mod ipt_lid;
64mod ipt_mgr;
65mod ipt_set;
66mod keys;
67mod pow;
68mod publish;
69mod rend_handshake;
70mod replay;
71mod req;
72pub mod status;
73mod timeout_track;
74
75#[doc(hidden)]
85pub mod timeout_track_for_doctests_unstable_no_semver_guarantees {
86 pub use crate::timeout_track::*;
87}
88#[doc(hidden)]
89pub mod time_store_for_doctests_unstable_no_semver_guarantees {
90 pub use crate::time_store::*;
91}
92
93use std::pin::Pin;
94
95use internal_prelude::*;
96
97pub use anon_level::Anonymity;
100pub use config::OnionServiceConfig;
101pub use err::{ClientError, EstablishSessionError, FatalError, IntroRequestError, StartupError};
102pub use ipt_mgr::IptError;
103use keys::HsTimePeriodKeySpecifier;
104pub use keys::{
105 BlindIdKeypairSpecifier, BlindIdPublicKeySpecifier, DescSigningKeypairSpecifier,
106 HsIdKeypairSpecifier, HsIdPublicKeySpecifier,
107};
108use pow::{NewPowManager, PowManager};
109pub use publish::UploadError as DescUploadError;
110pub use req::{RendRequest, StreamRequest};
111pub use tor_hscrypto::pk::HsId;
112use tor_keymgr::KeystoreEntry;
113pub use tor_persist::hsnickname::{HsNickname, InvalidNickname};
114
115pub use helpers::handle_rend_requests;
116
117#[cfg(feature = "onion-service-cli-extra")]
118use tor_netdir::NetDir;
119
120pub(crate) type LinkSpecs = Vec<tor_linkspec::EncodedLinkSpec>;
124
125pub(crate) type NtorPublicKey = curve25519::PublicKey;
130
131#[must_use = "a hidden service object will terminate the service when dropped"]
139pub struct RunningOnionService {
140 inner: Mutex<SvcInner>,
142 nickname: HsNickname,
144 keymgr: Arc<KeyMgr>,
146}
147
148struct SvcInner {
150 config_tx: postage::watch::Sender<Arc<OnionServiceConfig>>,
152
153 _shutdown_tx: postage::broadcast::Sender<void::Void>,
155
156 status_tx: StatusSender,
159
160 #[allow(clippy::type_complexity)]
162 unlaunched: Option<(
163 Pin<Box<dyn Stream<Item = RendRequest> + Send + Sync>>,
164 Box<dyn Launchable + Send + Sync>,
165 )>,
166}
167
168struct ForLaunch<R: Runtime> {
170 publisher: Publisher<R, publish::Real<R>>,
176
177 ipt_mgr: IptManager<R, crate::ipt_mgr::Real<R>>,
183
184 ipt_mgr_view: IptsManagerView,
188
189 pow_manager: Arc<PowManager<R>>,
191}
192
193trait Launchable: Send + Sync {
196 fn launch(self: Box<Self>) -> Result<(), StartupError>;
198}
199
200impl<R: Runtime> Launchable for ForLaunch<R> {
201 fn launch(self: Box<Self>) -> Result<(), StartupError> {
202 self.ipt_mgr.launch_background_tasks(self.ipt_mgr_view)?;
203 self.publisher.launch()?;
204 self.pow_manager.launch()?;
205
206 Ok(())
207 }
208}
209
210#[derive(PartialEq)]
214#[must_use]
215pub(crate) enum ShutdownStatus {
216 Continue,
218 Terminate,
220}
221
222impl From<oneshot::Canceled> for ShutdownStatus {
223 fn from(_: oneshot::Canceled) -> ShutdownStatus {
224 ShutdownStatus::Terminate
225 }
226}
227
228#[derive(Builder)]
237#[builder(build_fn(private, name = "build_unvalidated", error = "FatalError"))]
238pub struct OnionService {
239 config: OnionServiceConfig,
241 keymgr: Arc<KeyMgr>,
243 state_dir: StateDirectory,
245}
246
247impl OnionService {
248 pub fn builder() -> OnionServiceBuilder {
250 OnionServiceBuilder::default()
251 }
252
253 pub fn launch<R>(
263 self,
264 runtime: R,
265 netdir_provider: Arc<dyn NetDirProvider>,
266 circ_pool: Arc<HsCircPool<R>>,
267 path_resolver: Arc<tor_config_path::CfgPathResolver>,
268 ) -> Result<(Arc<RunningOnionService>, impl Stream<Item = RendRequest>), StartupError>
269 where
270 R: Runtime,
271 {
272 let OnionService {
273 config,
274 keymgr,
275 state_dir,
276 } = self;
277
278 let nickname = config.nickname.clone();
279
280 let offline_hsid = false;
284
285 let selector = KeystoreSelector::Primary;
287 maybe_generate_hsid(&keymgr, &config.nickname, offline_hsid, selector)?;
288
289 if config.restricted_discovery.enabled {
290 info!(
291 nickname=%nickname,
292 "Launching onion service in restricted discovery mode"
293 );
294 } else {
295 info!(
296 nickname=%nickname,
297 "Launching onion service"
298 );
299 }
300
301 let state_handle = state_dir
302 .acquire_instance(&config.nickname)
303 .map_err(StartupError::StateDirectoryInaccessible)?;
304
305 let iptpub_storage_handle = state_handle
308 .storage_handle("iptpub")
309 .map_err(StartupError::StateDirectoryInaccessible)?;
310
311 let status_tx = StatusSender::new(OnionServiceStatus::new_shutdown());
312 let (config_tx, config_rx) = postage::watch::channel_with(Arc::new(config));
313
314 let pow_manager_storage_handle = state_handle
315 .storage_handle("pow_manager")
316 .map_err(StartupError::StateDirectoryInaccessible)?;
317 let pow_nonce_dir = state_handle
318 .raw_subdir("pow_nonces")
319 .map_err(StartupError::StateDirectoryInaccessible)?;
320 let NewPowManager {
321 pow_manager,
322 rend_req_tx,
323 rend_req_rx,
324 publisher_update_rx,
325 } = PowManager::new(
326 runtime.clone(),
327 nickname.clone(),
328 pow_nonce_dir,
329 keymgr.clone(),
330 pow_manager_storage_handle,
331 netdir_provider.clone(),
332 status_tx.clone().into(),
333 config_rx.clone(),
334 )?;
335
336 let (shutdown_tx, shutdown_rx) = broadcast::channel(0);
337
338 let (ipt_mgr_view, publisher_view) =
339 crate::ipt_set::ipts_channel(&runtime, iptpub_storage_handle)?;
340
341 let ipt_mgr = IptManager::new(
342 runtime.clone(),
343 netdir_provider.clone(),
344 nickname.clone(),
345 config_rx.clone(),
346 rend_req_tx,
347 shutdown_rx.clone(),
348 &state_handle,
349 crate::ipt_mgr::Real {
350 circ_pool: circ_pool.clone(),
351 },
352 keymgr.clone(),
353 status_tx.clone().into(),
354 )?;
355
356 let publisher: Publisher<R, publish::Real<R>> = Publisher::new(
357 runtime,
358 nickname.clone(),
359 netdir_provider,
360 circ_pool,
361 publisher_view,
362 config_rx,
363 status_tx.clone().into(),
364 Arc::clone(&keymgr),
365 path_resolver,
366 pow_manager.clone(),
367 publisher_update_rx,
368 );
369
370 let svc = Arc::new(RunningOnionService {
371 nickname,
372 keymgr,
373 inner: Mutex::new(SvcInner {
374 config_tx,
375 _shutdown_tx: shutdown_tx,
376 status_tx,
377 unlaunched: Some((
378 rend_req_rx,
379 Box::new(ForLaunch {
380 publisher,
381 ipt_mgr,
382 ipt_mgr_view,
383 pow_manager,
384 }),
385 )),
386 }),
387 });
388
389 let stream = svc.launch()?;
390 Ok((svc, stream))
391 }
392
393 pub fn onion_address(&self) -> Option<HsId> {
401 onion_address(&self.keymgr, &self.config.nickname)
402 }
403
404 #[deprecated = "Use the new onion_address method instead"]
408 pub fn onion_name(&self) -> Option<HsId> {
409 self.onion_address()
410 }
411
412 pub fn generate_identity_key(&self, selector: KeystoreSelector) -> Result<HsId, StartupError> {
431 let offline_hsid = false;
435
436 maybe_generate_hsid(&self.keymgr, &self.config.nickname, offline_hsid, selector)
437 }
438
439 #[cfg(feature = "onion-service-cli-extra")]
447 pub fn list_expired_keys(&self, netdir: &NetDir) -> tor_keymgr::Result<Vec<KeystoreEntry>> {
448 list_expired_keys_for_service(
449 &netdir.hs_all_time_periods(),
450 self.config.nickname(),
451 &self.keymgr,
452 )
453 }
454}
455
456impl OnionServiceBuilder {
457 pub fn build(&self) -> Result<OnionService, StartupError> {
459 let svc = self.build_unvalidated()?;
460 Ok(svc)
461 }
462}
463
464impl RunningOnionService {
465 pub fn reconfigure(
471 &self,
472 new_config: OnionServiceConfig,
473 how: Reconfigure,
474 ) -> Result<(), ReconfigureError> {
475 let mut inner = self.inner.lock().expect("lock poisoned");
476 inner.config_tx.try_maybe_send(|cur_config| {
477 let new_config = cur_config.for_transition_to(new_config, how)?;
478 Ok(match how {
479 tor_config::Reconfigure::CheckAllOrNothing => Arc::clone(cur_config),
481 _ => Arc::new(new_config),
483 })
484 })
485
486 }
490
491 pub fn status(&self) -> OnionServiceStatus {
500 self.inner.lock().expect("poisoned lock").status_tx.get()
501 }
502
503 pub fn status_events(&self) -> OnionServiceStatusStream {
506 self.inner
507 .lock()
508 .expect("poisoned lock")
509 .status_tx
510 .subscribe()
511 }
512
513 fn launch(self: &Arc<Self>) -> Result<impl Stream<Item = RendRequest> + use<>, StartupError> {
519 let (rend_req_rx, launch) = {
520 let mut inner = self.inner.lock().expect("poisoned lock");
521 inner
522 .unlaunched
523 .take()
524 .ok_or(StartupError::AlreadyLaunched)?
525 };
526
527 match launch.launch() {
528 Ok(()) => {}
529 Err(e) => {
530 return Err(e);
531 }
532 }
533
534 Ok(rend_req_rx)
540 }
541
542 pub fn onion_address(&self) -> Option<HsId> {
562 onion_address(&self.keymgr, &self.nickname)
563 }
564
565 #[deprecated = "Use the new onion_address method instead"]
569 pub fn onion_name(&self) -> Option<HsId> {
570 self.onion_address()
571 }
572}
573
574fn maybe_generate_hsid(
578 keymgr: &Arc<KeyMgr>,
579 nickname: &HsNickname,
580 offline_hsid: bool,
581 selector: KeystoreSelector,
582) -> Result<HsId, StartupError> {
583 if offline_hsid {
584 unimplemented!("offline hsid mode");
585 }
586
587 let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
588
589 let kp = keymgr
590 .get::<HsIdKey>(&hsid_spec)
591 .map_err(|cause| StartupError::Keystore {
592 action: "read",
593 cause,
594 })?;
595
596 let mut rng = tor_llcrypto::rng::CautiousRng;
597 let (hsid, generated) = match kp {
598 Some(kp) => (kp.id(), false),
599 None => {
600 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
604 let kp = keymgr
605 .generate::<HsIdKeypair>(&hsid_spec, selector, &mut rng, false )
606 .map_err(|cause| StartupError::Keystore {
607 action: "generate",
608 cause,
609 })?;
610
611 (HsIdKey::from(&kp).id(), true)
612 }
613 };
614
615 if generated {
616 info!(
617 "Generated a new identity for service {nickname}: {}",
618 hsid.display_redacted()
619 );
620 } else {
621 info!(
624 "Using existing identity for service {nickname}: {}",
625 hsid.display_redacted()
626 );
627 }
628
629 Ok(hsid)
630}
631
632fn onion_address(keymgr: &KeyMgr, nickname: &HsNickname) -> Option<HsId> {
644 let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
645
646 keymgr
647 .get::<HsIdKey>(&hsid_spec)
648 .ok()?
649 .map(|hsid| hsid.id())
650}
651
652pub fn supported_hsservice_protocols() -> tor_protover::Protocols {
655 use tor_protover::named::*;
656 [
659 HSINTRO_V3,
661 HSINTRO_RATELIM,
662 HSREND_V3,
663 HSDIR_V3,
664 ]
665 .into_iter()
666 .collect()
667}
668
669fn list_expired_keys_for_service<'a>(
673 relevant_periods: &[HsDirParams],
674 nickname: &HsNickname,
675 keymgr: &'a KeyMgr,
676) -> tor_keymgr::Result<Vec<KeystoreEntry<'a>>> {
677 let arti_pat = tor_keymgr::KeyPathPattern::Arti(format!("hss/{}/*", nickname));
678 let possibly_relevant_keys = keymgr.list_matching(&arti_pat)?;
679 let mut expired_keys = Vec::new();
680
681 for entry in possibly_relevant_keys {
682 let key_path = entry.key_path();
683 let mut append_if_expired = |spec: &dyn HsTimePeriodKeySpecifier| {
684 if spec.nickname() != nickname {
685 return Err(internal!(
686 "keymgr gave us key {spec:?} that doesn't match our pattern {arti_pat:?}"
687 )
688 .into());
689 }
690 let is_expired = relevant_periods
691 .iter()
692 .all(|p| &p.time_period() != spec.period());
693
694 if is_expired {
695 expired_keys.push(entry.clone());
696 }
697
698 tor_keymgr::Result::Ok(())
699 };
700
701 macro_rules! append_if_expired {
702 ($K:ty) => {{
703 if let Ok(spec) = <$K>::try_from(key_path) {
704 append_if_expired(&spec)?;
705 }
706 }};
707 }
708
709 append_if_expired!(BlindIdPublicKeySpecifier);
710 append_if_expired!(BlindIdKeypairSpecifier);
711 append_if_expired!(DescSigningKeypairSpecifier);
712 }
713
714 Ok(expired_keys)
715}
716
717#[cfg(test)]
718pub(crate) mod test {
719 #![allow(clippy::bool_assert_comparison)]
721 #![allow(clippy::clone_on_copy)]
722 #![allow(clippy::dbg_macro)]
723 #![allow(clippy::mixed_attributes_style)]
724 #![allow(clippy::print_stderr)]
725 #![allow(clippy::print_stdout)]
726 #![allow(clippy::single_char_pattern)]
727 #![allow(clippy::unwrap_used)]
728 #![allow(clippy::unchecked_duration_subtraction)]
729 #![allow(clippy::useless_vec)]
730 #![allow(clippy::needless_pass_by_value)]
731 use super::*;
733
734 use std::fmt::Display;
735 use std::path::Path;
736
737 use fs_mistrust::Mistrust;
738 use test_temp_dir::{TestTempDir, TestTempDirGuard, test_temp_dir};
739
740 use tor_basic_utils::test_rng::testing_rng;
741 use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder};
742 use tor_llcrypto::pk::ed25519;
743 use tor_persist::state_dir::InstanceStateHandle;
744
745 use crate::config::OnionServiceConfigBuilder;
746 use crate::ipt_set::IptSetStorageHandle;
747 use crate::{HsIdKeypairSpecifier, HsIdPublicKeySpecifier};
748
749 const TEST_SVC_NICKNAME: &str = "test-svc";
751
752 #[test]
753 fn protocols() {
754 let pr = supported_hsservice_protocols();
755 let expected = "HSIntro=4-5 HSRend=2 HSDir=2".parse().unwrap();
756 assert_eq!(pr, expected);
757 }
758
759 pub(crate) fn create_keymgr(temp_dir: &TestTempDir) -> TestTempDirGuard<Arc<KeyMgr>> {
761 temp_dir.subdir_used_by("keystore", |keystore_dir| {
762 let keystore = ArtiNativeKeystore::from_path_and_mistrust(
763 keystore_dir,
764 &Mistrust::new_dangerously_trust_everyone(),
765 )
766 .unwrap();
767
768 Arc::new(
769 KeyMgrBuilder::default()
770 .primary_store(Box::new(keystore))
771 .build()
772 .unwrap(),
773 )
774 })
775 }
776
777 #[allow(clippy::let_and_return)] pub(crate) fn mk_state_instance(dir: &Path, nick: impl Display) -> InstanceStateHandle {
779 let nick = HsNickname::new(nick.to_string()).unwrap();
780 let mistrust = fs_mistrust::Mistrust::new_dangerously_trust_everyone();
781 let state_dir = StateDirectory::new(dir, &mistrust).unwrap();
782 let instance = state_dir.acquire_instance(&nick).unwrap();
783 instance
784 }
785
786 pub(crate) fn create_storage_handles(
787 dir: &Path,
788 ) -> (
789 tor_persist::state_dir::InstanceStateHandle,
790 IptSetStorageHandle,
791 ) {
792 let nick = HsNickname::try_from("allium".to_owned()).unwrap();
793 create_storage_handles_from_state_dir(dir, &nick)
794 }
795
796 pub(crate) fn create_storage_handles_from_state_dir(
797 state_dir: &Path,
798 nick: &HsNickname,
799 ) -> (
800 tor_persist::state_dir::InstanceStateHandle,
801 IptSetStorageHandle,
802 ) {
803 let instance = mk_state_instance(state_dir, nick);
804 let iptpub_state_handle = instance.storage_handle("iptpub").unwrap();
805 (instance, iptpub_state_handle)
806 }
807
808 macro_rules! maybe_generate_hsid {
809 ($keymgr:expr, $offline_hsid:expr) => {{
810 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
811 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
812 let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
813
814 assert!($keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
815 assert!($keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
816
817 maybe_generate_hsid(&$keymgr, &nickname, $offline_hsid, Default::default()).unwrap();
818 }};
819 }
820
821 fn create_hsid() -> (HsIdKeypair, HsIdKey) {
823 let mut rng = testing_rng();
824 let keypair = ed25519::Keypair::generate(&mut rng);
825
826 let id_pub = HsIdKey::from(keypair.verifying_key());
827 let id_keypair = HsIdKeypair::from(ed25519::ExpandedKeypair::from(&keypair));
828
829 (id_keypair, id_pub)
830 }
831
832 #[test]
833 fn generate_hsid() {
834 let temp_dir = test_temp_dir!();
835 let keymgr = create_keymgr(&temp_dir);
836
837 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
838 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
839
840 assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
841 maybe_generate_hsid!(keymgr, false );
842 assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_some());
843 }
844
845 #[test]
846 fn hsid_keypair_already_exists() {
847 let temp_dir = test_temp_dir!();
848 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
849 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
850 let keymgr = create_keymgr(&temp_dir);
851
852 let (existing_hsid_keypair, existing_hsid_public) = create_hsid();
854 let existing_keypair: ed25519::ExpandedKeypair = existing_hsid_keypair.into();
855 let existing_hsid_keypair = HsIdKeypair::from(existing_keypair);
856
857 keymgr
858 .insert(
859 existing_hsid_keypair,
860 &hsid_spec,
861 KeystoreSelector::Primary,
862 true,
863 )
864 .unwrap();
865
866 maybe_generate_hsid(
867 &keymgr,
868 &nickname,
869 false, Default::default(),
871 )
872 .unwrap();
873
874 let keypair = keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().unwrap();
875 let pk: HsIdKey = (&keypair).into();
876
877 assert_eq!(pk.as_ref(), existing_hsid_public.as_ref());
878 }
879
880 #[test]
881 #[ignore] fn generate_hsid_offline_hsid() {
883 let temp_dir = test_temp_dir!();
884 let keymgr = create_keymgr(&temp_dir);
885
886 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
887 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
888 let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
889
890 maybe_generate_hsid!(keymgr, true );
891
892 assert!(keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
893 assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
894 }
895
896 #[test]
897 #[ignore] fn generate_hsid_corrupt_keystore() {
899 let temp_dir = test_temp_dir!();
900 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
901 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
902 let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
903
904 let keymgr = create_keymgr(&temp_dir);
905
906 let (hsid_keypair, _hsid_public) = create_hsid();
907 let (_hsid_keypair, hsid_public) = create_hsid();
908
909 keymgr
910 .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
911 .unwrap();
912
913 keymgr
915 .insert(hsid_public, &pub_hsid_spec, KeystoreSelector::Primary, true)
916 .unwrap();
917
918 assert!(
919 maybe_generate_hsid(
920 &keymgr,
921 &nickname,
922 false, Default::default()
924 )
925 .is_err()
926 );
927 }
928
929 #[test]
930 fn onion_address() {
931 let temp_dir = test_temp_dir!();
932 let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
933 let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
934 let keymgr = create_keymgr(&temp_dir);
935
936 let (hsid_keypair, hsid_public) = create_hsid();
937
938 keymgr
940 .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
941 .unwrap();
942
943 let config = OnionServiceConfigBuilder::default()
944 .nickname(nickname)
945 .build()
946 .unwrap();
947
948 let state_dir = StateDirectory::new(
949 temp_dir.as_path_untracked(),
950 &fs_mistrust::Mistrust::new_dangerously_trust_everyone(),
951 )
952 .unwrap();
953
954 let service = OnionService::builder()
955 .config(config)
956 .keymgr(Arc::clone(&*keymgr))
957 .state_dir(state_dir)
958 .build()
959 .unwrap();
960
961 let hsid = HsId::from(hsid_public);
962 assert_eq!(service.onion_address().unwrap(), hsid);
963
964 drop(temp_dir); }
966}