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
#![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_time_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
#![allow(clippy::collapsible_if)] // See arti#2342
46
#![deny(clippy::unused_async)]
47
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
48

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

            
55
#[macro_use] // SerdeStringOrTransparent
56
mod time_store;
57

            
58
mod internal_prelude;
59

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

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

            
95
use std::pin::Pin;
96

            
97
use internal_prelude::*;
98

            
99
// ---------- public exports ----------
100

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

            
117
pub use helpers::handle_rend_requests;
118

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

            
122
//---------- top-level service implementation (types and methods) ----------
123

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

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

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

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

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

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

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

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

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

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

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

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

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

            
208
        Ok(())
209
    }
210
}
211

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

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

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

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

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

            
282
        let nickname = config.nickname.clone();
283

            
284
        // TODO (#1194): add a config option for specifying whether to expect the KS_hsid to be stored
285
        // offline
286
        //let offline_hsid = config.offline_hsid;
287
        let offline_hsid = false;
288

            
289
        // TODO (#1106): make this configurable
290
        let selector = KeystoreSelector::Primary;
291
        maybe_generate_hsid(&keymgr, &config.nickname, offline_hsid, selector)?;
292

            
293
        if !config.enabled() {
294
            return Ok(None);
295
        }
296

            
297
        if config.restricted_discovery.enabled {
298
            info!(
299
                nickname=%nickname,
300
                "Launching onion service in restricted discovery mode"
301
            );
302
        } else {
303
            info!(
304
                nickname=%nickname,
305
                "Launching onion service"
306
            );
307
        }
308

            
309
        let state_handle = state_dir
310
            .acquire_instance(&config.nickname)
311
            .map_err(StartupError::StateDirectoryInaccessible)?;
312

            
313
        // We pass the "cooked" handle, with the storage key embedded, to ipt_set,
314
        // since the ipt_set code doesn't otherwise have access to the HS nickname.
315
        let iptpub_storage_handle = state_handle
316
            .storage_handle("iptpub")
317
            .map_err(StartupError::StateDirectoryInaccessible)?;
318

            
319
        let status_tx = StatusSender::new(OnionServiceStatus::new_shutdown());
320
        let (config_tx, config_rx) = postage::watch::channel_with(Arc::new(config));
321

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

            
344
        let (shutdown_tx, shutdown_rx) = broadcast::channel(0);
345

            
346
        let (ipt_mgr_view, publisher_view) =
347
            crate::ipt_set::ipts_channel(&runtime, iptpub_storage_handle)?;
348

            
349
        let ipt_mgr = IptManager::new(
350
            runtime.clone(),
351
            netdir_provider.clone(),
352
            nickname.clone(),
353
            config_rx.clone(),
354
            rend_req_tx,
355
            shutdown_rx.clone(),
356
            &state_handle,
357
            crate::ipt_mgr::Real {
358
                circ_pool: circ_pool.clone(),
359
            },
360
            keymgr.clone(),
361
            status_tx.clone().into(),
362
        )?;
363

            
364
        let publisher: Publisher<R, publish::Real<R>> = Publisher::new(
365
            runtime,
366
            nickname.clone(),
367
            netdir_provider,
368
            circ_pool,
369
            publisher_view,
370
            config_rx,
371
            status_tx.clone().into(),
372
            Arc::clone(&keymgr),
373
            path_resolver,
374
            pow_manager.clone(),
375
            publisher_update_rx,
376
        );
377

            
378
        let svc = Arc::new(RunningOnionService {
379
            nickname,
380
            keymgr,
381
            inner: Mutex::new(SvcInner {
382
                config_tx,
383
                _shutdown_tx: shutdown_tx,
384
                status_tx,
385
                unlaunched: Some((
386
                    rend_req_rx,
387
                    Box::new(ForLaunch {
388
                        publisher,
389
                        ipt_mgr,
390
                        ipt_mgr_view,
391
                        pow_manager,
392
                    }),
393
                )),
394
            }),
395
        });
396

            
397
        let stream = svc.launch()?;
