1
#![cfg_attr(docsrs, feature(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
#![allow(clippy::cognitive_complexity)] // See arti#2556
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
#![warn(clippy::or_fun_call)]
28
#![deny(clippy::print_stderr)]
29
#![deny(clippy::print_stdout)]
30
#![warn(clippy::rc_buffer)]
31
#![deny(clippy::ref_option_ref)]
32
#![warn(clippy::semicolon_if_nothing_returned)]
33
#![warn(clippy::trait_duplication_in_bounds)]
34
#![deny(clippy::unchecked_time_subtraction)]
35
#![deny(clippy::unnecessary_wraps)]
36
#![warn(clippy::unseparated_literal_suffix)]
37
#![deny(clippy::unwrap_used)]
38
#![deny(clippy::mod_module_files)]
39
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
40
#![allow(clippy::uninlined_format_args)]
41
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
42
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
43
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
44
#![allow(clippy::needless_lifetimes)] // See arti#1765
45
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
46
#![allow(clippy::collapsible_if)] // See arti#2342
47
#![deny(clippy::unused_async)]
48
#![deny(clippy::string_slice)] // See arti#2571
49
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
50
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
51

            
52
// TODO beta clippy bug, rust-clippy/issues/17525
53
#![allow(clippy::redundant_field_names)]
54
// TODO #1645 (either remove this, or decide to have it everywhere)
55
#![cfg_attr(
56
    not(all(feature = "full", feature = "experimental")),
57
    allow(unused, unreachable_pub)
