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
use keys::HsTimePeriodKeySpecifier;
104
pub use keys::{
105
    BlindIdKeypairSpecifier, BlindIdPublicKeySpecifier, DescSigningKeypairSpecifier,
106
    HsIdKeypairSpecifier, HsIdPublicKeySpecifier,
107
};
108
use pow::{NewPowManager, PowManager};
109
pub use publish::UploadError as DescUploadError;
110
pub use req::{RendRequest, StreamRequest};
111
pub use tor_hscrypto::pk::HsId;
112
use tor_keymgr::KeystoreEntry;
113
pub use tor_persist::hsnickname::{HsNickname, InvalidNickname};
114

            
115
pub use helpers::handle_rend_requests;
116

            
117
#[cfg(feature = "onion-service-cli-extra")]
118
use tor_netdir::NetDir;
119

            
120
//---------- top-level service implementation (types and methods) ----------
121

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

            
125
/// Convenient type alias for an ntor public key
126
// TODO (#1022) maybe this should be
127
// `tor_proto::crypto::handshake::ntor::NtorPublicKey`,
128
// or a unified OnionKey type.
129
pub(crate) type NtorPublicKey = curve25519::PublicKey;
130

            
131
/// A handle to a running instance of an onion service.
132
//
133
/// To construct a `RunningOnionService`, use [`OnionServiceBuilder`]
134
/// to build an [`OnionService`], and then call its
135
/// [``.launch()``](OnionService::launch) method.
136
//
137
// (APIs should return Arc<OnionService>)
138
#[must_use = "a hidden service object will terminate the service when dropped"]
139
pub struct RunningOnionService {
140
    /// The mutable implementation details of this onion service.
141
    inner: Mutex<SvcInner>,
142
    /// The nickname of this service.
143
    nickname: HsNickname,
144
    /// The key manager, used for accessing the underlying key stores.
145
    keymgr: Arc<KeyMgr>,
146
}
147

            
148
/// Implementation details for an onion service.
149
struct SvcInner {
150
    /// Configuration information about this service.
151
    config_tx: postage::watch::Sender<Arc<OnionServiceConfig>>,
152

            
153
    /// A oneshot that will be dropped when this object is dropped.
154
    _shutdown_tx: postage::broadcast::Sender<void::Void>,
155

            
156
    /// Postage sender, used to tell subscribers about changes in the status of
157
    /// this onion service.
158
    status_tx: StatusSender,
159

            
160
    /// Handles that we'll take ownership of when launching the service.
161
    #[allow(clippy::type_complexity)]
162
    unlaunched: Option<(
163
        Pin<Box<dyn Stream<Item = RendRequest> + Send + Sync>>,
164
        Box<dyn Launchable + Send + Sync>,
165
    )>,
166
}
167

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

            
177
    /// Our handler for the introduction point manager.
178
    ///
179
    /// This manager is responsible for selecting introduction points,
180
    /// maintaining our connections to them, and telling the publisher which ones
181
    /// are publicly available.
182
    ipt_mgr: IptManager<R, crate::ipt_mgr::Real<R>>,
183

            
184
    /// A handle used by the ipt manager to send Ipts to the publisher.
185
    ///
186
    ///
187
    ipt_mgr_view: IptsManagerView,
188

            
189
    /// Proof-of-work manager.
190
    pow_manager: Arc<PowManager<R>>,