398
        Ok(Some((svc, stream)))
399
    }
400

            
401
    /// Return the onion address of this service.
402
    ///
403
    /// Clients must know the service's onion address in order to discover or
404
    /// connect to it.
405
    ///
406
    /// Returns `None` if the HsId of the service could not be found in any of the configured
407
    /// keystores.
408
298
    pub fn onion_address(&self) -> Option<HsId> {
409
298
        onion_address(&self.keymgr, &self.config.nickname)
410
298
    }
411

            
412
    /// Return the onion address of this service.
413
    ///
414
    /// See [`onion_address`](Self::onion_address)
415
    #[deprecated = "Use the new onion_address method instead"]
416
    pub fn onion_name(&self) -> Option<HsId> {
417
        self.onion_address()
418
    }
419

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

            
444
        maybe_generate_hsid(&self.keymgr, &self.config.nickname, offline_hsid, selector)
445
    }
446

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

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

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

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

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

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

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

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

            
535
        match launch.launch() {
536
            Ok(()) => {}
537
            Err(e) => {
538
                return Err(e);
539
            }
540
        }
541

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

            
547
        Ok(rend_req_rx)
548
    }
549

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

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

            
573
    /// Return the onion address of this service.
574
    ///
575
    /// See [`onion_address`](Self::onion_address)
576
    #[deprecated = "Use the new onion_address method instead"]
577
    pub fn onion_name(&self) -> Option<HsId> {
578
        self.onion_address()
579
    }
580
}
581

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

            
595
4
    let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
596

            
597
4
    let kp = keymgr
598
4
        .get::<HsIdKey>(&hsid_spec)
599
4
        .map_err(|cause| StartupError::Keystore {
600
            action: "read",
601
            cause,
602
        })?;
603

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

            
619
2
            (HsIdKey::from(&kp).id(), true)
620
        }
621
    };
622

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

            
637
4
    Ok(hsid)
638
4
}
639

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

            
654
298
    keymgr
655
298
        .get::<HsIdKey>(&hsid_spec)
656
298
        .ok()?
657
306
        .map(|hsid| hsid.id())
658
298
}
659

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

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

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

            
702
            if is_expired {
703
                expired_keys.push(entry.clone());
704
            }
705

            
706
            tor_keymgr::Result::Ok(())
707
        };
708

            
709
        macro_rules! append_if_expired {
710
            ($K:ty) => {{
711
                if let Ok(spec) = <$K>::try_from(key_path) {
712
                    append_if_expired(&spec)?;
713
                }
714
            }};
715
        }
716

            
717
        append_if_expired!(BlindIdPublicKeySpecifier);
718
        append_if_expired!(BlindIdKeypairSpecifier);
719
        append_if_expired!(DescSigningKeypairSpecifier);
720
    }
721

            
722
    Ok(expired_keys)
723
}
724

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

            
742
    use std::fmt::Display;
743
    use std::path::Path;
744

            
745
    use fs_mistrust::Mistrust;
746
    use test_temp_dir::{TestTempDir, TestTempDirGuard, test_temp_dir};
747

            
748
    use tor_basic_utils::test_rng::testing_rng;
749
    use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder};
750
    use tor_llcrypto::pk::ed25519;
751
    use tor_persist::state_dir::InstanceStateHandle;
752

            
753
    use crate::config::OnionServiceConfigBuilder;
754
    use crate::ipt_set::IptSetStorageHandle;
755
    use crate::{HsIdKeypairSpecifier, HsIdPublicKeySpecifier};
756

            
757
    /// The nickname of the test service.
758
    const TEST_SVC_NICKNAME: &str = "test-svc";
759

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

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

            
776
            Arc::new(
777
                KeyMgrBuilder::default()
778
                    .primary_store(Box::new(keystore))
779
                    .build()
780
                    .unwrap(),
781
            )
782
        })
783
    }