58
)]
59

            
60
#[macro_use] // SerdeStringOrTransparent
61
mod time_store;
62

            
63
mod internal_prelude;
64

            
65
mod anon_level;
66
mod caps;
67
pub mod config;
68
mod err;
69
mod helpers;
70
mod ipt_establish;
71
mod ipt_lid;
72
mod ipt_mgr;
73
mod ipt_set;
74
mod keys;
75
mod pow;
76
mod publish;
77
mod rend_handshake;
78
mod replay;
79
mod req;
80
pub mod status;
81
mod timeout_track;
82

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

            
101
use std::pin::Pin;
102

            
103
use internal_prelude::*;
104

            
105
// ---------- public exports ----------
106

            
107
pub use anon_level::Anonymity;
108
pub use config::OnionServiceConfig;
109
pub use err::{ClientError, EstablishSessionError, FatalError, IntroRequestError, StartupError};
110
pub use ipt_mgr::IptError;
111
use keys::HsTimePeriodKeySpecifier;
112
pub use keys::{
113
    BlindIdKeypairSpecifier, BlindIdPublicKeySpecifier, DescSigningKeypairSpecifier,
114
    HsIdKeypairSpecifier, HsIdPublicKeySpecifier,
115
};
116
use pow::{NewPowManager, PowManager};
117
pub use publish::UploadError as DescUploadError;
118
pub use req::{RendRequest, StreamRequest};
119
pub use tor_hscrypto::pk::HsId;
120
use tor_keymgr::KeystoreEntry;
121
pub use tor_persist::hsnickname::{HsNickname, InvalidNickname};
122

            
123
pub use helpers::handle_rend_requests;
124

            
125
#[cfg(feature = "onion-service-cli-extra")]
126
use tor_netdir::NetDir;
127

            
128
//---------- top-level service implementation (types and methods) ----------
129

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

            
133
/// Convenient type alias for an ntor public key
134
// TODO (#1022) maybe this should be
135
// `tor_proto::crypto::handshake::ntor::NtorPublicKey`,
136
// or a unified OnionKey type.
137
pub(crate) type NtorPublicKey = curve25519::PublicKey;
138

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

            
156
/// Implementation details for an onion service.
157
struct SvcInner {
158
    /// Configuration information about this service.
159
    config_tx: postage::watch::Sender<Arc<OnionServiceConfig>>,
160

            
161
    /// A oneshot that will be dropped when this object is dropped.
162
    _shutdown_tx: postage::broadcast::Sender<void::Void>,
163

            
164
    /// Postage sender, used to tell subscribers about changes in the status of
165
    /// this onion service.
166
    status_tx: StatusSender,
167

            
168
    /// Handles that we'll take ownership of when launching the service.
169
    #[allow(clippy::type_complexity)]
170
    unlaunched: Option<(
171
        Pin<Box<dyn Stream<Item = RendRequest> + Send + Sync>>,
172
        Box<dyn Launchable + Send + Sync>,
173
    )>,
174
}
175

            
176
/// Objects and handles needed to launch an onion service.
177
struct ForLaunch<R: Runtime> {
178
    /// An unlaunched handle for the HsDesc publisher.
179
    ///
180
    /// This publisher is responsible for determining when we need to upload a
181
    /// new set of HsDescs, building them, and publishing them at the correct
182
    /// HsDirs.
183
    publisher: Publisher<R, publish::Real<R>>,
184

            
185
    /// Our handler for the introduction point manager.
186
    ///
187
    /// This manager is responsible for selecting introduction points,
188
    /// maintaining our connections to them, and telling the publisher which ones
189
    /// are publicly available.
190
    ipt_mgr: IptManager<R, crate::ipt_mgr::Real<R>>,
191

            
192
    /// A handle used by the ipt manager to send Ipts to the publisher.
193
    ///
194
    ///
195
    ipt_mgr_view: IptsManagerView,
196

            
197
    /// Proof-of-work manager.
198
    pow_manager: Arc<PowManager<R>>,
199
}
200

            
201
/// Private trait used to type-erase `ForLaunch<R>`, so that we don't need to
202
/// parameterize OnionService on `<R>`.
203
trait Launchable: Send + Sync {
204
    /// Launch
205
    fn launch(self: Box<Self>) -> Result<(), StartupError>;
206
}
207

            
208
impl<R: Runtime> Launchable for ForLaunch<R> {
209
    fn launch(self: Box<Self>) -> Result<(), StartupError> {
210
        self.ipt_mgr.launch_background_tasks(self.ipt_mgr_view)?;
211
        self.publisher.launch()?;
212
        self.pow_manager.launch()?;
213

            
214
        Ok(())
215
    }
216
}
217

            
218
/// Return value from one call to the main loop iteration
219
///
220
/// Used by the publisher reactor and by the [`IptManager`].
221
#[derive(PartialEq)]
222
#[must_use]
223
pub(crate) enum ShutdownStatus {
224
    /// We should continue to operate this component
225
    Continue,
226
    /// We should shut down: the service, or maybe the whole process, is shutting down
227
    Terminate,
228
}
229

            
230
impl From<oneshot::Canceled> for ShutdownStatus {
231
    fn from(_: oneshot::Canceled) -> ShutdownStatus {
232
        ShutdownStatus::Terminate
233
    }
234
}
235

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

            
255
impl OnionService {
256
    /// Create an [`OnionServiceBuilder`].
257
289
    pub fn builder() -> OnionServiceBuilder {
258
289
        OnionServiceBuilder::default()
259
289
    }
260

            
261
    /// Tell this onion service to begin running, and return a
262
    /// [`RunningOnionService`] and its stream of rendezvous requests.
263
    ///
264
    /// Returns `Ok(None)` if the service specified is disabled in the config.
265
    ///
266
    /// You can turn the resulting stream into a stream of [`StreamRequest`]
267
    /// using the [`handle_rend_requests`] helper function.
268
    ///
269
    /// Once the `RunningOnionService` is dropped, the onion service will stop
270
    /// publishing, and stop accepting new introduction requests.  Existing
271
    /// streams and rendezvous circuits will remain open.
272
    pub fn launch<R>(
273
        self,
274
        runtime: R,
275
        netdir_provider: Arc<dyn NetDirProvider>,
276
        circ_pool: Arc<HsCircPool<R>>,
277
        path_resolver: Arc<tor_config_path::CfgPathResolver>,
278
    ) -> Result<Option<(Arc<RunningOnionService>, impl Stream<Item = RendRequest>)>, StartupError>
279
    where
280
        R: Runtime,
281
    {
282
        let OnionService {
283
            config,
284
            keymgr,
285
            state_dir,
286
        } = self;
287

            
288
        let nickname = config.nickname.clone();
289

            
290
        // TODO (#1194): add a config option for specifying whether to expect the KS_hsid to be stored
291
        // offline
292
        //let offline_hsid = config.offline_hsid;
293
        let offline_hsid = false;
294

            
295
        // TODO (#1106): make this configurable
296
        let selector = KeystoreSelector::Primary;
297
        maybe_generate_hsid(&keymgr, &config.nickname, offline_hsid, selector)?;
298

            
299
        if !config.enabled() {
300
            return Ok(None);
301
        }
302

            
303
        if config.restricted_discovery.enabled {
304
            info!(
305
                nickname=%nickname,
306
                "Launching onion service in restricted discovery mode"
307
            );
308
        } else {
309
            info!(
310
                nickname=%nickname,
311
                "Launching onion service"
312
            );
313
        }
314

            
315
        let state_handle = state_dir
316
            .acquire_instance(&config.nickname)
317
            .map_err(StartupError::StateDirectoryInaccessible)?;
318

            
319
        // We pass the "cooked" handle, with the storage key embedded, to ipt_set,
320
        // since the ipt_set code doesn't otherwise have access to the HS nickname.
321
        let iptpub_storage_handle = state_handle
322
            .storage_handle("iptpub")
323
            .map_err(StartupError::StateDirectoryInaccessible)?;
324

            
325
        let status_tx = StatusSender::new(OnionServiceStatus::new_shutdown());
326
        let (config_tx, config_rx) = postage::watch::channel_with(Arc::new(config));
327

            
328
        let pow_manager_storage_handle = state_handle
329
            .storage_handle("pow_manager")
330
            .map_err(StartupError::StateDirectoryInaccessible)?;
331
        let pow_nonce_dir = state_handle
332
            .raw_subdir("pow_nonces")
333
            .map_err(StartupError::StateDirectoryInaccessible)?;
334
        let NewPowManager {
335
            pow_manager,
336
            rend_req_tx,
337
            rend_req_rx,
338
            publisher_update_rx,
339
        } = PowManager::new(
340
            runtime.clone(),
341
            nickname.clone(),
342
            pow_nonce_dir,
343
            keymgr.clone(),
344
            pow_manager_storage_handle,
345
            netdir_provider.clone(),
346
            status_tx.clone().into(),
347
            config_rx.clone(),
348
        )?;
349

            
350
        let (shutdown_tx, shutdown_rx) = broadcast::channel(0);
351

            
352
        let (ipt_mgr_view, publisher_view) =
353
            crate::ipt_set::ipts_channel(&runtime, iptpub_storage_handle)?;
354

            
355
        let ipt_mgr = IptManager::new(
356
            runtime.clone(),
357
            netdir_provider.clone(),
358
            nickname.clone(),
359
            config_rx.clone(),
360
            rend_req_tx,
361
            shutdown_rx.clone(),
362
            &state_handle,
363
            crate::ipt_mgr::Real {
364
                circ_pool: circ_pool.clone(),
365
            },
366
            keymgr.clone(),
367
            status_tx.clone().into(),
368
        )?;
369

            
370
        let bad_hsdescs_dir = state_handle
371
            .raw_subdir("bad_hsdescs")
372
            .map_err(StartupError::StateDirectoryInaccessible)?;
373

            
374
        let publisher: Publisher<R, publish::Real<R>> = Publisher::new(
375
            runtime,
376
            nickname.clone(),
377
            netdir_provider,
378
            circ_pool,
379
            publisher_view,
380
            config_rx,
381
            status_tx.clone().into(),
382
            Arc::clone(&keymgr),
383
            path_resolver,
384
            pow_manager.clone(),
385
            publisher_update_rx,
386
            bad_hsdescs_dir,
387
        );
388

            
389
        let svc = Arc::new(RunningOnionService {
390
            nickname,
391
            keymgr,
392
            inner: Mutex::new(SvcInner {
393
                config_tx,
394
                _shutdown_tx: shutdown_tx,
395
                status_tx,
396
                unlaunched: Some((
397
                    rend_req_rx,
398
                    Box::new(ForLaunch {
399
                        publisher,
400
                        ipt_mgr,
401
                        ipt_mgr_view,
402
                        pow_manager,
403
                    }),
404
                )),
405
            }),
406
        });
407

            
408
        let stream = svc.launch()?;
409
        Ok(Some((svc, stream)))
410
    }
411

            
412
    /// Return the onion address of this service.
413
    ///
414
    /// Clients must know the service's onion address in order to discover or
415
    /// connect to it.
416
    ///
417
    /// Returns `None` if the HsId of the service could not be found in any of the configured
418
    /// keystores.
419
289
    pub fn onion_address(&self) -> Option<HsId> {
420
289
        onion_address(&self.keymgr, &self.config.nickname)
421
289
    }
422

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

            
447
        maybe_generate_hsid(&self.keymgr, &self.config.nickname, offline_hsid, selector)
448
    }
