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 pow;
67
mod publish;
68
mod rend_handshake;
69
mod replay;
70
mod req;
71
pub mod status;
72
mod timeout_track;
73

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

            
92
use std::pin::Pin;
93

            
94
use internal_prelude::*;
95

            
96
// ---------- public exports ----------
97

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

            
112
pub use helpers::handle_rend_requests;
113

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
199
        Ok(())
200
    }
201
}
202

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
513
        Ok(rend_req_rx)
514
    }
515

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

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

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

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

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

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

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

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

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

            
603
4
    Ok(hsid)
604
4
}
605

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
755
        (id_keypair, id_pub)
756
    }
757

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
830
        let keymgr = create_keymgr(&temp_dir);
831

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

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

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

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

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

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

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

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

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

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

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

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