1
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg))]
2
#![doc = include_str!("../README.md")]
3
// @@ begin lint list maintained by maint/add_warning @@
4
#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
5
#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
6
#![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)] // This can reasonably be done for explicitness
39
#![allow(clippy::uninlined_format_args)]
40
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
41
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
42
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
43
#![allow(clippy::needless_lifetimes)] // See arti#1765
44
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
45

            
46
// TODO #1645 (either remove this, or decide to have it everywhere)
47
#![cfg_attr(
48
    not(all(feature = "full", feature = "experimental")),
49
    allow(unused, unreachable_pub)
50
)]
51

            
52
#[macro_use] // SerdeStringOrTransparent
53
mod time_store;
54

            
55
mod internal_prelude;
56

            
57
mod anon_level;
58
pub mod config;
59
mod err;
60
mod helpers;
61
mod ipt_establish;
62
mod ipt_lid;
63
mod ipt_mgr;
64
mod ipt_set;
65
mod keys;
66
mod publish;
67
mod rend_handshake;
68
mod replay;
69
mod req;
70
pub mod status;
71
mod timeout_track;
72

            
73
// rustdoc doctests can't use crate-public APIs, so are broken if provided for private items.
74
// So we export the whole module again under this name.
75
// Supports the Example in timeout_track.rs's module-level docs.
76
//
77
// Any out-of-crate user needs to write this ludicrous name in their code,
78
// so we don't need to put any warnings in the docs for the individual items.)
79
//
80
// (`#[doc(hidden)] pub mod timeout_track;` would work for the test but it would
81
// completely suppress the actual documentation, which is not what we want.)
82
#[doc(hidden)]
83
pub mod timeout_track_for_doctests_unstable_no_semver_guarantees {
84
    pub use crate::timeout_track::*;
85
}
86
#[doc(hidden)]
87
pub mod time_store_for_doctests_unstable_no_semver_guarantees {
88
    pub use crate::time_store::*;
89
}
90

            
91
use internal_prelude::*;
92

            
93
// ---------- public exports ----------
94

            
95
pub use anon_level::Anonymity;
96
pub use config::OnionServiceConfig;
97
pub use err::{ClientError, EstablishSessionError, FatalError, IntroRequestError, StartupError};
98
pub use ipt_mgr::IptError;
99
pub use keys::{
100
    BlindIdKeypairSpecifier, BlindIdPublicKeySpecifier, DescSigningKeypairSpecifier,
101
    HsIdKeypairSpecifier, HsIdPublicKeySpecifier,
102
};
103
pub use publish::UploadError as DescUploadError;
104
pub use req::{RendRequest, StreamRequest};
105
pub use tor_hscrypto::pk::HsId;
106
pub use tor_persist::hsnickname::{HsNickname, InvalidNickname};
107

            
108
pub use helpers::handle_rend_requests;
109

            
110
//---------- top-level service implementation (types and methods) ----------
111

            
112
/// Convenience alias for link specifiers of an intro point
113
pub(crate) type LinkSpecs = Vec<tor_linkspec::EncodedLinkSpec>;
114

            
115
/// Convenient type alias for an ntor public key
116
// TODO (#1022) maybe this should be
117
// `tor_proto::crypto::handshake::ntor::NtorPublicKey`,
118
// or a unified OnionKey type.
119
pub(crate) type NtorPublicKey = curve25519::PublicKey;
120

            
121
/// A handle to a running instance of an onion service.
122
//
123
// TODO (#1228): Write more.
124
// TODO (#1247): Choose a better name for this struct
125
//
126
// (APIs should return Arc<OnionService>)
127
#[must_use = "a hidden service object will terminate the service when dropped"]
128
pub struct RunningOnionService {
129
    /// The mutable implementation details of this onion service.
130
    inner: Mutex<SvcInner>,
131
    /// The nickname of this service.
132
    nickname: HsNickname,
133
    /// The key manager, used for accessing the underlying key stores.
134
    keymgr: Arc<KeyMgr>,
135
}
136

            
137
/// Implementation details for an onion service.
138
struct SvcInner {
139
    /// Configuration information about this service.
140
    config_tx: postage::watch::Sender<Arc<OnionServiceConfig>>,
141

            
142
    /// A oneshot that will be dropped when this object is dropped.
143
    _shutdown_tx: postage::broadcast::Sender<void::Void>,
144

            
145
    /// Postage sender, used to tell subscribers about changes in the status of
146
    /// this onion service.
147
    status_tx: StatusSender,
148

            
149
    /// Handles that we'll take ownership of when launching the service.
150
    unlaunched: Option<(
151
        mpsc::Receiver<RendRequest>,
152
        Box<dyn Launchable + Send + Sync>,
153
    )>,
154
}
155

            
156
/// Objects and handles needed to launch an onion service.
157
struct ForLaunch<R: Runtime> {
158
    /// An unlaunched handle for the HsDesc publisher.
159
    ///
160
    /// This publisher is responsible for determining when we need to upload a
161
    /// new set of HsDescs, building them, and publishing them at the correct
162
    /// HsDirs.
163
    publisher: Publisher<R, publish::Real<R>>,
164

            
165
    /// Our handler for the introduction point manager.
166
    ///
167
    /// This manager is responsible for selecting introduction points,
168
    /// maintaining our connections to them, and telling the publisher which ones
169
    /// are publicly available.
170
    ipt_mgr: IptManager<R, crate::ipt_mgr::Real<R>>,
171

            
172
    /// A handle used by the ipt manager to send Ipts to the publisher.
173
    ///
174
    ///
175
    ipt_mgr_view: IptsManagerView,
176
}
177

            
178
/// Private trait used to type-erase `ForLaunch<R>`, so that we don't need to
179
/// parameterize OnionService on `<R>`.
180
trait Launchable: Send + Sync {
181
    /// Launch
182
    fn launch(self: Box<Self>) -> Result<(), StartupError>;
183
}
184

            
185
impl<R: Runtime> Launchable for ForLaunch<R> {
186
    fn launch(self: Box<Self>) -> Result<(), StartupError> {
187
        self.ipt_mgr.launch_background_tasks(self.ipt_mgr_view)?;
188
        self.publisher.launch()?;
189

            
190
        Ok(())
191
    }
192
}
193

            
194
/// Return value from one call to the main loop iteration
195
///
196
/// Used by the publisher reactor and by the [`IptManager`].
197
#[derive(PartialEq)]
198
#[must_use]
199
pub(crate) enum ShutdownStatus {
200
    /// We should continue to operate this component
201
    Continue,
202
    /// We should shut down: the service, or maybe the whole process, is shutting down
203
    Terminate,
204
}
205

            
206
impl From<oneshot::Canceled> for ShutdownStatus {
207
    fn from(_: oneshot::Canceled) -> ShutdownStatus {
208
        ShutdownStatus::Terminate
209
    }
210
}
211

            
212
/// A handle to an instance of an onion service, which may or may not be running.
213
///
214
/// To construct an `OnionService`, use [`OnionServiceBuilder`].
215
/// It will not start handling requests until you call its
216
/// [``.launch()``](OnionService::launch) method.
217
///
218
/// Note: the identity key (HsId) of the service is not generated until
219
/// [``.launch()``](OnionService::launch) is called.
220
370
#[derive(Builder)]
221
#[builder(build_fn(private, name = "build_unvalidated", error = "FatalError"))]
222
pub struct OnionService {
223
    /// The current configuration.
224
    config: OnionServiceConfig,
225
    /// The key manager, used for accessing the underlying key stores.
226
    keymgr: Arc<KeyMgr>,
227
    /// The location on disk where the persistent data is stored.
228
    state_dir: StateDirectory,
229
}
230

            
231
impl OnionService {
232
    /// Create an [`OnionServiceBuilder`].
233
74
    pub fn builder() -> OnionServiceBuilder {
234
74
        OnionServiceBuilder::default()
235
74
    }
236

            
237
    /// Tell this onion service to begin running, and return a
238
    /// [`RunningOnionService`] and its stream of rendezvous requests.
239
    ///
240
    /// You can turn the resulting stream into a stream of [`StreamRequest`]
241
    /// using the [`handle_rend_requests`] helper function.
242
    ///
243
    /// Once the `RunningOnionService` is dropped, the onion service will stop
244
    /// publishing, and stop accepting new introduction requests.  Existing
245
    /// streams and rendezvous circuits will remain open.
246
    pub fn launch<R>(
247
        self,
248
        runtime: R,
249
        netdir_provider: Arc<dyn NetDirProvider>,
250
        circ_pool: Arc<HsCircPool<R>>,
251
        path_resolver: Arc<tor_config_path::CfgPathResolver>,
252
    ) -> Result<(Arc<RunningOnionService>, impl Stream<Item = RendRequest>), StartupError>
253
    where
254
        R: Runtime,
255
    {
256
        let OnionService {
257
            config,
258
            keymgr,
259
            state_dir,
260
        } = self;
261

            
262
        let nickname = config.nickname.clone();
263

            
264
        // TODO (#1194): add a config option for specifying whether to expect the KS_hsid to be stored
265
        // offline
266
        //let offline_hsid = config.offline_hsid;
267
        let offline_hsid = false;
268

            
269
        // TODO (#1106): make this configurable
270
        let selector = KeystoreSelector::Primary;
271
        maybe_generate_hsid(&keymgr, &config.nickname, offline_hsid, selector)?;
272

            
273
        if config.restricted_discovery.enabled {
274
            info!(
275
                nickname=%nickname,
276
                "Launching onion service in restricted discovery mode"
277
            );
278
        } else {
279
            info!(
280
                nickname=%nickname,
281
                "Launching onion service"
282
            );
283
        }
284

            
285
        let state_handle = state_dir
286
            .acquire_instance(&config.nickname)
287
            .map_err(StartupError::StateDirectoryInaccessible)?;
288

            
289
        // We pass the "cooked" handle, with the storage key embedded, to ipt_set,
290
        // since the ipt_set code doesn't otherwise have access to the HS nickname.
291
        let iptpub_storage_handle = state_handle
292
            .storage_handle("iptpub")
293
            .map_err(StartupError::StateDirectoryInaccessible)?;
294

            
295
        // If the HS implementation is stalled somehow, this is a local problem.
296
        // We shouldn't kill the HS even if this is the oldest data in the system.
297
        let (rend_req_tx, rend_req_rx) = mpsc_channel_no_memquota(32);
298

            
299
        let (shutdown_tx, shutdown_rx) = broadcast::channel(0);
300
        let (config_tx, config_rx) = postage::watch::channel_with(Arc::new(config));
301

            
302
        let (ipt_mgr_view, publisher_view) =
303
            crate::ipt_set::ipts_channel(&runtime, iptpub_storage_handle)?;
304

            
305
        let status_tx = StatusSender::new(OnionServiceStatus::new_shutdown());
306

            
307
        let ipt_mgr = IptManager::new(
308
            runtime.clone(),
309
            netdir_provider.clone(),
310
            nickname.clone(),
311
            config_rx.clone(),
312
            rend_req_tx,
313
            shutdown_rx.clone(),
314
            &state_handle,
315
            crate::ipt_mgr::Real {
316
                circ_pool: circ_pool.clone(),
317
            },
318
            keymgr.clone(),
319
            status_tx.clone().into(),
320
        )?;
321

            
322
        let publisher: Publisher<R, publish::Real<R>> = Publisher::new(
323
            runtime,
324
            nickname.clone(),
325
            netdir_provider,
326
            circ_pool,
327
            publisher_view,
328
            config_rx,
329
            status_tx.clone().into(),
330
            Arc::clone(&keymgr),
331
            path_resolver,
332
        );
333

            
334
        let svc = Arc::new(RunningOnionService {
335
            nickname,
336
            keymgr,
337
            inner: Mutex::new(SvcInner {
338
                config_tx,
339
                _shutdown_tx: shutdown_tx,
340
                status_tx,
341
                unlaunched: Some((
342
                    rend_req_rx,
343
                    Box::new(ForLaunch {
344
                        publisher,
345
                        ipt_mgr,
346
                        ipt_mgr_view,
347
                    }),
348
                )),
349
            }),
350
        });
351

            
352
        let stream = svc.launch()?;
353
        Ok((svc, stream))
354
    }
355

            
356
    /// Return the onion address of this service.
357
    ///
358
    /// Clients must know the service's onion address in order to discover or
359
    /// connect to it.
360
    ///
361
    /// Returns `None` if the HsId of the service could not be found in any of the configured
362
    /// keystores.
363
74
    pub fn onion_address(&self) -> Option<HsId> {
364
74
        onion_address(&self.keymgr, &self.config.nickname)
365
74
    }
366

            
367
    /// Return the onion address of this service.
368
    ///
369
    /// See [`onion_address`](Self::onion_address)
370
    #[deprecated = "Use the new onion_address method instead"]
371
    pub fn onion_name(&self) -> Option<HsId> {
372
        self.onion_address()
373
    }
374

            
375
    /// Generate an identity key (KP_hs_id) for this service.
376
    ///
377
    /// If the keystore specified by `selector` contains an entry for the identity key
378
    /// of this service, it will be returned. Otherwise, a new key will be generated.
379
    ///
380
    /// Most users do not need to call this function: on [`launch`](`OnionService::launch`),
381
    /// the service will automatically generate its identity key if needed.
382
    /// You should only use this function if you need to know the KP_hs_id of the service
383
    /// before launching it.
384
    ///
385
    /// The `selector` argument is used for choosing the keystore in which to generate the keypair.
386
    /// While most users will want to write to the [`Primary`](KeystoreSelector::Primary), if you
387
    /// have configured this `TorClient` with a non-default keystore and wish to generate the
388
    /// keypair in it, you can do so by calling this function with a [KeystoreSelector::Id]
389
    /// specifying the keystore ID of your keystore.
390
    ///
391
    // Note: the selector argument exists for future-proofing reasons. We don't currently support
392
    // configuring custom or non-default keystores (see #1106).
393
    pub fn generate_identity_key(&self, selector: KeystoreSelector) -> Result<HsId, StartupError> {
394
        // TODO (#1194): add a config option for specifying whether to expect the KS_hsid to be stored
395
        // offline
396
        //let offline_hsid = config.offline_hsid;
397
        let offline_hsid = false;
398

            
399
        maybe_generate_hsid(&self.keymgr, &self.config.nickname, offline_hsid, selector)
400
    }
401
}
402

            
403
impl OnionServiceBuilder {
404
    /// Build the [`OnionService`]
405
74
    pub fn build(&self) -> Result<OnionService, StartupError> {
406
74
        let svc = self.build_unvalidated()?;
407
74
        Ok(svc)
408
74
    }
409
}
410

            
411
impl RunningOnionService {
412
    /// Change the configuration of this onion service.
413
    ///
414
    /// (Not everything can be changed here. At the very least we'll need to say
415
    /// that the identity of a service is fixed. We might want to make the
416
    /// storage  backing this, and the anonymity status, unchangeable.)
417
    pub fn reconfigure(
418
        &self,
419
        new_config: OnionServiceConfig,
420
        how: Reconfigure,
421
    ) -> Result<(), ReconfigureError> {
422
        let mut inner = self.inner.lock().expect("lock poisoned");
423
        inner.config_tx.try_maybe_send(|cur_config| {
424
            let new_config = cur_config.for_transition_to(new_config, how)?;
425
            Ok(match how {
426
                // We're only checking, so return the current configuration.
427
                tor_config::Reconfigure::CheckAllOrNothing => Arc::clone(cur_config),
428
                // We're replacing the configuration, and we didn't get an error.
429
                _ => Arc::new(new_config),
430
            })
431
        })
432

            
433
        // TODO (#1153, #1209): We need to make sure that the various tasks listening on
434
        // config_rx actually enforce the configuration, not only on new
435
        // connections, but existing ones.
436
    }
437

            
438
    /*
439
    /// Tell this onion service about some new short-term keys it can use.
440
    pub fn add_keys(&self, keys: ()) -> Result<(), Bug> {
441
        todo!() // TODO #1194
442
    }
443
    */
444

            
445
    /// Return the current status of this onion service.
446
    pub fn status(&self) -> OnionServiceStatus {
447
        self.inner.lock().expect("poisoned lock").status_tx.get()
448
    }
449

            
450
    /// Return a stream of events that will receive notifications of changes in
451
    /// this onion service's status.
452
    pub fn status_events(&self) -> OnionServiceStatusStream {
453
        self.inner
454
            .lock()
455
            .expect("poisoned lock")
456
            .status_tx
457
            .subscribe()
458
    }
459

            
460
    /// Tell this onion service to begin running, and return a
461
    /// stream of rendezvous requests on the service.
462
    ///
463
    /// You can turn the resulting stream into a stream of [`StreamRequest`]
464
    /// using the [`handle_rend_requests`] helper function.
465
    fn launch(self: &Arc<Self>) -> Result<impl Stream<Item = RendRequest>, StartupError> {
466
        let (rend_req_rx, launch) = {
467
            let mut inner = self.inner.lock().expect("poisoned lock");
468
            inner
469
                .unlaunched
470
                .take()
471
                .ok_or(StartupError::AlreadyLaunched)?
472
        };
473

            
474
        match launch.launch() {
475
            Ok(()) => {}
476
            Err(e) => {
477
                return Err(e);
478
            }
479
        }
480

            
481
        // This needs to launch at least the following tasks:
482
        //
483
        // TODO (#1194) If we decide to use separate disk-based key
484
        // provisioning, we need a task to monitor our keys directory.
485

            
486
        Ok(rend_req_rx)
487
    }
488

            
489
    /*
490
    /// Tell this onion service to stop running.
491
    ///
492
    /// It can be restarted with launch().
493
    ///
494
    /// You can also shut down an onion service completely by dropping the last
495
    /// Clone of it.
496
    pub fn pause(&self) {
497
        todo!() // TODO (#1231)
498
    }
499
    */
500

            
501
    /// Return the onion address of this service.
502
    ///
503
    /// Clients must know the service's onion address in order to discover or
504
    /// connect to it.
505
    ///
506
    /// Returns `None` if the HsId of the service could not be found in any of the configured
507
    /// keystores.
508
    pub fn onion_address(&self) -> Option<HsId> {
509
        onion_address(&self.keymgr, &self.nickname)
510
    }
511

            
512
    /// Return the onion address of this service.
513
    ///
514
    /// See [`onion_address`](Self::onion_address)
515
    #[deprecated = "Use the new onion_address method instead"]
516
    pub fn onion_name(&self) -> Option<HsId> {
517
        self.onion_address()
518
    }
519
}
520

            
521
/// Generate the identity key of the service, unless it already exists or `offline_hsid` is `true`.
522
//
523
// TODO (#1194): we don't support offline_hsid yet.
524
4
fn maybe_generate_hsid(
525
4
    keymgr: &Arc<KeyMgr>,
526
4
    nickname: &HsNickname,
527
4
    offline_hsid: bool,
528
4
    selector: KeystoreSelector,
529
4
) -> Result<HsId, StartupError> {
530
4
    if offline_hsid {
531
        unimplemented!("offline hsid mode");
532
4
    }
533
4

            
534
4
    let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
535

            
536
4
    let kp = keymgr
537
4
        .get::<HsIdKey>(&hsid_spec)
538
4
        .map_err(|cause| StartupError::Keystore {
539
            action: "read",
540
            cause,
541
4
        })?;
542

            
543
4
    let mut rng = tor_llcrypto::rng::CautiousRng;
544
4
    let (hsid, generated) = match kp {
545
2
        Some(kp) => (kp.id(), false),
546
        None => {
547
            // Note: there is a race here. If the HsId is generated through some other means
548
            // (e.g. via the CLI) at some point between the time we looked up the keypair and
549
            // now, we will return an error.
550
2
            let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
551
2
            let kp = keymgr
552
2
                .generate::<HsIdKeypair>(&hsid_spec, selector, &mut rng, false /* overwrite */)
553
2
                .map_err(|cause| StartupError::Keystore {
554
                    action: "generate",
555
                    cause,
556
2
                })?;
557

            
558
2
            (HsIdKey::from(&kp).id(), true)
559
        }
560
    };
561

            
562
4
    if generated {
563
2
        info!(
564
            "Generated a new identity for service {nickname}: {}",
565
            sensitive(hsid)
566
        );
567
    } else {
568
        // TODO: We may want to downgrade this to trace once we have a CLI
569
        // for extracting it.
570
2
        info!(
571
            "Using existing identity for service {nickname}: {}",
572
            sensitive(hsid)
573
        );
574
    }
575

            
576
4
    Ok(hsid)
577
4
}
578

            
579
/// Return the onion address of this service.
580
///
581
/// Clients must know the service's onion address in order to discover or
582
/// connect to it.
583
///
584
/// Returns `None` if the HsId of the service could not be found in any of the configured
585
/// keystores.
586
//
587
// TODO: instead of duplicating RunningOnionService::onion_address, maybe we should make this a
588
// method on an ArtiHss type, and make both OnionService and RunningOnionService deref to
589
// ArtiHss.
590
74
fn onion_address(keymgr: &KeyMgr, nickname: &HsNickname) -> Option<HsId> {
591
74
    let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
592
74

            
593
74
    keymgr
594
74
        .get::<HsIdKey>(&hsid_spec)
595
74
        .ok()?
596
76
        .map(|hsid| hsid.id())
597
74
}
598

            
599
/// Return a list of the protocols[supported](tor_protover::doc_supported)
600
/// by this crate, running as a hidden service.
601
182
pub fn supported_hsservice_protocols() -> tor_protover::Protocols {
602
    use tor_protover::named::*;
603
    // WARNING: REMOVING ELEMENTS FROM THIS LIST CAN BE DANGEROUS!
604
    // SEE [`tor_protover::doc_changing`]
605
182
    [
606
182
        //
607
182
        HSINTRO_V3,
608
182
        HSINTRO_RATELIM,
609
182
        HSREND_V3,
610
182
        HSDIR_V3,
611
182
    ]
612
182
    .into_iter()
613
182
    .collect()
614
182
}
615

            
616
#[cfg(test)]
617
pub(crate) mod test {
618
    // @@ begin test lint list maintained by maint/add_warning @@
619
    #![allow(clippy::bool_assert_comparison)]
620
    #![allow(clippy::clone_on_copy)]
621
    #![allow(clippy::dbg_macro)]
622
    #![allow(clippy::mixed_attributes_style)]
623
    #![allow(clippy::print_stderr)]
624
    #![allow(clippy::print_stdout)]
625
    #![allow(clippy::single_char_pattern)]
626
    #![allow(clippy::unwrap_used)]
627
    #![allow(clippy::unchecked_duration_subtraction)]
628
    #![allow(clippy::useless_vec)]
629
    #![allow(clippy::needless_pass_by_value)]
630
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
631
    use super::*;
632

            
633
    use std::fmt::Display;
634
    use std::path::Path;
635

            
636
    use fs_mistrust::Mistrust;
637
    use test_temp_dir::{test_temp_dir, TestTempDir, TestTempDirGuard};
638

            
639
    use tor_basic_utils::test_rng::testing_rng;
640
    use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder};