191
}
192

            
193
/// Private trait used to type-erase `ForLaunch<R>`, so that we don't need to
194
/// parameterize OnionService on `<R>`.
195
trait Launchable: Send + Sync {
196
    /// Launch
197
    fn launch(self: Box<Self>) -> Result<(), StartupError>;
198
}
199

            
200
impl<R: Runtime> Launchable for ForLaunch<R> {
201
    fn launch(self: Box<Self>) -> Result<(), StartupError> {
202
        self.ipt_mgr.launch_background_tasks(self.ipt_mgr_view)?;
203
        self.publisher.launch()?;
204
        self.pow_manager.launch()?;
205

            
206
        Ok(())
207
    }
208
}
209

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

            
222
impl From<oneshot::Canceled> for ShutdownStatus {
223
    fn from(_: oneshot::Canceled) -> ShutdownStatus {
224
        ShutdownStatus::Terminate
225
    }
226
}
227

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

            
247
impl OnionService {
248
    /// Create an [`OnionServiceBuilder`].
249
290
    pub fn builder() -> OnionServiceBuilder {
250
290
        OnionServiceBuilder::default()
251
290
    }
252

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

            
278
        let nickname = config.nickname.clone();
279

            
280
        // TODO (#1194): add a config option for specifying whether to expect the KS_hsid to be stored
281
        // offline
282
        //let offline_hsid = config.offline_hsid;
283
        let offline_hsid = false;
284

            
285
        // TODO (#1106): make this configurable
286
        let selector = KeystoreSelector::Primary;
287
        maybe_generate_hsid(&keymgr, &config.nickname, offline_hsid, selector)?;
288

            
289
        if config.restricted_discovery.enabled {
290
            info!(
291
                nickname=%nickname,
292
                "Launching onion service in restricted discovery mode"
293
            );
294
        } else {
295
            info!(
296
                nickname=%nickname,
297
                "Launching onion service"
298
            );
299
        }
300

            
301
        let state_handle = state_dir
302
            .acquire_instance(&config.nickname)
303
            .map_err(StartupError::StateDirectoryInaccessible)?;
304

            
305
        // We pass the "cooked" handle, with the storage key embedded, to ipt_set,
306
        // since the ipt_set code doesn't otherwise have access to the HS nickname.
307
        let iptpub_storage_handle = state_handle
308
            .storage_handle("iptpub")
309
            .map_err(StartupError::StateDirectoryInaccessible)?;
310

            
311
        let status_tx = StatusSender::new(OnionServiceStatus::new_shutdown());
312
        let (config_tx, config_rx) = postage::watch::channel_with(Arc::new(config));
313

            
314
        let pow_manager_storage_handle = state_handle
315
            .storage_handle("pow_manager")
316
            .map_err(StartupError::StateDirectoryInaccessible)?;
317
        let pow_nonce_dir = state_handle
318
            .raw_subdir("pow_nonces")
319
            .map_err(StartupError::StateDirectoryInaccessible)?;
320
        let NewPowManager {
321
            pow_manager,
322
            rend_req_tx,
323
            rend_req_rx,
324
            publisher_update_rx,
325
        } = PowManager::new(
326
            runtime.clone(),
327
            nickname.clone(),
328
            pow_nonce_dir,
329
            keymgr.clone(),
330
            pow_manager_storage_handle,
331
            netdir_provider.clone(),
332
            status_tx.clone().into(),
333
            config_rx.clone(),
334
        )?;
335

            
336
        let (shutdown_tx, shutdown_rx) = broadcast::channel(0);
337

            
338
        let (ipt_mgr_view, publisher_view) =
339
            crate::ipt_set::ipts_channel(&runtime, iptpub_storage_handle)?;
340

            
341
        let ipt_mgr = IptManager::new(
342
            runtime.clone(),
343
            netdir_provider.clone(),
344
            nickname.clone(),
345
            config_rx.clone(),
346
            rend_req_tx,
347
            shutdown_rx.clone(),
348
            &state_handle,
349
            crate::ipt_mgr::Real {
350
                circ_pool: circ_pool.clone(),
351
            },
352
            keymgr.clone(),
353
            status_tx.clone().into(),
354
        )?;
355

            
356
        let publisher: Publisher<R, publish::Real<R>> = Publisher::new(
357
            runtime,
358
            nickname.clone(),
359
            netdir_provider,
360
            circ_pool,
361
            publisher_view,
362
            config_rx,
363
            status_tx.clone().into(),
364
            Arc::clone(&keymgr),
365
            path_resolver,
366
            pow_manager.clone(),
367
            publisher_update_rx,
368
        );
369

            
370
        let svc = Arc::new(RunningOnionService {
371
            nickname,
372
            keymgr,
373
            inner: Mutex::new(SvcInner {
374
                config_tx,
375
                _shutdown_tx: shutdown_tx,
376
                status_tx,
377
                unlaunched: Some((
378
                    rend_req_rx,
379
                    Box::new(ForLaunch {
380
                        publisher,
381
                        ipt_mgr,
382
                        ipt_mgr_view,
383
                        pow_manager,
384
                    }),
385
                )),
386
            }),
387
        });
388

            
389
        let stream = svc.launch()?;
390
        Ok((svc, stream))
391
    }
