1
#![cfg_attr(docsrs, feature(doc_cfg))]
2
#![doc = include_str!("../README.md")]
3
// @@ begin lint list maintained by maint/add_warning @@
4
#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
5
#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
6
#![warn(missing_docs)]
7
#![warn(noop_method_call)]
8
#![warn(unreachable_pub)]
9
#![warn(clippy::all)]
10
#![deny(clippy::await_holding_lock)]
11
#![deny(clippy::cargo_common_metadata)]
12
#![deny(clippy::cast_lossless)]
13
#![deny(clippy::checked_conversions)]
14
#![allow(clippy::cognitive_complexity)] // See arti#2556
15
#![deny(clippy::debug_assert_with_mut_call)]
16
#![deny(clippy::exhaustive_enums)]
17
#![deny(clippy::exhaustive_structs)]
18
#![deny(clippy::expl_impl_clone_on_copy)]
19
#![deny(clippy::fallible_impl_from)]
20
#![deny(clippy::implicit_clone)]
21
#![deny(clippy::large_stack_arrays)]
22
#![warn(clippy::manual_ok_or)]
23
#![deny(clippy::missing_docs_in_private_items)]
24
#![warn(clippy::needless_borrow)]
25
#![warn(clippy::needless_pass_by_value)]
26
#![warn(clippy::option_option)]
27
#![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
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
49
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
50

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

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

            
62
mod internal_prelude;
63

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

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

            
100
use std::pin::Pin;
101

            
102
use internal_prelude::*;
103

            
104
// ---------- public exports ----------
105

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

            
122
pub use helpers::handle_rend_requests;
123

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
213
        Ok(())
214
    }
215
}
216

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
402
        let stream = svc.launch()?;
403
        Ok(Some((svc, stream)))
404
    }
405

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

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

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

            
449
        maybe_generate_hsid(&self.keymgr, &self.config.nickname, offline_hsid, selector)
450
    }
451

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

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

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

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

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

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

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

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

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

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

            
552
        Ok(rend_req_rx)
553
    }
554

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

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

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

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

            
600
4
    let hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
601

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

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

            
624
2
            (HsIdKey::from(&kp).id(), true)
625
        }
626
    };
627

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

            
642
4
    Ok(hsid)
643
4
}
644

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

            
659
298
    keymgr
660
298
        .get::<HsIdKey>(&hsid_spec)
661
298
        .ok()?
662
306
        .map(|hsid| hsid.id())
663
298
}
664

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

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

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

            
707
            if is_expired {
708
                expired_keys.push(entry.clone());
709
            }
710

            
711
            tor_keymgr::Result::Ok(())
712
        };
713

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

            
722
        append_if_expired!(BlindIdPublicKeySpecifier);
723
        append_if_expired!(BlindIdKeypairSpecifier);
724
        append_if_expired!(DescSigningKeypairSpecifier);
725
    }
726

            
727
    Ok(expired_keys)
728
}
729

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

            
748
    use std::fmt::Display;
749
    use std::path::Path;
750

            
751
    use fs_mistrust::Mistrust;
752
    use test_temp_dir::{TestTempDir, TestTempDirGuard, test_temp_dir};
753

            
754
    use tor_basic_utils::test_rng::testing_rng;
755
    use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder};
756
    use tor_llcrypto::pk::ed25519;
757
    use tor_persist::state_dir::InstanceStateHandle;
758

            
759
    use crate::config::OnionServiceConfigBuilder;
760
    use crate::ipt_set::IptSetStorageHandle;
761
    use crate::{HsIdKeypairSpecifier, HsIdPublicKeySpecifier};
762

            
763
    /// The nickname of the test service.
764
    const TEST_SVC_NICKNAME: &str = "test-svc";
765

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

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

            
782
            Arc::new(
783
                KeyMgrBuilder::default()
784
                    .primary_store(Box::new(keystore))
785
                    .build()
786
                    .unwrap(),
787
            )
788
        })
789
    }
790

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

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

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

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

            
828
            assert!($keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
829
            assert!($keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
830

            
831
            maybe_generate_hsid(&$keymgr, &nickname, $offline_hsid, Default::default()).unwrap();
832
        }};
833
    }
834

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

            
840
        let id_pub = HsIdKey::from(keypair.verifying_key());
841
        let id_keypair = HsIdKeypair::from(ed25519::ExpandedKeypair::from(&keypair));
842

            
843
        (id_keypair, id_pub)
844
    }
845

            
846
    #[test]
847
    fn generate_hsid() {
848
        let temp_dir = test_temp_dir!();
849
        let keymgr = create_keymgr(&temp_dir);
850

            
851
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
852
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
853

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

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

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

            
871
        keymgr
872
            .insert(
873
                existing_hsid_keypair,
874
                &hsid_spec,
875
                KeystoreSelector::Primary,
876
                true,
877
            )
878
            .unwrap();
879

            
880
        maybe_generate_hsid(
881
            &keymgr,
882
            &nickname,
883
            false, /* offline_hsid */
884
            Default::default(),
885
        )
886
        .unwrap();
887

            
888
        let keypair = keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().unwrap();
889
        let pk: HsIdKey = (&keypair).into();
890

            
891
        assert_eq!(pk.as_ref(), existing_hsid_public.as_ref());
892
    }
893

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

            
900
        let nickname = HsNickname::try_from(TEST_SVC_NICKNAME.to_string()).unwrap();
901
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
902
        let pub_hsid_spec = HsIdPublicKeySpecifier::new(nickname.clone());
903

            
904
        maybe_generate_hsid!(keymgr, true /* offline_hsid */);
905

            
906
        assert!(keymgr.get::<HsIdKey>(&pub_hsid_spec).unwrap().is_none());
907
        assert!(keymgr.get::<HsIdKeypair>(&hsid_spec).unwrap().is_none());
908
    }
909

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

            
918
        let keymgr = create_keymgr(&temp_dir);
919

            
920
        let (hsid_keypair, _hsid_public) = create_hsid();
921
        let (_hsid_keypair, hsid_public) = create_hsid();
922

            
923
        keymgr
924
            .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
925
            .unwrap();
926

            
927
        // Insert a mismatched public key
928
        keymgr
929
            .insert(hsid_public, &pub_hsid_spec, KeystoreSelector::Primary, true)
930
            .unwrap();
931

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

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

            
950
        let (hsid_keypair, hsid_public) = create_hsid();
951

            
952
        // Insert the hsid into the keystore
953
        keymgr
954
            .insert(hsid_keypair, &hsid_spec, KeystoreSelector::Primary, true)
955
            .unwrap();
956

            
957
        let config = OnionServiceConfigBuilder::default()
958
            .nickname(nickname)
959
            .build()
960
            .unwrap();
961

            
962
        let state_dir = StateDirectory::new(
963
            temp_dir.as_path_untracked(),
964
            &fs_mistrust::Mistrust::new_dangerously_trust_everyone(),
965
        )
966
        .unwrap();
967

            
968
        let service = OnionService::builder()
969
            .config(config)
970
            .keymgr(Arc::clone(&*keymgr))
971
            .state_dir(state_dir)
972
            .build()
973
            .unwrap();
974

            
975
        let hsid = HsId::from(hsid_public);
976
        assert_eq!(service.onion_address().unwrap(), hsid);
977

            
978
        drop(temp_dir); // prove that this is still live
979
    }
980
}