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
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
45
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
46

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

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

            
56
mod internal_prelude;
57

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

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

            
93
use std::pin::Pin;
94

            
95
use internal_prelude::*;
96

            
97
// ---------- public exports ----------
98

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

            
113
pub use helpers::handle_rend_requests;
114

            
115
//---------- top-level service implementation (types and methods) ----------
116

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

            
120
/// Convenient type alias for an ntor public key
121
// TODO (#1022) maybe this should be
122
// `tor_proto::crypto::handshake::ntor::NtorPublicKey`,
123
// or a unified OnionKey type.
124
pub(crate) type NtorPublicKey = curve25519::PublicKey;
125

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

            
142
/// Implementation details for an onion service.
143
struct SvcInner {
144
    /// Configuration information about this service.
145
    config_tx: postage::watch::Sender<Arc<OnionServiceConfig>>,
146

            
147
    /// A oneshot that will be dropped when this object is dropped.
148
    _shutdown_tx: postage::broadcast::Sender<void::Void>,
149

            
150
    /// Postage sender, used to tell subscribers about changes in the status of
151
    /// this onion service.
152
    status_tx: StatusSender,
153

            
154
    /// Handles that we'll take ownership of when launching the service.
155
    #[allow(clippy::type_complexity)]
156
    unlaunched: Option<(
157
        Pin<Box<dyn Stream<Item = RendRequest> + Send + Sync>>,
158
        Box<dyn Launchable + Send + Sync>,
159
    )>,
160
}
161

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

            
171
    /// Our handler for the introduction point manager.
172
    ///
173
    /// This manager is responsible for selecting introduction points,
174
    /// maintaining our connections to them, and telling the publisher which ones
175
    /// are publicly available.
176
    ipt_mgr: IptManager<R, crate::ipt_mgr::Real<R>>,
177

            
178
    /// A handle used by the ipt manager to send Ipts to the publisher.
179
    ///
180
    ///
181
    ipt_mgr_view: IptsManagerView,
182

            
183
    /// Proof-of-work manager.
184
    pow_manager: Arc<PowManager<R>>,