449

            
450
    /// List the no-longer-relevant keys of this service.
451
    ///
452
    /// Returns the [`KeystoreEntry`]s associated with time periods that are not
453
    /// "relevant" according to the specified [`NetDir`],
454
    /// (i.e. the keys associated with time periods
455
    /// the service is not publishing descriptors for).
456
    // TODO: unittest
457
    #[cfg(feature = "onion-service-cli-extra")]
458
    pub fn list_expired_keys(&self, netdir: &NetDir) -> tor_keymgr::Result<Vec<KeystoreEntry>> {
459
        list_expired_keys_for_service(
460
            &netdir.hs_all_time_periods(),
461
            self.config.nickname(),
462
            &self.keymgr,
463
        )
464
    }
465
}
466

            
467
impl OnionServiceBuilder {
468
    /// Build the [`OnionService`]
469
289
    pub fn build(&self) -> Result<OnionService, StartupError> {
470
289
        let svc = self.build_unvalidated()?;
471
289
        Ok(svc)
472
289
    }
473
}
474

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

            
497
        // TODO (#1153, #1209): We need to make sure that the various tasks listening on
498
        // config_rx actually enforce the configuration, not only on new
499
        // connections, but existing ones.
500
    }
501

            
502
    /*
503
    /// Tell this onion service about some new short-term keys it can use.
504
    pub fn add_keys(&self, keys: ()) -> Result<(), Bug> {
505
        todo!() // TODO #1194
506
    }
507
    */