392

            
393
    /// Return the onion address of this service.
394
    ///
395
    /// Clients must know the service's onion address in order to discover or
396
    /// connect to it.
397
    ///
398
    /// Returns `None` if the HsId of the service could not be found in any of the configured
399
    /// keystores.
400
290
    pub fn onion_address(&self) -> Option<HsId> {
401
290
        onion_address(&self.keymgr, &self.config.nickname)
402
290
    }
403

            
404
    /// Return the onion address of this service.
405
    ///
406
    /// See [`onion_address`](Self::onion_address)
407
    #[deprecated = "Use the new onion_address method instead"]
408
    pub fn onion_name(&self) -> Option<HsId> {
409
        self.onion_address()
410
    }
411

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

            
436
        maybe_generate_hsid(&self.keymgr, &self.config.nickname, offline_hsid, selector)
437
    }
438

            
439
    /// List the no-longer-relevant keys of this service.
440
    ///
441
    /// Returns the [`KeystoreEntry`]s associated with time periods that are not
442
    /// "relevant" according to the specified [`NetDir`],
443
    /// (i.e. the keys associated with time periods
444
    /// the service is not publishing descriptors for).
445
    // TODO: unittest
446
    #[cfg(feature = "onion-service-cli-extra")]
447
    pub fn list_expired_keys(&self, netdir: &NetDir) -> tor_keymgr::Result<Vec<KeystoreEntry>> {
448
        list_expired_keys_for_service(
449
            &netdir.hs_all_time_periods(),
450
            self.config.nickname(),
451
            &self.keymgr,
452
        )
453
    }