185
}
186

            
187
/// Private trait used to type-erase `ForLaunch<R>`, so that we don't need to
188
/// parameterize OnionService on `<R>`.
189
trait Launchable: Send + Sync {
190
    /// Launch
191
    fn launch(self: Box<Self>) -> Result<(), StartupError>;
192
}
193

            
194
impl<R: Runtime> Launchable for ForLaunch<R> {
195
    fn launch(self: Box<Self>) -> Result<(), StartupError> {
196
        self.ipt_mgr.launch_background_tasks(self.ipt_mgr_view)?;
197
        self.publisher.launch()?;
198
        self.pow_manager.launch()?;
199

            
200
        Ok(())
201
    }
202
}
203

            
204
/// Return value from one call to the main loop iteration
205
///
206
/// Used by the publisher reactor and by the [`IptManager`].
207
#[derive(PartialEq)]
208
#[must_use]
209
pub(crate) enum ShutdownStatus {
210
    /// We should continue to operate this component
211
    Continue,
212
    /// We should shut down: the service, or maybe the whole process, is shutting down
213
    Terminate,
214
}
215

            
216
impl From<oneshot::Canceled> for ShutdownStatus {
217
    fn from(_: oneshot::Canceled) -> ShutdownStatus {
218
        ShutdownStatus::Terminate
219
    }
220
}
221

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

            
241
impl OnionService {
242
    /// Create an [`OnionServiceBuilder`].
243
74
    pub fn builder() -> OnionServiceBuilder {
244
74
        OnionServiceBuilder::default()
245
74
    }
246

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

            
272
        let nickname = config.nickname.clone();
273

            
274
        // TODO (#1194): add a config option for specifying whether to expect the KS_hsid to be stored
275
        // offline
276
        //let offline_hsid = config.offline_hsid;
277
        let offline_hsid = false;
278

            
279
        // TODO (#1106): make this configurable
280
        let selector = KeystoreSelector::Primary;
281
        maybe_generate_hsid(&keymgr, &config.nickname, offline_hsid, selector)?;
282

            
283
        if config.restricted_discovery.enabled {
284
            info!(
285
                nickname=%nickname,
286
                "Launching onion service in restricted discovery mode"
287
            );
288
        } else {
289
            info!(
290
                nickname=%nickname,
291
                "Launching onion service"
292
            );
293
        }
294

            
295
        let state_handle = state_dir
296
            .acquire_instance(&config.nickname)
297
            .map_err(StartupError::StateDirectoryInaccessible)?;
298

            
299
        // We pass the "cooked" handle, with the storage key embedded, to ipt_set,
300
        // since the ipt_set code doesn't otherwise have access to the HS nickname.
301
        let iptpub_storage_handle = state_handle
302
            .storage_handle("iptpub")
303
            .map_err(StartupError::StateDirectoryInaccessible)?;
304

            
305
        let pow_manager_storage_handle = state_handle
306
            .storage_handle("pow_manager")
307
            .map_err(StartupError::StateDirectoryInaccessible)?;
308
        let pow_nonce_dir = state_handle
309
            .raw_subdir("pow_nonces")
310
            .map_err(StartupError::StateDirectoryInaccessible)?;
311
        let NewPowManager {
312
            pow_manager,
313
            rend_req_tx,
314
            rend_req_rx,
315
            publisher_update_rx,
316
        } = PowManager::new(
317
            runtime.clone(),
318
            nickname.clone(),
319
            pow_nonce_dir,
320
            keymgr.clone(),
321
            pow_manager_storage_handle,
322
        )?;
323

            
324
        let (shutdown_tx, shutdown_rx) = broadcast::channel(0);
325
        let (config_tx, config_rx) = postage::watch::channel_with(Arc::new(config));
326

            
327
        let (ipt_mgr_view, publisher_view) =
328
            crate::ipt_set::ipts_channel(&runtime, iptpub_storage_handle)?;
329

            
330
        let status_tx = StatusSender::new(OnionServiceStatus::new_shutdown());
331

            
332
        let ipt_mgr = IptManager::new(
333
            runtime.clone(),
334
            netdir_provider.clone(),
335
            nickname.clone(),
336
            config_rx.clone(),
337
            rend_req_tx,
338
            shutdown_rx.clone(),
339
            &state_handle,
340
            crate::ipt_mgr::Real {
341
                circ_pool: circ_pool.clone(),
342
            },
343
            keymgr.clone(),
344
            status_tx.clone().into(),
345
        )?;
346

            
347
        let publisher: Publisher<R, publish::Real<R>> = Publisher::new(
348
            runtime,
349
            nickname.clone(),
350
            netdir_provider,
351
            circ_pool,
352
            publisher_view,
353
            config_rx,
354
            status_tx.clone().into(),
355
            Arc::clone(&keymgr),
356
            path_resolver,
357
            pow_manager.clone(),
358
            publisher_update_rx,
359
        );
360

            
361
        let svc = Arc::new(RunningOnionService {
362
            nickname,
363
            keymgr,
364
            inner: Mutex::new(SvcInner {
365
                config_tx,
366
                _shutdown_tx: shutdown_tx,
367
                status_tx,
368
                unlaunched: Some((
369
                    rend_req_rx,
370
                    Box::new(ForLaunch {
371
                        publisher,
372
                        ipt_mgr,
373
                        ipt_mgr_view,
374
                        pow_manager,
375
                    }),
376
                )),
377
            }),
378
        });
379

            
380
        let stream = svc.launch()?;
381
        Ok((svc, stream))
382
    }
383

            
384
    /// Return the onion address of this service.
385
    ///
386
    /// Clients must know the service's onion address in order to discover or
387
    /// connect to it.
388
    ///
389
    /// Returns `None` if the HsId of the service could not be found in any of the configured
390
    /// keystores.
391
74
    pub fn onion_address(&self) -> Option<HsId> {
392
74
        onion_address(&self.keymgr, &self.config.nickname)
393
74
    }
394

            
395
    /// Return the onion address of this service.
396
    ///
397
    /// See [`onion_address`](Self::onion_address)
398
    #[deprecated = "Use the new onion_address method instead"]
399
    pub fn onion_name(&self) -> Option<HsId> {
400
        self.onion_address()
401
    }
402

            
403
    /// Generate an identity key (KP_hs_id) for this service.
404
    ///
405
    /// If the keystore specified by `selector` contains an entry for the identity key
406
    /// of this service, it will be returned. Otherwise, a new key will be generated.
407
    ///
408
    /// Most users do not need to call this function: on [`launch`](`OnionService::launch`),
409
    /// the service will automatically generate its identity key if needed.
410
    /// You should only use this function if you need to know the KP_hs_id of the service
411
    /// before launching it.
412
    ///
413
    /// The `selector` argument is used for choosing the keystore in which to generate the keypair.