508

            
509
    /// Return the current status of this onion service.
510
    pub fn status(&self) -> OnionServiceStatus {
511
        self.inner.lock().expect("poisoned lock").status_tx.get()
512
    }
513

            
514
    /// Return a stream of events that will receive notifications of changes in
515
    /// this onion service's status.
516
    pub fn status_events(&self) -> OnionServiceStatusStream {
517
        self.inner
518
            .lock()
519
            .expect("poisoned lock")
520
            .status_tx
521
            .subscribe()
522
    }
523

            
524
    /// Tell this onion service to begin running, and return a
525
    /// stream of rendezvous requests on the service.
526
    ///
527
    /// You can turn the resulting stream into a stream of [`StreamRequest`]
528
    /// using the [`handle_rend_requests`] helper function.
529
    fn launch(self: &Arc<Self>) -> Result<impl Stream<Item = RendRequest> + use<>, StartupError> {
530
        let (rend_req_rx, launch) = {
531
            let mut inner = self.inner.lock().expect("poisoned lock");
532
            inner
533
                .unlaunched
534
                .take()
535
                .ok_or(StartupError::AlreadyLaunched)?
536
        };
537

            
538
        match launch.launch() {
539
            Ok(()) => {}
540
            Err(e) => {
541
                return Err(e);
542
            }
543
        }
544

            
545
        // This needs to launch at least the following tasks:
546
        //
547
        // TODO (#1194) If we decide to use separate disk-based key
548
        // provisioning, we need a task to monitor our keys directory.
549

            
550
        Ok(rend_req_rx)
551
    }