454
}
455

            
456
impl OnionServiceBuilder {
457
    /// Build the [`OnionService`]
458
290
    pub fn build(&self) -> Result<OnionService, StartupError> {
459
290
        let svc = self.build_unvalidated()?;
460
290
        Ok(svc)
461
290
    }
462
}
463

            
464
impl RunningOnionService {
465
    /// Change the configuration of this onion service.
466
    ///
467
    /// (Not everything can be changed here. At the very least we'll need to say
468
    /// that the identity of a service is fixed. We might want to make the
469
    /// storage  backing this, and the anonymity status, unchangeable.)
470
    pub fn reconfigure(
471
        &self,
472
        new_config: OnionServiceConfig,
473
        how: Reconfigure,
474
    ) -> Result<(), ReconfigureError> {
475
        let mut inner = self.inner.lock().expect("lock poisoned");
476
        inner.config_tx.try_maybe_send(|cur_config| {
477
            let new_config = cur_config.for_transition_to(new_config, how)?;
478
            Ok(match how {
479
                // We're only checking, so return the current configuration.
480
                tor_config::Reconfigure::CheckAllOrNothing => Arc::clone(cur_config),
481
                // We're replacing the configuration, and we didn't get an error.
482
                _ => Arc::new(new_config),
483
            })
484
        })
485

            
486
        // TODO (#1153, #1209): We need to make sure that the various tasks listening on
487
        // config_rx actually enforce the configuration, not only on new
488
        // connections, but existing ones.
489
    }
490

            
491
    /*
492
    /// Tell this onion service about some new short-term keys it can use.
493
    pub fn add_keys(&self, keys: ()) -> Result<(), Bug> {
494
        todo!() // TODO #1194
495
    }
496
    */
497

            
498
    /// Return the current status of this onion service.
499
    pub fn status(&self) -> OnionServiceStatus {
500
        self.inner.lock().expect("poisoned lock").status_tx.get()
501
    }
502

            
503
    /// Return a stream of events that will receive notifications of changes in
504
    /// this onion service's status.
505
    pub fn status_events(&self) -> OnionServiceStatusStream {
506
        self.inner
507
            .lock()
508
            .expect("poisoned lock")
509
            .status_tx
510
            .subscribe()
511
    }
512

            
513
    /// Tell this onion service to begin running, and return a
514
    /// stream of rendezvous requests on the service.
515
    ///
516
    /// You can turn the resulting stream into a stream of [`StreamRequest`]
517
    /// using the [`handle_rend_requests`] helper function.
518
    fn launch(self: &Arc<Self>) -> Result<impl Stream<Item = RendRequest> + use<>, StartupError> {
519
        let (rend_req_rx, launch) = {
520
            let mut inner = self.inner.lock().expect("poisoned lock");
521
            inner
522
                .unlaunched
523
                .take()
524
                .ok_or(StartupError::AlreadyLaunched)?
525
        };
526

            
527
        match launch.launch() {
528
            Ok(()) => {}
529
            Err(e) => {
530
                return Err(e);
531
            }
532
        }
533

            
534
        // This needs to launch at least the following tasks:
535
        //
536
        // TODO (#1194) If we decide to use separate disk-based key
537
        // provisioning, we need a task to monitor our keys directory.
538

            
539
        Ok(rend_req_rx)
540
    }
541

            
542
    /*
543
    /// Tell this onion service to stop running.
544
    ///
545
    /// It can be restarted with launch().
546
    ///
547
    /// You can also shut down an onion service completely by dropping the last
548
    /// Clone of it.
549
    pub fn pause(&self) {
550
        todo!() // TODO (#1231)
551
    }
552
    */
553

            
554
    /// Return the onion address of this service.
555
    ///
556
    /// Clients must know the service's onion address in order to discover or
557
    /// connect to it.
558
    ///
559
    /// Returns `None` if the HsId of the service could not be found in any of the configured
560
    /// keystores.
561
    pub fn onion_address(&self) -> Option<HsId> {
562
        onion_address(&self.keymgr, &self.nickname)
563
    }
564

            
565
    /// Return the onion address of this service.
566
    ///
567
    /// See [`onion_address`](Self::onion_address)
568
    #[deprecated = "Use the new onion_address method instead"]
569
    pub fn onion_name(&self) -> Option<HsId> {
570
        self.onion_address()
571
    }
572
}
573

            
574
/// Generate the identity key of the service, unless it already exists or `offline_hsid` is `true`.
575
//
576
// TODO (#1194): we don't support offline_hsid yet.
577
4
fn maybe_generate_hsid(
578
4
    keymgr: &Arc<KeyMgr>,
579
4
    nickname: &HsNickname,
580
4
    offline_hsid: bool,
581
4
    selector: KeystoreSelector,
582
4
) -> Result<HsId, StartupError> {
583
4
    if offline_hsid {
584
        unimplemented!("offline hsid mode");
585
4
    }
586

            
587
4
    let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
588

            
589
4
    let kp = keymgr
590
4
        .get::<HsIdKey>(&hsid_spec)
591
4
        .map_err(|cause| StartupError::Keystore {
592
            action: "read",
593
            cause,
594
        })?;
595

            
596
4
    let mut rng = tor_llcrypto::rng::CautiousRng;
597
4
    let (hsid, generated) = match kp {
598
2
        Some(kp) => (kp.id(), false),
599
        None => {
600
            // Note: there is a race here. If the HsId is generated through some other means
601
            // (e.g. via the CLI) at some point between the time we looked up the keypair and
602
            // now, we will return an error.
603
2
            let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
604
2
            let kp = keymgr
605
2
                .generate::<HsIdKeypair>(&hsid_spec, selector, &mut rng, false /* overwrite */)
606
2
                .map_err(|cause| StartupError::Keystore {
607
                    action: "generate",
608
                    cause,
609
                })?;
610

            
611
2
            (HsIdKey::from(&kp).id(), true)
612
        }
613
    };
614

            
615
4
    if generated {
616
2
        info!(
617
            "Generated a new identity for service {nickname}: {}",
618
            hsid.display_redacted()
619
        );
620
    } else {
621
        // TODO: We may want to downgrade this to trace once we have a CLI
622
        // for extracting it.
623
2
        info!(
624
            "Using existing identity for service {nickname}: {}",
625
            hsid.display_redacted()
626
        );
627
    }
628

            
629
4
    Ok(hsid)
630
4
}
631

            
632
/// Return the onion address of this service.
633
///
634
/// Clients must know the service's onion address in order to discover or
635
/// connect to it.
636
///
637
/// Returns `None` if the HsId of the service could not be found in any of the configured
638
/// keystores.
639
//
640
// TODO: instead of duplicating RunningOnionService::onion_address, maybe we should make this a
641
// method on an ArtiHss type, and make both OnionService and RunningOnionService deref to
642
// ArtiHss.
643
290
fn onion_address(keymgr: &KeyMgr, nickname: &HsNickname) -> Option<HsId> {
644
290
    let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
645

            
646
290
    keymgr
647
290
        .get::<HsIdKey>(&hsid_spec)
648
290
        .ok()?
649
298
        .map(|hsid| hsid.id())
650
290
}
651

            
652
/// Return a list of the protocols[supported](tor_protover::doc_supported)
653
/// by this crate, running as a hidden service.
654
290
pub fn supported_hsservice_protocols() -> tor_protover::Protocols {
655
    use tor_protover::named::*;
656
    // WARNING: REMOVING ELEMENTS FROM THIS LIST CAN BE DANGEROUS!
657
    // SEE [`tor_protover::doc_changing`]
658
290
    [
659
290
        //
660
290
        HSINTRO_V3,
661
290
        HSINTRO_RATELIM,
662
290
        HSREND_V3,
663
290
        HSDIR_V3,
664
290
    ]
665
290
    .into_iter()
666
290
    .collect()
667
290
}
668

            
669
/// Returns all the keys (as [`KeystoreEntry`]) of the service
670
/// identified by `nickname` that are expired according to the
671
/// provided [`HsDirParams`].
672
fn list_expired_keys_for_service<'a>(
673
    relevant_periods: &[HsDirParams],