414
    /// While most users will want to write to the [`Primary`](KeystoreSelector::Primary), if you
415
    /// have configured this `TorClient` with a non-default keystore and wish to generate the
416
    /// keypair in it, you can do so by calling this function with a [KeystoreSelector::Id]
417
    /// specifying the keystore ID of your keystore.
418
    ///
419
    // Note: the selector argument exists for future-proofing reasons. We don't currently support
420
    // configuring custom or non-default keystores (see #1106).
421
    pub fn generate_identity_key(&self, selector: KeystoreSelector) -> Result<HsId, StartupError> {
422
        // TODO (#1194): add a config option for specifying whether to expect the KS_hsid to be stored
423
        // offline
424
        //let offline_hsid = config.offline_hsid;
425
        let offline_hsid = false;
426

            
427
        maybe_generate_hsid(&self.keymgr, &self.config.nickname, offline_hsid, selector)
428
    }
429
}
430

            
431
impl OnionServiceBuilder {
432
    /// Build the [`OnionService`]
433
74
    pub fn build(&self) -> Result<OnionService, StartupError> {
434
74
        let svc = self.build_unvalidated()?;
435
74
        Ok(svc)
436
74
    }
437
}
438

            
439
impl RunningOnionService {
440
    /// Change the configuration of this onion service.
441
    ///
442
    /// (Not everything can be changed here. At the very least we'll need to say
443
    /// that the identity of a service is fixed. We might want to make the
444
    /// storage  backing this, and the anonymity status, unchangeable.)
445
    pub fn reconfigure(
446
        &self,
447
        new_config: OnionServiceConfig,
448
        how: Reconfigure,
449
    ) -> Result<(), ReconfigureError> {
450
        let mut inner = self.inner.lock().expect("lock poisoned");
451
        inner.config_tx.try_maybe_send(|cur_config| {
452
            let new_config = cur_config.for_transition_to(new_config, how)?;
453
            Ok(match how {
454
                // We're only checking, so return the current configuration.
455
                tor_config::Reconfigure::CheckAllOrNothing => Arc::clone(cur_config),
456
                // We're replacing the configuration, and we didn't get an error.
457
                _ => Arc::new(new_config),
458
            })
459
        })
460

            
461
        // TODO (#1153, #1209): We need to make sure that the various tasks listening on
462
        // config_rx actually enforce the configuration, not only on new
463
        // connections, but existing ones.
464
    }
465

            
466
    /*
467
    /// Tell this onion service about some new short-term keys it can use.
468
    pub fn add_keys(&self, keys: ()) -> Result<(), Bug> {
469
        todo!() // TODO #1194
470
    }
471
    */
472

            
473
    /// Return the current status of this onion service.
474
    pub fn status(&self) -> OnionServiceStatus {
475
        self.inner.lock().expect("poisoned lock").status_tx.get()
476
    }
477

            
478
    /// Return a stream of events that will receive notifications of changes in
479
    /// this onion service's status.
480
    pub fn status_events(&self) -> OnionServiceStatusStream {
481
        self.inner
482
            .lock()
483
            .expect("poisoned lock")
484
            .status_tx
485
            .subscribe()
486
    }
487

            
488
    /// Tell this onion service to begin running, and return a
489
    /// stream of rendezvous requests on the service.
490
    ///
491
    /// You can turn the resulting stream into a stream of [`StreamRequest`]
492
    /// using the [`handle_rend_requests`] helper function.
493
    fn launch(self: &Arc<Self>) -> Result<impl Stream<Item = RendRequest>, StartupError> {
494
        let (rend_req_rx, launch) = {
495
            let mut inner = self.inner.lock().expect("poisoned lock");
496
            inner
497
                .unlaunched
498
                .take()
499
                .ok_or(StartupError::AlreadyLaunched)?
500
        };
501

            
502
        match launch.launch() {
503
            Ok(()) => {}
504
            Err(e) => {
505
                return Err(e);
506
            }
507
        }
508

            
509
        // This needs to launch at least the following tasks:
510
        //
511
        // TODO (#1194) If we decide to use separate disk-based key
512
        // provisioning, we need a task to monitor our keys directory.
513

            
514
        Ok(rend_req_rx)
515
    }
516

            
517
    /*
518
    /// Tell this onion service to stop running.
519
    ///
520
    /// It can be restarted with launch().
521
    ///
522
    /// You can also shut down an onion service completely by dropping the last
523
    /// Clone of it.
524
    pub fn pause(&self) {
525
        todo!() // TODO (#1231)
526
    }
527
    */
528

            
529
    /// Return the onion address of this service.
530
    ///
531
    /// Clients must know the service's onion address in order to discover or
532
    /// connect to it.
533
    ///
534
    /// Returns `None` if the HsId of the service could not be found in any of the configured
535
    /// keystores.
536
    pub fn onion_address(&self) -> Option<HsId> {
537
        onion_address(&self.keymgr, &self.nickname)
538
    }
539

            
540
    /// Return the onion address of this service.
541
    ///
542
    /// See [`onion_address`](Self::onion_address)
543
    #[deprecated = "Use the new onion_address method instead"]
544
    pub fn onion_name(&self) -> Option<HsId> {
545
        self.onion_address()
546
    }
547
}
548

            
549
/// Generate the identity key of the service, unless it already exists or `offline_hsid` is `true`.
550
//
551
// TODO (#1194): we don't support offline_hsid yet.
552
4
fn maybe_generate_hsid(
553
4
    keymgr: &Arc<KeyMgr>,
554
4
    nickname: &HsNickname,
555
4
    offline_hsid: bool,
556
4
    selector: KeystoreSelector,
557
4
) -> Result<HsId, StartupError> {
558
4
    if offline_hsid {
559
        unimplemented!("offline hsid mode");
560
4
    }
561
4

            
562
4
    let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
563

            
564
4
    let kp = keymgr
565
4
        .get::<HsIdKey>(&hsid_spec)
566
4
        .map_err(|cause| StartupError::Keystore {
567
            action: "read",
568
            cause,
569
4
        })?;
570

            
571
4
    let mut rng = tor_llcrypto::rng::CautiousRng;
572
4
    let (hsid, generated) = match kp {
573
2
        Some(kp) => (kp.id(), false),
574
        None => {
575
            // Note: there is a race here. If the HsId is generated through some other means
576
            // (e.g. via the CLI) at some point between the time we looked up the keypair and
577
            // now, we will return an error.
578
2
            let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
579
2
            let kp = keymgr
580
2
                .generate::<HsIdKeypair>(&hsid_spec, selector, &mut rng, false /* overwrite */)
581
2
                .map_err(|cause| StartupError::Keystore {
582
                    action: "generate",
583
                    cause,
584
2
                })?;
585

            
586
2
            (HsIdKey::from(&kp).id(), true)
587
        }
588
    };
589

            
590
4
    if generated {
591
2
        info!(
592
            "Generated a new identity for service {nickname}: {}",
593
            sensitive(hsid)
594
        );
595
    } else {
596
        // TODO: We may want to downgrade this to trace once we have a CLI
597
        // for extracting it.
598
2
        info!(
599
            "Using existing identity for service {nickname}: {}",
600
            sensitive(hsid)
601
        );
602
    }
603

            
604
4
    Ok(hsid)
605
4
}
606

            
607
/// Return the onion address of this service.
608
///
609
/// Clients must know the service's onion address in order to discover or
610
/// connect to it.
611
///
612
/// Returns `None` if the HsId of the service could not be found in any of the configured
613
/// keystores.
614
//
615
// TODO: instead of duplicating RunningOnionService::onion_address, maybe we should make this a
616
// method on an ArtiHss type, and make both OnionService and RunningOnionService deref to
617
// ArtiHss.
618
74
fn onion_address(keymgr: &KeyMgr, nickname: &HsNickname) -> Option<HsId> {
619
74
    let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
620
74

            
621
74
    keymgr
622
74
        .get::<HsIdKey>(&hsid_spec)
623
74
        .ok()?
624
76
        .map(|hsid| hsid.id())
625
74
}
626

            
627
/// Return a list of the protocols[supported](tor_protover::doc_supported)
628
/// by this crate, running as a hidden service.
629
182
pub fn supported_hsservice_protocols() -> tor_protover::Protocols {
630
    use tor_protover::named::*;
631
    // WARNING: REMOVING ELEMENTS FROM THIS LIST CAN BE DANGEROUS!
632
    // SEE [`tor_protover::doc_changing`]
633
182
    [
634
182
        //
635
182
        HSINTRO_V3,
636
182
        HSINTRO_RATELIM,
637
182
        HSREND_V3,
638
182
        HSDIR_V3,
639
182
    ]
640
182
    .into_iter()
641
182
    .collect()
642
182
}
643

            
644
#[cfg(test)]
645
pub(crate) mod test {
646
    // @@ begin test lint list maintained by maint/add_warning @@
647
    #![allow(clippy::bool_assert_comparison)]
648
    #![allow(clippy::clone_on_copy)]
649
    #![allow(clippy::dbg_macro)]
650
    #![allow(clippy::mixed_attributes_style)]
651
    #![allow(clippy::print_stderr)]
652
    #![allow(clippy::print_stdout)]
653
    #![allow(clippy::single_char_pattern)]
654
    #![allow(clippy::unwrap_used)]
655
    #![allow(clippy::unchecked_duration_subtraction)]
656
    #![allow(clippy::useless_vec)]
657
    #![allow(clippy::needless_pass_by_value)]
658
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
659
    use super::*;
660

            
661
    use std::fmt::Display;
662
    use std::path::Path;
663

            
664
    use fs_mistrust::Mistrust;
665
    use test_temp_dir::{test_temp_dir, TestTempDir, TestTempDirGuard};
666

            
667
    use tor_basic_utils::test_rng::testing_rng;
668
    use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder};