641
    use tor_llcrypto::pk::ed25519;
642
    use tor_persist::state_dir::InstanceStateHandle;
643

            
644
    use crate::config::OnionServiceConfigBuilder;
645
    use crate::ipt_set::IptSetStorageHandle;
646
    use crate::{HsIdKeypairSpecifier, HsIdPublicKeySpecifier};
647

            
648
    /// The nickname of the test service.
649
    const TEST_SVC_NICKNAME: &str = "test-svc";
650

            
651
    #[test]
652
    fn protocols() {
653
        let pr = supported_hsservice_protocols();
654
        let expected = "HSIntro=4-5 HSRend=2 HSDir=2".parse().unwrap();
655
        assert_eq!(pr, expected);
656
    }
657

            
658
    /// Make a fresh `KeyMgr` (containing no keys) using files in `temp_dir`
659
    pub(crate) fn create_keymgr(temp_dir: &TestTempDir) -> TestTempDirGuard<Arc<KeyMgr>> {
660
        temp_dir.subdir_used_by("keystore", |keystore_dir| {
661
            let keystore = ArtiNativeKeystore::from_path_and_mistrust(
662
                keystore_dir,
663
                &Mistrust::new_dangerously_trust_everyone(),
664
            )
665
            .unwrap();
666

            
667
            Arc::new(
668
                KeyMgrBuilder::default()
669
                    .primary_store(Box::new(keystore))
670
                    .build()
671
                    .unwrap(),
672
            )
673
        })
674
    }
