1use super::*;
4
5use tor_cell::relaycell::{
7 hs::intro_payload::{IntroduceHandshakePayload, OnionKey},
8 msg::{Introduce2, Rendezvous1},
9};
10use tor_circmgr::build::onion_circparams_from_netparams;
11use tor_linkspec::{decode::Strictness, verbatim::VerbatimLinkSpecCircTarget};
12use tor_proto::{
13 circuit::handshake::{
14 self,
15 hs_ntor::{self, HsNtorHkdfKeyGenerator},
16 },
17 stream::{IncomingStream, IncomingStreamRequestFilter},
18};
19
20#[derive(Debug, Clone, thiserror::Error)]
23#[allow(clippy::enum_variant_names)]
24#[non_exhaustive]
25pub enum IntroRequestError {
26 #[error("Introduction handshake was invalid")]
29 InvalidHandshake(#[source] tor_proto::Error),
30
31 #[error("Could not parse INTRODUCE2 payload")]
33 InvalidPayload(#[source] tor_bytes::Error),
34
35 #[error("Invalid link specifiers in INTRODUCE2 payload")]
37 InvalidLinkSpecs(#[source] tor_linkspec::decode::ChanTargetDecodeError),
38
39 #[error("Could not obtain subcredentials")]
41 Subcredentials(#[source] crate::FatalError),
42}
43
44impl HasKind for IntroRequestError {
45 fn kind(&self) -> tor_error::ErrorKind {
46 use tor_error::ErrorKind as EK;
47 use IntroRequestError as E;
48 match self {
49 E::InvalidHandshake(e) => e.kind(),
50 E::InvalidPayload(_) => EK::RemoteProtocolViolation,
51 E::InvalidLinkSpecs(_) => EK::RemoteProtocolViolation,
52 E::Subcredentials(e) => e.kind(),
53 }
54 }
55}
56
57#[derive(Debug, Clone, thiserror::Error)]
60#[non_exhaustive]
61pub enum EstablishSessionError {
62 #[error("Network directory not available")]
65 NetdirUnavailable(#[source] tor_netdir::Error),
66 #[error("Received an unsupported type of onion key")]
68 UnsupportedOnionKey,
69 #[error("Could not establish circuit to rendezvous point")]
71 RendCirc(#[source] RetryError<tor_circmgr::Error>),
72 #[error("Could not add virtual hop to circuit")]
74 VirtualHop(#[source] tor_proto::Error),
75 #[error("Could not configure circuit to allow BEGIN messages")]
78 AcceptBegins(#[source] tor_proto::Error),
79 #[error("Could not send RENDEZVOUS1 message")]
81 SendRendezvous(#[source] tor_proto::Error),
82 #[error("Impossible combination of identities for rendezvous point")]
87 ImpossibleIds(#[source] tor_netdir::RelayLookupError),
88 #[error("Internal error")]
90 Bug(#[from] tor_error::Bug),
91}
92
93impl HasKind for EstablishSessionError {
94 fn kind(&self) -> tor_error::ErrorKind {
95 use tor_error::ErrorKind as EK;
96 use EstablishSessionError as E;
97 match self {
98 E::NetdirUnavailable(e) => e.kind(),
99 E::UnsupportedOnionKey => EK::RemoteProtocolViolation,
100 EstablishSessionError::RendCirc(e) => {
101 tor_circmgr::Error::summarized_error_kind(e.sources())
102 }
103 EstablishSessionError::VirtualHop(e) => e.kind(),
104 EstablishSessionError::AcceptBegins(e) => e.kind(),
105 EstablishSessionError::SendRendezvous(e) => e.kind(),
106 EstablishSessionError::ImpossibleIds(_) => EK::RemoteProtocolViolation,
107 EstablishSessionError::Bug(e) => e.kind(),
108 }
109 }
110}
111
112#[derive(educe::Educe)]
121#[educe(Debug)]
122pub(crate) struct IntroRequest {
123 req: Introduce2,
126
127 #[educe(Debug(ignore))]
130 key_gen: HsNtorHkdfKeyGenerator,
131
132 rend1_msg: Rendezvous1,
136
137 intro_payload: IntroduceHandshakePayload,
139
140 chan_target: OwnedChanTargetBuilder,
143}
144
145pub(crate) struct OpenSession {
150 pub(crate) stream_requests: BoxStream<'static, IncomingStream>,
152
153 pub(crate) circuit: Arc<ClientCirc>,
158}
159
160#[async_trait]
165pub(crate) trait RendCircConnector: Send + Sync {
166 async fn get_or_launch_specific(
167 &self,
168 netdir: &tor_netdir::NetDir,
169 kind: HsCircKind,
170 target: VerbatimLinkSpecCircTarget<OwnedCircTarget>,
171 ) -> tor_circmgr::Result<Arc<ClientCirc>>;
172}
173
174#[async_trait]
175impl<R: Runtime> RendCircConnector for HsCircPool<R> {
176 async fn get_or_launch_specific(
177 &self,
178 netdir: &tor_netdir::NetDir,
179 kind: HsCircKind,
180 target: VerbatimLinkSpecCircTarget<OwnedCircTarget>,
181 ) -> tor_circmgr::Result<Arc<ClientCirc>> {
182 HsCircPool::get_or_launch_specific(self, netdir, kind, target).await
183 }
184}
185
186#[derive(Clone, Debug)]
188pub(crate) struct RequestFilter {
189 pub(crate) max_concurrent_streams: usize,
196}
197impl IncomingStreamRequestFilter for RequestFilter {
198 fn disposition(
199 &mut self,
200 _ctx: &tor_proto::stream::IncomingStreamRequestContext<'_>,
201 circ: &tor_proto::circuit::ClientCircSyncView<'_>,
202 ) -> tor_proto::Result<tor_proto::stream::IncomingStreamRequestDisposition> {
203 if circ.n_open_streams() >= self.max_concurrent_streams {
204 Ok(tor_proto::stream::IncomingStreamRequestDisposition::CloseCircuit)
207 } else {
208 Ok(tor_proto::stream::IncomingStreamRequestDisposition::Accept)
209 }
210 }
211}
212
213impl IntroRequest {
214 pub(crate) fn decrypt_from_introduce2(
216 req: Introduce2,
217 context: &RendRequestContext,
218 ) -> Result<Self, IntroRequestError> {
219 use IntroRequestError as E;
220 let mut rng = rand::rng();
221
222 let subcredentials = context
226 .compute_subcredentials()
227 .map_err(IntroRequestError::Subcredentials)?;
228
229 let (key_gen, rend1_body, msg_body) = hs_ntor::server_receive_intro(
230 &mut rng,
231 &context.kp_hss_ntor,
232 &context.kp_hs_ipt_sid,
233 &subcredentials[..],
234 req.encoded_header(),
235 req.encrypted_body(),
236 )
237 .map_err(E::InvalidHandshake)?;
238
239 let intro_payload: IntroduceHandshakePayload = {
240 let mut r = tor_bytes::Reader::from_slice(&msg_body);
241 r.extract().map_err(E::InvalidPayload)?
242 };
246
247 let chan_target = OwnedChanTargetBuilder::from_encoded_linkspecs(
250 Strictness::Standard,
251 intro_payload.link_specifiers(),
252 )
253 .map_err(E::InvalidLinkSpecs)?;
254
255 let rend1_msg = Rendezvous1::new(*intro_payload.cookie(), rend1_body);
256
257 Ok(IntroRequest {
258 req,
259 key_gen,
260 rend1_msg,
261 intro_payload,
262 chan_target,
263 })
264 }
265
266 pub(crate) async fn establish_session(
272 self,
273 filter: RequestFilter,
274 hs_pool: Arc<dyn RendCircConnector>,
275 provider: Arc<dyn NetDirProvider>,
276 ) -> Result<OpenSession, EstablishSessionError> {
277 use EstablishSessionError as E;
278
279 let netdir = provider
282 .netdir(tor_netdir::Timeliness::Timely)
283 .map_err(E::NetdirUnavailable)?;
284
285 let rend_point = {
288 let ntor_onion_key = match self.intro_payload.onion_key() {
290 OnionKey::NtorOnionKey(ntor_key) => ntor_key,
291 _ => return Err(E::UnsupportedOnionKey),
292 };
293 let mut bld = OwnedCircTarget::builder();
294 *bld.chan_target() = self.chan_target;
295
296 let protocols = {
300 let chan_target = bld.chan_target().build().map_err(into_internal!(
301 "from_encoded_linkspecs gave an invalid output"
302 ))?;
303 match netdir
304 .by_ids_detailed(&chan_target)
305 .map_err(E::ImpossibleIds)?
306 {
307 Some(relay) => relay.protovers().clone(),
308 None => netdir.relay_protocol_status().required_protocols().clone(),
309 }
310 };
311 bld.protocols(protocols);
312 bld.ntor_onion_key(*ntor_onion_key);
313 VerbatimLinkSpecCircTarget::new(
314 bld.build()
315 .map_err(into_internal!("Failed to construct a valid circtarget"))?,
316 self.intro_payload.link_specifiers().into(),
317 )
318 };
319
320 let max_n_attempts = netdir.params().hs_service_rendezvous_failures_max;
321 let mut circuit = None;
322 let mut retry_err: RetryError<tor_circmgr::Error> =
323 RetryError::in_attempt_to("Establish a circuit to a rendezvous point");
324
325 for _attempt in 1..=max_n_attempts.into() {
327 match hs_pool
328 .get_or_launch_specific(&netdir, HsCircKind::SvcRend, rend_point.clone())
329 .await
330 {
331 Ok(circ) => {
332 circuit = Some(circ);
333 break;
334 }
335 Err(e) => {
336 retry_err.push(e);
337 }
341 }
342 }
343 let circuit = circuit.ok_or_else(|| E::RendCirc(retry_err))?;
344
345 let params = onion_circparams_from_netparams(netdir.params())
347 .map_err(into_internal!("Unable to build CircParameters"))?;
348
349 drop(netdir);
351
352 let last_real_hop = circuit
353 .last_hop_num()
354 .map_err(into_internal!("Circuit with no final hop"))?;
355
356 circuit
358 .extend_virtual(
359 handshake::RelayProtocol::HsV3,
360 handshake::HandshakeRole::Responder,
361 self.key_gen,
362 params,
363 )
364 .await
365 .map_err(E::VirtualHop)?;
366
367 let virtual_hop = circuit
368 .last_hop_num()
369 .map_err(into_internal!("Circuit with no virtual hop"))?;
370
371 let stream_requests = circuit
373 .allow_stream_requests(&[tor_cell::relaycell::RelayCmd::BEGIN], virtual_hop, filter)
374 .await
375 .map_err(E::AcceptBegins)?
376 .boxed();
377
378 circuit
380 .send_raw_msg(self.rend1_msg.into(), last_real_hop)
381 .await
382 .map_err(E::SendRendezvous)?;
383
384 Ok(OpenSession {
385 stream_requests,
386 circuit,
387 })
388 }
389}