1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
|
//! Principal types for onion services.
mod netdir;
use std::sync::{Arc, Mutex};
use futures::channel::mpsc;
use futures::channel::oneshot;
use futures::Stream;
use tor_circmgr::hspool::HsCircPool;
use tor_config::ReconfigureError;
use tor_error::Bug;
use tor_keymgr::KeyMgr;
use tor_llcrypto::pk::curve25519;
use tor_netdir::NetDirProvider;
use tor_rtcompat::Runtime;
use crate::ipt_mgr::IptManager;
use crate::ipt_set::IptsManagerView;
use crate::svc::publish::Publisher;
use crate::OnionServiceConfig;
use crate::OnionServiceStatus;
use crate::RendRequest;
use crate::StartupError;
pub(crate) mod ipt_establish;
pub(crate) mod publish;
pub(crate) mod rend_handshake;
/// Convenience alias for link specifiers of an intro point
pub(crate) type LinkSpecs = Vec<tor_linkspec::EncodedLinkSpec>;
/// Convenient type alias for an ntor public key
// TODO HSS maybe this should be `tor_proto::crypto::handshake::ntor::NtorPublicKey`?
type NtorPublicKey = curve25519::PublicKey;
/// A handle to an instance of an onion service.
//
// TODO HSS: Write more.
//
// (APIs should return Arc<OnionService>)
pub struct OnionService {
/// The mutable implementation details of this onion service.
inner: Mutex<SvcInner>,
}
/// Implementation details for an onion service.
struct SvcInner {
/// Configuration information about this service.
config_tx: postage::watch::Sender<Arc<OnionServiceConfig>>,
/// A keymgr used to look up our keys and store new medium-term keys.
//
// TODO HSS: Do we actually need this in this structure?
keymgr: Arc<KeyMgr>,
/// A oneshot that will be dropped when this object is dropped.
shutdown_tx: oneshot::Sender<void::Void>,
/// Handles that we'll take ownership of when launching the service.
///
/// (TODO HSS: Having to consume this may indicate a design problem.)
unlaunched: Option<(
mpsc::Receiver<RendRequest>,
Box<dyn Launchable + Send + Sync>,
)>,
}
/// Objects and handles needed to launch an onion service.
struct ForLaunch<R: Runtime> {
/// An unlaunched handle for the HsDesc publisher.
///
/// This publisher is responsible for determining when we need to upload a
/// new set of HsDescs, building them, and publishing them at the correct
/// HsDirs.
publisher: Publisher<R>,
/// Our handler for the introduction point manager.
///
/// This manager is responsible for selecting introduction points,
/// maintaining our connections to them, and telling the publisher which ones
/// are publicly available.
ipt_mgr: IptManager<R, crate::ipt_mgr::Real<R>>,
/// A handle used by the ipt manager to send Ipts to the publisher.
///
///
ipt_mgr_view: IptsManagerView,
}
/// Private trait used to type-erase `ForLaunch<R>`, so that we don't need to
/// parameterize OnionService on `<R>`.
trait Launchable: Send + Sync {
/// Launch
fn launch(self: Box<Self>) -> Result<(), StartupError>;
}
impl<R: Runtime> Launchable for ForLaunch<R> {
fn launch(self: Box<Self>) -> Result<(), StartupError> {
self.ipt_mgr.launch_background_tasks(self.ipt_mgr_view)?;
self.publisher.launch()?;
Ok(())
}
}
impl OnionService {
/// Create (but do not launch) a new onion service.
pub fn new<R, S>(
runtime: R,
config: OnionServiceConfig,
netdir_provider: Arc<dyn NetDirProvider>,
circ_pool: Arc<HsCircPool<R>>,
keymgr: Arc<KeyMgr>,
statemgr: S,
) -> Result<Arc<Self>, StartupError>
where
R: Runtime,
S: tor_persist::StateMgr + Send + Sync + 'static,
{
let nickname = config.name.clone();
// TODO HSS: Maybe, adjust tor_persist::fs to handle subdirectories, and
// use onion/{nickname}?
let storage_key = format!("onion_svc_{nickname}");
// TODO HSS-IPT-PERSIST: Use this handle, and use a real struct type instead.
let storage_handle: Arc<dyn tor_persist::StorageHandle<()>> =
statemgr.create_handle(storage_key);
let (rend_req_tx, rend_req_rx) = mpsc::channel(32);
let (shutdown_tx, shutdown_rx) = oneshot::channel();
let (config_tx, config_rx) = postage::watch::channel_with(Arc::new(config));
// TODO HSS: How do I give ipt_mgr_view to ipt_mgr? Does IptManager even take
// one of these?
let (ipt_mgr_view, publisher_view) = crate::ipt_set::ipts_channel(None);
let ipt_mgr = IptManager::new(
runtime.clone(),
netdir_provider.clone(),
nickname,
config_rx.clone(),
rend_req_tx,
shutdown_rx,
crate::ipt_mgr::Real {
circ_pool: circ_pool.clone(),
},
)?;
let hs_id = {
// TODO HSS BLOCKER: Look up HsId by KeyMgr based on nickname. This
// is just a placeholder so the function will compile.
tor_hscrypto::pk::HsId::from([0xff; 32])
};
// TODO HSS Why does this not need a keymgr?
let publisher = Publisher::new(
runtime,
hs_id,
netdir_provider,
circ_pool,
publisher_view,
config_rx,
Arc::clone(&keymgr),
);
// TODO HSS: we need to actually do something with: shutdown_tx,
// rend_req_rx. The latter may need to be refactored to actually work
// with svc::rend_handshake, if it doesn't already.
Ok(Arc::new(OnionService {
inner: Mutex::new(SvcInner {
config_tx,
shutdown_tx,
keymgr,
unlaunched: Some((
rend_req_rx,
Box::new(ForLaunch {
publisher,
ipt_mgr,
ipt_mgr_view,
}),
)),
}),
}))
}
/// Change the configuration of this onion service.
///
/// (Not everything can be changed here. At the very least we'll need to say
/// that the identity of a service is fixed. We might want to make the
/// storage backing this, and the anonymity status, unchangeable.)
pub fn reconfigure(&self, new_config: ()) -> Result<(), ReconfigureError> {
todo!() // TODO hss
}
/// Tell this onion service about some new short-term keys it can use.
pub fn add_keys(&self, keys: ()) -> Result<(), Bug> {
todo!() // TODO hss
}
/// Return the current status of this onion service.
pub fn status(&self) -> OnionServiceStatus {
todo!() // TODO hss
}
// TODO hss let's also have a function that gives you a stream of Status
// changes? Or use a publish-based watcher?
/// Tell this onion service to begin running, and return a
/// stream of rendezvous requests on the service.
///
/// You can turn the resulting stream into a stream of [`StreamRequest`](crate::StreamRequest)
/// using the [`handle_rend_requests`](crate::handle_rend_requests) helper function
pub fn launch(self: &Arc<Self>) -> Result<impl Stream<Item = RendRequest>, StartupError> {
let (rend_req_rx, launch) = {
let mut inner = self.inner.lock().expect("poisoned lock");
inner
.unlaunched
.take()
.ok_or(StartupError::AlreadyLaunched)?
};
launch.launch()?;
// TODO HSS: This needs to launch at least the following tasks:
//
// - If we decide to use separate disk-based key provisioning, a task to
// monitor our keys directory.
// - If we own our identity key, a task to generate per-period sub-keys as
// needed.
Ok(rend_req_rx)
}
/// Tell this onion service to stop running.
///
/// It can be restarted with launch().
///
/// You can also shut down an onion service completely by dropping the last
/// Clone of it.
pub fn stop(&self) {
todo!() // TODO hss
}
}
|