674
    nickname: &HsNickname,
675
    keymgr: &'a KeyMgr,
676
) -> tor_keymgr::Result<Vec<KeystoreEntry<'a>>> {
677
    let arti_pat = tor_keymgr::KeyPathPattern::Arti(format!("hss/{}/*", nickname));
678
    let possibly_relevant_keys = keymgr.list_matching(&arti_pat)?;
679
    let mut expired_keys = Vec::new();
680

            
681
    for entry in possibly_relevant_keys {
682
        let key_path = entry.key_path();
683
        let mut append_if_expired = |spec: &dyn HsTimePeriodKeySpecifier| {
684
            if spec.nickname() != nickname {
685
                return Err(internal!(
686
                    "keymgr gave us key {spec:?} that doesn't match our pattern {arti_pat:?}"
687
                )
688
                .into());
689
            }
690
            let is_expired = relevant_periods
691
                .iter()
692
                .all(|p| &p.time_period() != spec.period());
693

            
694
            if is_expired {
695
                expired_keys.push(entry.clone());
696
            }
697

            
698
            tor_keymgr::Result::Ok(())
699
        };
700

            
701
        macro_rules! append_if_expired {
702
            ($K:ty) => {{
703
                if let Ok(spec) = <$K>::try_from(key_path) {
704
                    append_if_expired(&spec)?;
705
                }
706
            }};
707
        }
708

            
709
        append_if_expired!(BlindIdPublicKeySpecifier);
710
        append_if_expired!(BlindIdKeypairSpecifier);
711
        append_if_expired!(DescSigningKeypairSpecifier);
712
    }
713

            
714
    Ok(expired_keys)
715
}
716

            
717
#[cfg(test)]
718
pub(crate) mod test {
719
    // @@ begin test lint list maintained by maint/add_warning @@
720
    #![allow(clippy::bool_assert_comparison)]
721
    #![allow(clippy::clone_on_copy)]
722
    #![allow(clippy::dbg_macro)]
723
    #![allow(clippy::mixed_attributes_style)]
724
    #![allow(clippy::print_stderr)]
725
    #![allow(clippy::print_stdout)]
726
    #![allow(clippy::single_char_pattern)]
727
    #![allow(clippy::unwrap_used)]
728
    #![allow(clippy::unchecked_duration_subtraction)]
729
    #![allow(clippy::useless_vec)]
730
    #![allow(clippy::needless_pass_by_value)]
731
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
732
    use super::*;
733

            
734
    use std::fmt::Display;
735
    use std::path::Path;
736

            
737
    use fs_mistrust::Mistrust;
738
    use test_temp_dir::{TestTempDir, TestTempDirGuard, test_temp_dir};
739

            
740
    use tor_basic_utils::test_rng::testing_rng;
741
    use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder};
