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
#![deny(clippy::string_slice)] // See arti#2571
48
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
49

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

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

            
59
mod internal_prelude;
60

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

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

            
97
use std::pin::Pin;
98

            
99
use internal_prelude::*;
100

            
101
// ---------- public exports ----------
102

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

            
119
pub use helpers::handle_rend_requests;
120

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

            
124
//---------- top-level service implementation (types and methods) ----------
125

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

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

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

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

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

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

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

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

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

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

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

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

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

            
210
        Ok(())
211
    }
212
}
213

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

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

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

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

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

            
284
        let nickname = config.nickname.clone();
285

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

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

            
295
        if !config.enabled() {
296
            return Ok(None);
297
        }
298

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

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

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

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

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

            
346
        let (shutdown_tx, shutdown_rx) = broadcast::channel(0);
347

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
549
        Ok(rend_req_rx)
550
    }
551

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

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

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

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

            
597
4
    let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
598

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

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

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

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

            
639
4
    Ok(hsid)
640
4
}
641

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

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

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

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

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

            
704
            if is_expired {
705
                expired_keys.push(entry.clone());
706
            }
707

            
708
            tor_keymgr::Result::Ok(())
709
        };
710

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

            
719
        append_if_expired!(BlindIdPublicKeySpecifier);
720
        append_if_expired!(BlindIdKeypairSpecifier);
721
        append_if_expired!(DescSigningKeypairSpecifier);
722
    }
723

            
724
    Ok(expired_keys)
725
}
726

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

            
745
    use std::fmt::Display;
746
    use std::path::Path;
747

            
748
    use fs_mistrust::Mistrust;
749
    use test_temp_dir::{TestTempDir, TestTempDirGuard, test_temp_dir};
750

            
751
    use tor_basic_utils::test_rng::testing_rng;
752
    use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder};
753
    use tor_llcrypto::pk::ed25519;
754
    use tor_persist::state_dir::InstanceStateHandle;
755

            
756
    use crate::config::OnionServiceConfigBuilder;
757
    use crate::ipt_set::IptSetStorageHandle;
758
    use crate::{HsIdKeypairSpecifier, HsIdPublicKeySpecifier};
759

            
760
    /// The nickname of the test service.
761
    const TEST_SVC_NICKNAME: &str = "test-svc";
762

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

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

            
779
            Arc::new(
780
                KeyMgrBuilder::default()
781
                    .primary_store(Box::new(keystore))
782
                    .build()
783
                    .unwrap(),
784
            )
785
        })
786
    }
787

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

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

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

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

            
825
            assert!($keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
826
            assert!($keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
827

            
828
            maybe_generate_hsid(&$keymgr, &nickname, $offline_hsid, Default::default()).unwrap();
829
        }};
830
    }
831

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

            
837
        let id_pub = HsIdKey::from(keypair.verifying_key());
838
        let id_keypair = HsIdKeypair::from(ed25519::ExpandedKeypair::from(&keypair));
839

            
840
        (id_keypair, id_pub)
841
    }
842

            
843
    #[test]
844
    fn generate_hsid() {
845
        let temp_dir = test_temp_dir!();
846
        let keymgr = create_keymgr(&temp_dir);
847

            
848
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
849
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
850

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

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

            
863
        // Insert the preexisting hsid keypair.
864
        let (existing_hsid_keypair, existing_hsid_public) = create_hsid();
865
        let existing_keypair: ed25519::ExpandedKeypair = existing_hsid_keypair.into();
866
        let existing_hsid_keypair = HsIdKeypair::from(existing_keypair);
867

            
868
        keymgr
869
            .insert(
870
                existing_hsid_keypair,
871
                &hsid_spec,
872
                KeystoreSelector::Primary,
873
                true,
874
            )
875
            .unwrap();
876

            
877
        maybe_generate_hsid(
878
            &keymgr,
879
            &nickname,
880
            false, /* offline_hsid */
881
            Default::default(),
882
        )
883
        .unwrap();
884

            
885
        let keypair = keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().unwrap();
886
        let pk: HsIdKey = (&keypair).into();
887

            
888
        assert_eq!(pk.as_ref(), existing_hsid_public.as_ref());
889
    }
890

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

            
897
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
898
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
899
        let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
900

            
901
        maybe_generate_hsid!(keymgr, true /* offline_hsid */);
902

            
903
        assert!(keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
904
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
905
    }
906

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

            
915
        let keymgr = create_keymgr(&temp_dir);
916

            
917
        let (hsid_keypair, _hsid_public) = create_hsid();
918
        let (_hsid_keypair, hsid_public) = create_hsid();
919

            
920
        keymgr
921
            .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
922
            .unwrap();
923

            
924
        // Insert a mismatched public key
925
        keymgr
926
            .insert(hsid_public, &pub_hsid_spec, KeystoreSelector::Primary, true)
927
            .unwrap();
928

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

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

            
947
        let (hsid_keypair, hsid_public) = create_hsid();
948

            
949
        // Insert the hsid into the keystore
950
        keymgr
951
            .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
952
            .unwrap();
953

            
954
        let config = OnionServiceConfigBuilder::default()
955
            .nickname(nickname)
956
            .build()
957
            .unwrap();
958

            
959
        let state_dir = StateDirectory::new(
960
            temp_dir.as_path_untracked(),
961
            &fs_mistrust::Mistrust::new_dangerously_trust_everyone(),
962
        )
963
        .unwrap();
964

            
965
        let service = OnionService::builder()
966
            .config(config)
967
            .keymgr(Arc::clone(&*keymgr))
968
            .state_dir(state_dir)
969
            .build()
970
            .unwrap();
971

            
972
        let hsid = HsId::from(hsid_public);
973
        assert_eq!(service.onion_address().unwrap(), hsid);
974

            
975
        drop(temp_dir); // prove that this is still live
976
    }
977
}