669
    use tor_llcrypto::pk::ed25519;
670
    use tor_persist::state_dir::InstanceStateHandle;
671

            
672
    use crate::config::OnionServiceConfigBuilder;
673
    use crate::ipt_set::IptSetStorageHandle;
674
    use crate::{HsIdKeypairSpecifier, HsIdPublicKeySpecifier};
675

            
676
    /// The nickname of the test service.
677
    const TEST_SVC_NICKNAME: &str = "test-svc";
678

            
679
    #[test]
680
    fn protocols() {
681
        let pr = supported_hsservice_protocols();
682
        let expected = "HSIntro=4-5 HSRend=2 HSDir=2".parse().unwrap();
683
        assert_eq!(pr, expected);
684
    }
685

            
686
    /// Make a fresh `KeyMgr` (containing no keys) using files in `temp_dir`
687
    pub(crate) fn create_keymgr(temp_dir: &TestTempDir) -> TestTempDirGuard<Arc<KeyMgr>> {
688
        temp_dir.subdir_used_by("keystore", |keystore_dir| {
689
            let keystore = ArtiNativeKeystore::from_path_and_mistrust(
690
                keystore_dir,
691
                &Mistrust::new_dangerously_trust_everyone(),
692
            )
693
            .unwrap();
694

            
695
            Arc::new(
696
                KeyMgrBuilder::default()
697
                    .primary_store(Box::new(keystore))
698
                    .build()
699
                    .unwrap(),
700
            )
701
        })
702
    }