742
    use tor_llcrypto::pk::ed25519;
743
    use tor_persist::state_dir::InstanceStateHandle;
744

            
745
    use crate::config::OnionServiceConfigBuilder;
746
    use crate::ipt_set::IptSetStorageHandle;
747
    use crate::{HsIdKeypairSpecifier, HsIdPublicKeySpecifier};
748

            
749
    /// The nickname of the test service.
750
    const TEST_SVC_NICKNAME: &str = "test-svc";
751

            
752
    #[test]
753
    fn protocols() {
754
        let pr = supported_hsservice_protocols();
755
        let expected = "HSIntro=4-5 HSRend=2 HSDir=2".parse().unwrap();
756
        assert_eq!(pr, expected);
757
    }
758

            
759
    /// Make a fresh `KeyMgr` (containing no keys) using files in `temp_dir`
760
    pub(crate) fn create_keymgr(temp_dir: &TestTempDir) -> TestTempDirGuard<Arc<KeyMgr>> {
761
        temp_dir.subdir_used_by("keystore", |keystore_dir| {
762
            let keystore = ArtiNativeKeystore::from_path_and_mistrust(
763
                keystore_dir,
764
                &Mistrust::new_dangerously_trust_everyone(),
765
            )
766
            .unwrap();
767

            
768
            Arc::new(
769
                KeyMgrBuilder::default()
770
                    .primary_store(Box::new(keystore))
771
                    .build()
772
                    .unwrap(),
773
            )
774
        })
775
    }
776

            
777
    #[allow(clippy::let_and_return)] // clearer and more regular
778
    pub(crate) fn mk_state_instance(dir: &Path, nick: impl Display) -> InstanceStateHandle {
779
        let nick = HsNickname::new(nick.to_string()).unwrap();
780
        let mistrust = fs_mistrust::Mistrust::new_dangerously_trust_everyone();
781
        let state_dir = StateDirectory::new(dir, &mistrust).unwrap();
782
        let instance = state_dir.acquire_instance(&nick).unwrap();
783
        instance
784
    }
785

            
786
    pub(crate) fn create_storage_handles(
787
        dir: &Path,
788
    ) -> (
789
        tor_persist::state_dir::InstanceStateHandle,
790
        IptSetStorageHandle,
791
    ) {
792
        let nick = HsNickname::try_from("allium".to_owned()).unwrap();
793
        create_storage_handles_from_state_dir(dir, &nick)
794
    }
795

            
796
    pub(crate) fn create_storage_handles_from_state_dir(
797
        state_dir: &Path,
798
        nick: &HsNickname,
799
    ) -> (
800
        tor_persist::state_dir::InstanceStateHandle,
801
        IptSetStorageHandle,
802
    ) {
803
        let instance = mk_state_instance(state_dir, nick);
804
        let iptpub_state_handle = instance.storage_handle("iptpub").unwrap();
805
        (instance, iptpub_state_handle)
806
    }