675

            
676
    #[allow(clippy::let_and_return)] // clearer and more regular
677
    pub(crate) fn mk_state_instance(dir: &Path, nick: impl Display) -> InstanceStateHandle {
678
        let nick = HsNickname::new(nick.to_string()).unwrap();
679
        let mistrust = fs_mistrust::Mistrust::new_dangerously_trust_everyone();
680
        let state_dir = StateDirectory::new(dir, &mistrust).unwrap();
681
        let instance = state_dir.acquire_instance(&nick).unwrap();
682
        instance
683
    }
684

            
685
    pub(crate) fn create_storage_handles(
686
        dir: &Path,
687
    ) -> (
688
        tor_persist::state_dir::InstanceStateHandle,
689
        IptSetStorageHandle,
690
    ) {
691
        let nick = HsNickname::try_from("allium".to_owned()).unwrap();
692
        create_storage_handles_from_state_dir(dir, &nick)
693
    }
694

            
695
    pub(crate) fn create_storage_handles_from_state_dir(
696
        state_dir: &Path,
697
        nick: &HsNickname,
698
    ) -> (
699
        tor_persist::state_dir::InstanceStateHandle,
700
        IptSetStorageHandle,
701
    ) {
702
        let instance = mk_state_instance(state_dir, nick);
703
        let iptpub_state_handle = instance.storage_handle("iptpub").unwrap();
704
        (instance, iptpub_state_handle)
705
    }