784

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

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

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

            
816
    macro_rules! maybe_generate_hsid {
817
        ($keymgr:expr, $offline_hsid:expr) => {{
818
            let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
819
            let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
820
            let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
821

            
822
            assert!($keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
823
            assert!($keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
824

            
825
            maybe_generate_hsid(&$keymgr, &nickname, $offline_hsid, Default::default()).unwrap();
826
        }};
827
    }
828

            
829
    /// Create a test hsid keypair.
830
    fn create_hsid() -> (HsIdKeypair, HsIdKey) {
831
        let mut rng = testing_rng();
832
        let keypair = ed25519::Keypair::generate(&mut rng);
833

            
834
        let id_pub = HsIdKey::from(keypair.verifying_key());
835
        let id_keypair = HsIdKeypair::from(ed25519::ExpandedKeypair::from(&keypair));
836

            
837
        (id_keypair, id_pub)
838
    }
839

            
840
    #[test]
841
    fn generate_hsid() {
842
        let temp_dir = test_temp_dir!();
843
        let keymgr = create_keymgr(&temp_dir);
844

            
845
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
846
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
847

            
848
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
849
        maybe_generate_hsid!(keymgr, false /* offline_hsid */);
850
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_some());
851
    }
852

            
853
    #[test]
854
    fn hsid_keypair_already_exists() {
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
        // Insert the preexisting hsid keypair.
861
        let (existing_hsid_keypair, existing_hsid_public) = create_hsid();
862
        let existing_keypair: ed25519::ExpandedKeypair = existing_hsid_keypair.into();
863
        let existing_hsid_keypair = HsIdKeypair::from(existing_keypair);
864

            
865
        keymgr
866
            .insert(
867
                existing_hsid_keypair,
868
                &hsid_spec,
869
                KeystoreSelector::Primary,
870
                true,
871
            )
872
            .unwrap();
873

            
874
        maybe_generate_hsid(
875
            &keymgr,
876
            &nickname,
877
            false, /* offline_hsid */
878
            Default::default(),
879
        )
880
        .unwrap();
881

            
882
        let keypair = keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().unwrap();
883
        let pk: HsIdKey = (&keypair).into();
884

            
885
        assert_eq!(pk.as_ref(), existing_hsid_public.as_ref());
886
    }
887

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

            
894
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
895
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
896
        let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
897

            
898
        maybe_generate_hsid!(keymgr, true /* offline_hsid */);
899

            
900
        assert!(keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
901
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
902
    }
903

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

            
912
        let keymgr = create_keymgr(&temp_dir);
913

            
914
        let (hsid_keypair, _hsid_public) = create_hsid();
915
        let (_hsid_keypair, hsid_public) = create_hsid();
916

            
917
        keymgr
918
            .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
919
            .unwrap();
920

            
921
        // Insert a mismatched public key
922
        keymgr
923
            .insert(hsid_public, &pub_hsid_spec, KeystoreSelector::Primary, true)
924
            .unwrap();
925

            
926
        assert!(
927
            maybe_generate_hsid(
928
                &keymgr,
929
                &nickname,
930
                false, /* offline_hsid */
931
                Default::default()
932
            )
933
            .is_err()
934
        );
935
    }
936

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

            
944
        let (hsid_keypair, hsid_public) = create_hsid();
945

            
946
        // Insert the hsid into the keystore
947
        keymgr
948
            .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
949
            .unwrap();
950

            
951
        let config = OnionServiceConfigBuilder::default()
952
            .nickname(nickname)
953
            .build()
954
            .unwrap();
955

            
956
        let state_dir = StateDirectory::new(
957
            temp_dir.as_path_untracked(),
958
            &fs_mistrust::Mistrust::new_dangerously_trust_everyone(),
959
        )
960
        .unwrap();
961

            
962
        let service = OnionService::builder()
963
            .config(config)
964
            .keymgr(Arc::clone(&*keymgr))
965
            .state_dir(state_dir)
966
            .build()
967
            .unwrap();
968

            
969
        let hsid = HsId::from(hsid_public);
970
        assert_eq!(service.onion_address().unwrap(), hsid);
971

            
972
        drop(temp_dir); // prove that this is still live
973
    }
974
}