807

            
808
    macro_rules! maybe_generate_hsid {
809
        ($keymgr:expr, $offline_hsid:expr) => {{
810
            let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
811
            let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
812
            let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
813

            
814
            assert!($keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
815
            assert!($keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
816

            
817
            maybe_generate_hsid(&$keymgr, &nickname, $offline_hsid, Default::default()).unwrap();
818
        }};
819
    }
820

            
821
    /// Create a test hsid keypair.
822
    fn create_hsid() -> (HsIdKeypair, HsIdKey) {
823
        let mut rng = testing_rng();
824
        let keypair = ed25519::Keypair::generate(&mut rng);
825

            
826
        let id_pub = HsIdKey::from(keypair.verifying_key());
827
        let id_keypair = HsIdKeypair::from(ed25519::ExpandedKeypair::from(&keypair));
828

            
829
        (id_keypair, id_pub)
830
    }
831

            
832
    #[test]
833
    fn generate_hsid() {
834
        let temp_dir = test_temp_dir!();
835
        let keymgr = create_keymgr(&temp_dir);
836

            
837
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
838
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
839

            
840
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
841
        maybe_generate_hsid!(keymgr, false /* offline_hsid */);
842
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_some());
843
    }
844

            
845
    #[test]
846
    fn hsid_keypair_already_exists() {
847
        let temp_dir = test_temp_dir!();
848
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
849
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
850
        let keymgr = create_keymgr(&temp_dir);
851

            
852
        // Insert the preexisting hsid keypair.
853
        let (existing_hsid_keypair, existing_hsid_public) = create_hsid();
854
        let existing_keypair: ed25519::ExpandedKeypair = existing_hsid_keypair.into();
855
        let existing_hsid_keypair = HsIdKeypair::from(existing_keypair);
856

            
857
        keymgr
858
            .insert(
859
                existing_hsid_keypair,
860
                &hsid_spec,
861
                KeystoreSelector::Primary,
862
                true,
863
            )
864
            .unwrap();
865

            
866
        maybe_generate_hsid(
867
            &keymgr,
868
            &nickname,
869
            false, /* offline_hsid */
870
            Default::default(),
871
        )
872
        .unwrap();
873

            
874
        let keypair = keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().unwrap();
875
        let pk: HsIdKey = (&keypair).into();
876

            
877
        assert_eq!(pk.as_ref(), existing_hsid_public.as_ref());
878
    }
879

            
880
    #[test]
881
    #[ignore] // TODO (#1194): Revisit when we add support for offline hsid mode
882
    fn generate_hsid_offline_hsid() {
883
        let temp_dir = test_temp_dir!();
884
        let keymgr = create_keymgr(&temp_dir);
885

            
886
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
887
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
888
        let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
889

            
890
        maybe_generate_hsid!(keymgr, true /* offline_hsid */);
891

            
892
        assert!(keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
893
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
894
    }
895

            
896
    #[test]
897
    #[ignore] // TODO (#1194): Revisit when we add support for offline hsid mode
898
    fn generate_hsid_corrupt_keystore() {
899
        let temp_dir = test_temp_dir!();
900
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
901
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
902
        let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
903

            
904
        let keymgr = create_keymgr(&temp_dir);
905

            
906
        let (hsid_keypair, _hsid_public) = create_hsid();
907
        let (_hsid_keypair, hsid_public) = create_hsid();
908

            
909
        keymgr
910
            .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
911
            .unwrap();
912

            
913
        // Insert a mismatched public key
914
        keymgr
915
            .insert(hsid_public, &pub_hsid_spec, KeystoreSelector::Primary, true)
916
            .unwrap();
917

            
918
        assert!(
919
            maybe_generate_hsid(
920
                &keymgr,
921
                &nickname,
922
                false, /* offline_hsid */
923
                Default::default()
924
            )
925
            .is_err()
926
        );
927
    }
928

            
929
    #[test]
930
    fn onion_address() {
931
        let temp_dir = test_temp_dir!();
932
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
933
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
934
        let keymgr = create_keymgr(&temp_dir);
935

            
936
        let (hsid_keypair, hsid_public) = create_hsid();
937

            
938
        // Insert the hsid into the keystore
939
        keymgr
940
            .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
941
            .unwrap();
942

            
943
        let config = OnionServiceConfigBuilder::default()
944
            .nickname(nickname)
945
            .build()
946
            .unwrap();
947

            
948
        let state_dir = StateDirectory::new(
949
            temp_dir.as_path_untracked(),
950
            &fs_mistrust::Mistrust::new_dangerously_trust_everyone(),
951
        )
952
        .unwrap();
953

            
954
        let service = OnionService::builder()
955
            .config(config)
956
            .keymgr(Arc::clone(&*keymgr))
957
            .state_dir(state_dir)
958
            .build()
959
            .unwrap();
960

            
961
        let hsid = HsId::from(hsid_public);
962
        assert_eq!(service.onion_address().unwrap(), hsid);
963

            
964
        drop(temp_dir); // prove that this is still live
965
    }
966
}