552

            
553
    /*
554
    /// Tell this onion service to stop running.
555
    ///
556
    /// It can be restarted with launch().
557
    ///
558
    /// You can also shut down an onion service completely by dropping the last
559
    /// Clone of it.
560
    pub fn pause(&self) {
561
        todo!() // TODO (#1231)
562
    }
563
    */
564

            
565
    /// Return the onion address of this service.
566
    ///
567
    /// Clients must know the service's onion address in order to discover or
568
    /// connect to it.
569
    ///
570
    /// Returns `None` if the HsId of the service could not be found in any of the configured
571
    /// keystores.
572
    pub fn onion_address(&self) -> Option<HsId> {
573
        onion_address(&self.keymgr, &self.nickname)
574
    }
575
}
576

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

            
590
2
    let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
591

            
592
2
    let kp = keymgr
593
2
        .get::<HsIdKey>(&hsid_spec)
594
2
        .map_err(|cause| StartupError::Keystore {
595
            action: "read",
596
            cause,
597
        })?;
598

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

            
614
1
            (HsIdKey::from(&kp).id(), true)
615
        }
616
    };
617

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

            
632
2
    Ok(hsid)
633
2
}
634

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

            
649
289
    keymgr
650
289
        .get::<HsIdKey>(&hsid_spec)
651
289
        .ok()?
652
289
        .map(|hsid| hsid.id())
653
289
}
654

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

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

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

            
697
            if is_expired {
698
                expired_keys.push(entry.clone());
699
            }
700

            
701
            tor_keymgr::Result::Ok(())
702
        };
703

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

            
712
        append_if_expired!(BlindIdPublicKeySpecifier);
713
        append_if_expired!(BlindIdKeypairSpecifier);
714
        append_if_expired!(DescSigningKeypairSpecifier);
715
    }
716

            
717
    Ok(expired_keys)
718
}
719

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

            
738
    use std::fmt::Display;
739
    use std::path::Path;
740

            
741
    use fs_mistrust::Mistrust;
742
    use test_temp_dir::{TestTempDir, TestTempDirGuard, test_temp_dir};
743

            
744
    use tor_basic_utils::test_rng::testing_rng;
745
    use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder};
746
    use tor_llcrypto::pk::ed25519;
747
    use tor_persist::state_dir::InstanceStateHandle;
748

            
749
    use crate::config::OnionServiceConfigBuilder;
750
    use crate::ipt_set::IptSetStorageHandle;
751
    use crate::{HsIdKeypairSpecifier, HsIdPublicKeySpecifier};
752

            
753
    /// The nickname of the test service.
754
    const TEST_SVC_NICKNAME: &str = "test-svc";
755

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

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

            
772
            Arc::new(
773
                KeyMgrBuilder::default()
774
                    .primary_store(Box::new(keystore))
775
                    .build()
776
                    .unwrap(),
777
            )
778
        })
779
    }
780

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

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

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

            
812
    macro_rules! maybe_generate_hsid {
813
        ($keymgr:expr, $offline_hsid:expr) => {{
814
            let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
815
            let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
816
            let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
817

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

            
821
            maybe_generate_hsid(&$keymgr, &nickname, $offline_hsid, Default::default()).unwrap();
822
        }};
823
    }
