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
pub mod config;
63
mod err;
64
mod helpers;
65
mod ipt_establish;
66
mod ipt_lid;
67
mod ipt_mgr;
68
mod ipt_set;
69
mod keys;
70
mod pow;
71
mod publish;
72
mod rend_handshake;
73
mod replay;
74
mod req;
75
pub mod status;
76
mod timeout_track;
77

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

            
96
use std::pin::Pin;
97

            
98
use internal_prelude::*;
99

            
100
// ---------- public exports ----------
101

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

            
118
pub use helpers::handle_rend_requests;
119

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
209
        Ok(())
210
    }
211
}
212

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
548
        Ok(rend_req_rx)
549
    }
550

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

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

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

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

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

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

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

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

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

            
638
4
    Ok(hsid)
639
4
}
640

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

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

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

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

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

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

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

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

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

            
723
    Ok(expired_keys)
724
}
725

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
839
        (id_keypair, id_pub)
840
    }
841

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
914
        let keymgr = create_keymgr(&temp_dir);
915

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

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

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

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

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

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

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

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

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

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

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

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