703

            
704
    #[allow(clippy::let_and_return)] // clearer and more regular
705
    pub(crate) fn mk_state_instance(dir: &Path, nick: impl Display) -> InstanceStateHandle {
706
        let nick = HsNickname::new(nick.to_string()).unwrap();
707
        let mistrust = fs_mistrust::Mistrust::new_dangerously_trust_everyone();
708
        let state_dir = StateDirectory::new(dir, &mistrust).unwrap();
709
        let instance = state_dir.acquire_instance(&nick).unwrap();
710
        instance
711
    }
712

            
713
    pub(crate) fn create_storage_handles(
714
        dir: &Path,
715
    ) -> (
716
        tor_persist::state_dir::InstanceStateHandle,
717
        IptSetStorageHandle,
718
    ) {
719
        let nick = HsNickname::try_from("allium".to_owned()).unwrap();
720
        create_storage_handles_from_state_dir(dir, &nick)
721
    }
722

            
723
    pub(crate) fn create_storage_handles_from_state_dir(
724
        state_dir: &Path,
725
        nick: &HsNickname,
726
    ) -> (
727
        tor_persist::state_dir::InstanceStateHandle,
728
        IptSetStorageHandle,
729
    ) {
730
        let instance = mk_state_instance(state_dir, nick);
731
        let iptpub_state_handle = instance.storage_handle("iptpub").unwrap();
732
        (instance, iptpub_state_handle)
733
    }