824

            
825
    /// Create a test hsid keypair.
826
    fn create_hsid() -> (HsIdKeypair, HsIdKey) {
827
        let mut rng = testing_rng();
828
        let keypair = ed25519::Keypair::generate(&mut rng);
829

            
830
        let id_pub = HsIdKey::from(keypair.verifying_key());
831
        let id_keypair = HsIdKeypair::from(ed25519::ExpandedKeypair::from(&keypair));
832

            
833
        (id_keypair, id_pub)
834
    }
835

            
836
    #[test]
837
    fn generate_hsid() {
838
        let temp_dir = test_temp_dir!();
839
        let keymgr = create_keymgr(&temp_dir);
840

            
841
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
842
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
843

            
844
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
845
        maybe_generate_hsid!(keymgr, false /* offline_hsid */);
846
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_some());
847
    }
848

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

            
856
        // Insert the preexisting hsid keypair.
857
        let (existing_hsid_keypair, existing_hsid_public) = create_hsid();
858
        let existing_keypair: ed25519::ExpandedKeypair = existing_hsid_keypair.into();
859
        let existing_hsid_keypair = HsIdKeypair::from(existing_keypair);
860

            
861
        keymgr
862
            .insert(
863
                existing_hsid_keypair,
864
                &hsid_spec,
865
                KeystoreSelector::Primary,
866
                true,
867
            )
868
            .unwrap();
869

            
870
        maybe_generate_hsid(
871
            &keymgr,
872
            &nickname,
873
            false, /* offline_hsid */
874
            Default::default(),
875
        )
876
        .unwrap();
877

            
878
        let keypair = keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().unwrap();
879
        let pk: HsIdKey = (&keypair).into();
880

            
881
        assert_eq!(pk.as_ref(), existing_hsid_public.as_ref());
882
    }
883

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

            
890
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
891
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
892
        let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
893

            
894
        maybe_generate_hsid!(keymgr, true /* offline_hsid */);
895

            
896
        assert!(keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
897
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
898
    }
899

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

            
908
        let keymgr = create_keymgr(&temp_dir);
909

            
910
        let (hsid_keypair, _hsid_public) = create_hsid();
911
        let (_hsid_keypair, hsid_public) = create_hsid();
912

            
913
        keymgr
914
            .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
915
            .unwrap();
916

            
917
        // Insert a mismatched public key
918
        keymgr
919
            .insert(hsid_public, &pub_hsid_spec, KeystoreSelector::Primary, true)
920
            .unwrap();
921

            
922
        assert!(
923
            maybe_generate_hsid(
924
                &keymgr,
925
                &nickname,
926
                false, /* offline_hsid */
927
                Default::default()
928
            )
929
            .is_err()
930
        );
931
    }
932

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

            
940
        let (hsid_keypair, hsid_public) = create_hsid();
941

            
942
        // Insert the hsid into the keystore
943
        keymgr
944
            .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
945
            .unwrap();
946

            
947
        let config = OnionServiceConfigBuilder::default()
948
            .nickname(nickname)
949
            .build()
950
            .unwrap();
951

            
952
        let state_dir = StateDirectory::new(
953
            temp_dir.as_path_untracked(),
954
            &fs_mistrust::Mistrust::new_dangerously_trust_everyone(),
955
        )
956
        .unwrap();
957

            
958
        let service = OnionService::builder()
959
            .config(config)
960
            .keymgr(Arc::clone(&*keymgr))
961
            .state_dir(state_dir)
962
            .build()
963
            .unwrap();
964

            
965
        let hsid = HsId::from(hsid_public);
966
        assert_eq!(service.onion_address().unwrap(), hsid);
967

            
968
        drop(temp_dir); // prove that this is still live
969
    }
970
}