706

            
707
    macro_rules! maybe_generate_hsid {
708
        ($keymgr:expr, $offline_hsid:expr) => {{
709
            let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
710
            let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
711
            let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
712

            
713
            assert!($keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
714
            assert!($keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
715

            
716
            maybe_generate_hsid(&$keymgr, &nickname, $offline_hsid, Default::default()).unwrap();
717
        }};
718
    }
719

            
720
    /// Create a test hsid keypair.
721
    fn create_hsid() -> (HsIdKeypair, HsIdKey) {
722
        let mut rng = testing_rng();
723
        let keypair = ed25519::Keypair::generate(&mut rng);
724

            
725
        let id_pub = HsIdKey::from(keypair.verifying_key());
726
        let id_keypair = HsIdKeypair::from(ed25519::ExpandedKeypair::from(&keypair));
727

            
728
        (id_keypair, id_pub)
729
    }
730

            
731
    #[test]
732
    fn generate_hsid() {
733
        let temp_dir = test_temp_dir!();
734
        let keymgr = create_keymgr(&temp_dir);
735

            
736
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
737
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
738

            
739
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
740
        maybe_generate_hsid!(keymgr, false /* offline_hsid */);
741
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_some());
742
    }
743

            
744
    #[test]
745
    fn hsid_keypair_already_exists() {
746
        let temp_dir = test_temp_dir!();
747
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
748
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
749
        let keymgr = create_keymgr(&temp_dir);
750

            
751
        // Insert the preexisting hsid keypair.
752
        let (existing_hsid_keypair, existing_hsid_public) = create_hsid();
753
        let existing_keypair: ed25519::ExpandedKeypair = existing_hsid_keypair.into();
754
        let existing_hsid_keypair = HsIdKeypair::from(existing_keypair);
755

            
756
        keymgr
757
            .insert(
758
                existing_hsid_keypair,
759
                &hsid_spec,
760
                KeystoreSelector::Primary,
761
                true,
762
            )
763
            .unwrap();
764

            
765
        maybe_generate_hsid(
766
            &keymgr,
767
            &nickname,
768
            false, /* offline_hsid */
769
            Default::default(),
770
        )
771
        .unwrap();
772

            
773
        let keypair = keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().unwrap();
774
        let pk: HsIdKey = (&keypair).into();
775

            
776
        assert_eq!(pk.as_ref(), existing_hsid_public.as_ref());
777
    }
778

            
779
    #[test]
780
    #[ignore] // TODO (#1194): Revisit when we add support for offline hsid mode
781
    fn generate_hsid_offline_hsid() {
782
        let temp_dir = test_temp_dir!();
783
        let keymgr = create_keymgr(&temp_dir);
784

            
785
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
786
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
787
        let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
788

            
789
        maybe_generate_hsid!(keymgr, true /* offline_hsid */);
790

            
791
        assert!(keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
792
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
793
    }
794

            
795
    #[test]
796
    #[ignore] // TODO (#1194): Revisit when we add support for offline hsid mode
797
    fn generate_hsid_corrupt_keystore() {
798
        let temp_dir = test_temp_dir!();
799
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
800
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
801
        let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
802

            
803
        let keymgr = create_keymgr(&temp_dir);
804

            
805
        let (hsid_keypair, _hsid_public) = create_hsid();
806
        let (_hsid_keypair, hsid_public) = create_hsid();
807

            
808
        keymgr
809
            .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
810
            .unwrap();
811

            
812
        // Insert a mismatched public key
813
        keymgr
814
            .insert(hsid_public, &pub_hsid_spec, KeystoreSelector::Primary, true)
815
            .unwrap();
816

            
817
        assert!(maybe_generate_hsid(
818
            &keymgr,
819
            &nickname,
820
            false, /* offline_hsid */
821
            Default::default()
822
        )
823
        .is_err());
824
    }
825

            
826
    #[test]
827
    fn onion_address() {
828
        let temp_dir = test_temp_dir!();
829
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
830
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
831
        let keymgr = create_keymgr(&temp_dir);
832

            
833
        let (hsid_keypair, hsid_public) = create_hsid();
834

            
835
        // Insert the hsid into the keystore
836
        keymgr
837
            .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
838
            .unwrap();
839

            
840
        let config = OnionServiceConfigBuilder::default()
841
            .nickname(nickname)
842
            .build()
843
            .unwrap();
844

            
845
        let state_dir = StateDirectory::new(
846
            temp_dir.as_path_untracked(),
847
            &fs_mistrust::Mistrust::new_dangerously_trust_everyone(),
848
        )
849
        .unwrap();
850

            
851
        let service = OnionService::builder()
852
            .config(config)
853
            .keymgr(Arc::clone(&*keymgr))
854
            .state_dir(state_dir)
855
            .build()
856
            .unwrap();
857

            
858
        let hsid = HsId::from(hsid_public);
859
        assert_eq!(service.onion_address().unwrap(), hsid);
860

            
861
        drop(temp_dir); // prove that this is still live
862
    }
863
}