734

            
735
    macro_rules! maybe_generate_hsid {
736
        ($keymgr:expr, $offline_hsid:expr) => {{
737
            let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
738
            let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
739
            let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
740

            
741
            assert!($keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
742
            assert!($keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
743

            
744
            maybe_generate_hsid(&$keymgr, &nickname, $offline_hsid, Default::default()).unwrap();
745
        }};
746
    }
747

            
748
    /// Create a test hsid keypair.
749
    fn create_hsid() -> (HsIdKeypair, HsIdKey) {
750
        let mut rng = testing_rng();
751
        let keypair = ed25519::Keypair::generate(&mut rng);
752

            
753
        let id_pub = HsIdKey::from(keypair.verifying_key());
754
        let id_keypair = HsIdKeypair::from(ed25519::ExpandedKeypair::from(&keypair));
755

            
756
        (id_keypair, id_pub)
757
    }
758

            
759
    #[test]
760
    fn generate_hsid() {
761
        let temp_dir = test_temp_dir!();
762
        let keymgr = create_keymgr(&temp_dir);
763

            
764
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
765
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
766

            
767
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
768
        maybe_generate_hsid!(keymgr, false /* offline_hsid */);
769
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_some());
770
    }
771

            
772
    #[test]
773
    fn hsid_keypair_already_exists() {
774
        let temp_dir = test_temp_dir!();
775
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
776
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
777
        let keymgr = create_keymgr(&temp_dir);
778

            
779
        // Insert the preexisting hsid keypair.
780
        let (existing_hsid_keypair, existing_hsid_public) = create_hsid();
781
        let existing_keypair: ed25519::ExpandedKeypair = existing_hsid_keypair.into();
782
        let existing_hsid_keypair = HsIdKeypair::from(existing_keypair);
783

            
784
        keymgr
785
            .insert(
786
                existing_hsid_keypair,
787
                &hsid_spec,
788
                KeystoreSelector::Primary,
789
                true,
790
            )
791
            .unwrap();
792

            
793
        maybe_generate_hsid(
794
            &keymgr,
795
            &nickname,
796
            false, /* offline_hsid */
797
            Default::default(),
798
        )
799
        .unwrap();
800

            
801
        let keypair = keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().unwrap();
802
        let pk: HsIdKey = (&keypair).into();
803

            
804
        assert_eq!(pk.as_ref(), existing_hsid_public.as_ref());
805
    }
806

            
807
    #[test]
808
    #[ignore] // TODO (#1194): Revisit when we add support for offline hsid mode
809
    fn generate_hsid_offline_hsid() {
810
        let temp_dir = test_temp_dir!();
811
        let keymgr = create_keymgr(&temp_dir);
812

            
813
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
814
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
815
        let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
816

            
817
        maybe_generate_hsid!(keymgr, true /* offline_hsid */);
818

            
819
        assert!(keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
820
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
821
    }
822

            
823
    #[test]
824
    #[ignore] // TODO (#1194): Revisit when we add support for offline hsid mode
825
    fn generate_hsid_corrupt_keystore() {
826
        let temp_dir = test_temp_dir!();
827
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
828
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
829
        let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
830

            
831
        let keymgr = create_keymgr(&temp_dir);
832

            
833
        let (hsid_keypair, _hsid_public) = create_hsid();
834
        let (_hsid_keypair, hsid_public) = create_hsid();
835

            
836
        keymgr
837
            .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
838
            .unwrap();
839

            
840
        // Insert a mismatched public key
841
        keymgr
842
            .insert(hsid_public, &pub_hsid_spec, KeystoreSelector::Primary, true)
843
            .unwrap();
844

            
845
        assert!(maybe_generate_hsid(
846
            &keymgr,
847
            &nickname,
848
            false, /* offline_hsid */
849
            Default::default()
850
        )
851
        .is_err());
852
    }
853

            
854
    #[test]
855
    fn onion_address() {
856
        let temp_dir = test_temp_dir!();
857
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
858
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
859
        let keymgr = create_keymgr(&temp_dir);
860

            
861
        let (hsid_keypair, hsid_public) = create_hsid();
862

            
863
        // Insert the hsid into the keystore
864
        keymgr
865
            .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
866
            .unwrap();
867

            
868
        let config = OnionServiceConfigBuilder::default()
869
            .nickname(nickname)
870
            .build()
871
            .unwrap();
872

            
873
        let state_dir = StateDirectory::new(
874
            temp_dir.as_path_untracked(),
875
            &fs_mistrust::Mistrust::new_dangerously_trust_everyone(),
876
        )
877
        .unwrap();
878

            
879
        let service = OnionService::builder()
880
            .config(config)
881
            .keymgr(Arc::clone(&*keymgr))
882
            .state_dir(state_dir)
883
            .build()
884
            .unwrap();
885

            
886
        let hsid = HsId::from(hsid_public);
887
        assert_eq!(service.onion_address().unwrap(), hsid);
888

            
889
        drop(temp_dir); // prove that this is still live
890
    }
891
}