1
//! Handler for CREATE* cells.
2

            
3
use crate::FlowCtrlParameters;
4
use crate::ccparams::{
5
    AlgorithmDiscriminants, CongestionWindowParams, FixedWindowParams, RoundTripEstimatorParams,
6
    VegasParams,
7
};
8
use crate::channel::Channel;
9
use crate::circuit::celltypes::{CreateRequest, CreateResponse};
10
use crate::circuit::circhop::{HandshakeParamsError, HopSettings};
11
use crate::circuit::{
12
    CircuitRxSender, HandshakeSubprotocols, InvalidHandshakeSubprotocolError, UniqId,
13
};
14
use crate::client::circuit::padding::PaddingController;
15
use crate::crypto::binding::CircuitBinding;
16
use crate::crypto::cell::CryptInit as _;
17
use crate::crypto::cell::{
18
    CgoRelayCrypto, InboundRelayLayer, OutboundRelayLayer, RelayLayer, Tor1RelayCrypto,
19
};
20
use crate::crypto::handshake::RelayHandshakeError;
21
use crate::crypto::handshake::ServerHandshake as _;
22
use crate::crypto::handshake::fast::CreateFastServer;
23
use crate::crypto::handshake::ntor::{NtorSecretKey, NtorServer};
24
use crate::crypto::handshake::ntor_v3::{NtorV3SecretKey, NtorV3Server};
25
use crate::memquota::SpecificAccount as _;
26
use crate::memquota::{ChannelAccount, CircuitAccount};
27
use crate::relay::channel_provider::ChannelProvider;
28
use crate::relay::reactor::Reactor;
29
use crate::relay::{IncomingStreamRequestFilter, RelayCirc};
30
use crate::stream::IncomingStream;
31
use futures::channel::mpsc;
32
use futures::{SinkExt, Stream};
33
use smallvec::SmallVec;
34
use std::sync::{Arc, RwLock, Weak};
35
use tor_cell::chancell::ChanMsg as _;
36
use tor_cell::chancell::CircId;
37
use tor_cell::chancell::msg::{
38
    CreateFast, Created2, CreatedFast, Destroy, DestroyReason, HandshakeType,
39
};
40
use tor_cell::relaycell::RelayCmd;
41
use tor_cell::relaycell::extend::{
42
    CcRequest, CcResponse, CircRequestExt, CircResponseExt, SubprotocolRequest,
43
};
44
use tor_error::{ErrorKind, HasKind, debug_report, internal, into_internal, warn_report};
45
use tor_linkspec::OwnedChanTarget;
46
use tor_llcrypto::pk::ed25519::Ed25519Identity;
47
use tor_llcrypto::pk::rsa::RsaIdentity;
48
use tor_memquota::mq_queue::ChannelSpec as _;
49
use tor_memquota::mq_queue::MpscSpec;
50
use tor_relay_crypto::pk::{RelayNtorKeypair, RelayNtorKeys};
51
use tor_rtcompat::SpawnExt as _;
52
use tor_rtcompat::{DynTimeProvider, Runtime};
53
use tracing::{debug, trace};
54

            
55
/// Everything needed to handle CREATE* messages on channels.
56
#[derive(derive_more::Debug)]
57
pub struct CreateRequestHandler {
58
    /// Something that can launch channels. Typically the `ChanMgr`.
59
    chan_provider: Weak<dyn ChannelProvider<BuildSpec = OwnedChanTarget> + Send + Sync>,
60
    /// Circuit-related network parameters.
61
    circ_net_params: RwLock<CircNetParameters>,
62
    /// The circuit extension keys.
63
    #[debug(skip)]
64
    ntor_keys: RwLock<RelayNtorKeys>,
65
    /// An [`IncomingStreamRequestFilter`] factory for checking whether the user wants
66
    /// this request, or wants to reject it immediately.
67
    ///
68
    /// Used for obtaining a current [`IncomingStreamRequestFilter`]
69
    /// for building a circuit reactor.
70
    //
71
    // TODO(relay): it's likely this will end up changing quite a bit once we start
72
    // figuring out exactly how the config/reconfigure() logic and IncomingStreamRequestFilter
73
    // should function for relays.
74
    #[debug(skip)]
75
    incoming_filter_factory: Box<dyn IncomingStreamRequestFilterFactory + Send + Sync>,
76
    /// The allowed incoming stream commands.
77
    ///
78
    /// Used for rejecting BEGIN and RESOLVE if we are not configured to be an exit.
79
    ///
80
    // TODO(relay): we might use this for rejecting BEGIN_DIR too,
81
    // if we decide to allow relays to opt out of being dir mirrors.
82
    // See https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/4107/diffs#note_3426447
83
    allowed_stream_cmds: SmallVec<[RelayCmd; 3]>,
84
    /// A sender for the [`Stream`]s of `IncomingStream` of all circuits.
85
    ///
86
    /// The receiver will receive one [`Stream`] (of tor streams) per circuit.
87
    ///
88
    /// This being a bounded MPSC might seem a bit risky, because in theory,
89
    /// if the receiver is not reading fast enough, sending will block.
90
    /// In practice, however, it should never block (or buffer very much at all,
91
    /// for that matter), because the user (arti-relay) is expected to read from
92
    /// this in a tight loop, and spawn a task for handling each [`Stream`].
93
    ///
94
    /// Note: because this MPSC is not associated with any particular circuit or channel,
95
    /// it does not participate in the memquota system (see [crate::memquota]).
96
    #[debug(skip)]
97
    circuit_stream_tx: mpsc::Sender<Box<dyn Stream<Item = IncomingStream> + Send + Sync + Unpin>>,
98
}
99

            
100
// We make the CREATE-handling methods of `CreateRequestHandler` async
101
// since we expect that in the future we may want to offload the crypto to a worker thread.
102
#[expect(clippy::unused_async)]
103
impl CreateRequestHandler {
104
    /// Build a new [`CreateRequestHandler`], and a [`CircuitIncomingStreamReceiver`]
105
    /// for receiving new streams that are opened on any incoming circuits.
106
86
    pub fn new(
107
86
        chan_provider: Weak<dyn ChannelProvider<BuildSpec = OwnedChanTarget> + Send + Sync>,
108
86
        circ_net_params: CircNetParameters,
109
86
        ntor_keys: RelayNtorKeys,
110
86
        incoming_filter_factory: Box<dyn IncomingStreamRequestFilterFactory + Send + Sync>,
111
86
        allowed_stream_cmds: &[RelayCmd],
112
86
    ) -> (Self, CircuitIncomingStreamReceiver) {
113
        // TODO(relay-tuning): this MPSC can be a bottleneck,
114
        // as all the channels on this relay will want to send one item on it
115
        // each time a new circuit is created.
116
        //
117
        // The value set here is a guesstimate.
118
        const CIRC_STREAM_BUF_SIZE: usize = 1024;
119

            
120
        // This is not associated with any particular circuit
121
        // (it is for *all* circuits), so it doesn't participate in memquota
122
        // (see circuit_stream_tx docs)
123
        #[allow(clippy::disallowed_methods)]
124
86
        let (stream_tx, stream_rx) = mpsc::channel(CIRC_STREAM_BUF_SIZE);
125

            
126
86
        let handler = Self {
127
86
            chan_provider,
128
86
            circ_net_params: RwLock::new(circ_net_params),
129
86
            ntor_keys: RwLock::new(ntor_keys),
130
86
            incoming_filter_factory,
131
86
            allowed_stream_cmds: allowed_stream_cmds.into(),
132
86
            circuit_stream_tx: stream_tx,
133
86
        };
134

            
135
86
        let circuit_stream_rx = CircuitIncomingStreamReceiver {
136
86
            circuit_stream_rx: stream_rx,
137
86
        };
138

            
139
86
        (handler, circuit_stream_rx)
140
86
    }
141

            
142
    /// Update the circuit parameters from a network consensus.
143
    pub fn update_params(&self, circ_net_params: CircNetParameters) {
144
        *self.circ_net_params.write().expect("rwlock poisoned") = circ_net_params;
145
    }
146

            
147
    /// Update the handler with a new set of circuit extension keys.
148
    ///
149
    /// This is called periodically by the relay key rotation task.
150
    pub fn update_ntor_keys(&self, ntor_keys: RelayNtorKeys) {
151
        *self.ntor_keys.write().expect("rwlock poisoned") = ntor_keys;
152
    }
153

            
154
    /// Handle a CREATE* cell.
155
    ///
156
    /// This intentionally does not return a [`crate::Error`] so that we don't accidentally shut
157
    /// down the channel reactor when we really should be returning a DESTROY. Shutting down a
158
    /// channel may cause us to leak information about paths of circuits travelling through this
159
    /// relay. This is especially important here since we're handling data that is controllable from
160
    /// the other end of the circuit.
161
    #[allow(clippy::too_many_arguments)]
162
30
    pub(crate) async fn handle_create<R: Runtime>(
163
30
        &self,
164
30
        runtime: &R,
165
30
        channel: &Arc<Channel>,
166
30
        our_ed25519_id: &Ed25519Identity,
167
30
        our_rsa_id: &RsaIdentity,
168
30
        circ_id: CircId,
169
30
        msg: &CreateRequest,
170
30
        memquota: &ChannelAccount,
171
30
        circ_unique_id: UniqId,
172
30
    ) -> Result<(CreateResponse, RelayCircComponents), Destroy> {
173
30
        let result = self
174
30
            .handle_create_inner(
175
30
                runtime,
176
30
                channel,
177
30
                our_ed25519_id,
178
30
                our_rsa_id,
179
30
                circ_id,
180
30
                msg,
181
30
                memquota,
182
30
                circ_unique_id,
183
30
            )
184
30
            .await;
185

            
186
30
        match result {
187
16
            Ok(x) => Ok(x),
188
14
            Err(e) => {
189
                // TODO(relay): The log messages throughout could be very noisy, so should have rate limiting.
190
14
                let cmd = msg.cmd();
191
14
                debug_report!(&e, %cmd, "Failed to handle circuit create request");
192

            
193
                // `tor-spec/tearing-down-circuits.md`:
194
                //
195
                // > Implementations SHOULD always use the NONE reason to avoid side channels: [...]
196
14
                Err(Destroy::new(DestroyReason::NONE))
197
            }
198
        }
199
30
    }
200

            
201
    /// See [`Self::handle_create`].
202
    #[allow(clippy::too_many_arguments)]
203
30
    async fn handle_create_inner<R: Runtime>(
204
30
        &self,
205
30
        runtime: &R,
206
30
        channel: &Arc<Channel>,
207
30
        our_ed25519_id: &Ed25519Identity,
208
30
        our_rsa_id: &RsaIdentity,
209
30
        circ_id: CircId,
210
30
        msg: &CreateRequest,
211
30
        memquota: &ChannelAccount,
212
30
        circ_unique_id: UniqId,
213
30
    ) -> Result<(CreateResponse, RelayCircComponents), HandleCreateError> {
214
        // Perform the handshake crypto and build the response.
215
30
        let handshake_components = match msg {
216
4
            CreateRequest::CreateFast(msg) => self.handle_create_fast(msg).await?,
217
26
            CreateRequest::Create2(msg) => match msg.handshake_type() {
218
                HandshakeType::NTOR_V3 => {
219
12
                    self.handle_create2_ntorv3(msg.body(), our_ed25519_id)
220
12
                        .await?
221
                }
222
10
                HandshakeType::NTOR => self.handle_create2_ntor(msg.body(), our_rsa_id).await?,
223
4
                x @ HandshakeType::TAP | x => {
224
4
                    return Err(HandleCreateError::Create2HandshakeType(x));
225
                }
226
            },
227
        };
228

            
229
16
        let memquota = CircuitAccount::new(memquota)?;
230

            
231
        // We use a large mpsc queue here since a circuit should never block the channel,
232
        // and we hope that memquota will help us if an attacker intentionally fills this buffer.
233
        // We use `10_000_000` since `usize::MAX` causes `futures::channel::mpsc` to panic.
234
        // TODO(relay): We should switch to an unbounded queue, but the circuit reactor is expecting
235
        // a bounded queue.
236
16
        let time_provider = DynTimeProvider::new(runtime.clone());
237
16
        let account = memquota.as_raw_account();
238
16
        let (sender, receiver) =
239
16
            MpscSpec::new(10_000_000).new_mq(time_provider.clone(), account)?;
240
16
        let (sender, receiver) = crate::circuit::circ_sender::channel(sender, receiver);
241

            
242
        // TODO(relay): Do we really want a client padding machine here?
243
16
        let (padding_ctrl, padding_stream) =
244
16
            crate::client::circuit::padding::new_padding(DynTimeProvider::new(runtime.clone()));
245

            
246
        // Upgrade the channel provider, which in practice is the `ChanMgr` so this should not fail.
247
16
        let Some(chan_provider) = self.chan_provider.upgrade() else {
248
            return Err(internal!("Unable to upgrade weak `ChannelProvider`").into());
249
        };
250

            
251
        // Create an IncomingStreamRequestFilter for this circuit.
252
        // This will get applied to every stream request (BEGIN, BEGIN_DIR, RESOLVE)
253
        // arriving on the circuit.
254
        //
255
        // Note: once built, a circuit reactor's IncomingStreamRequestFilter cannot be changed
256
        // (it's fixed for the entire duration of the circuit).
257
16
        let incoming_filter = self.incoming_filter_factory.current_filter();
258

            
259
        // Build the relay circuit reactor.
260
16
        let (reactor, circ, incoming_streams) = Reactor::new(
261
16
            runtime.clone(),
262
16
            channel,
263
16
            circ_id,
264
16
            circ_unique_id,
265
16
            receiver,
266
16
            handshake_components.crypto_in,
267
16
            handshake_components.crypto_out,
268
16
            &handshake_components.hop_settings,
269
16
            chan_provider,
270
16
            padding_ctrl.clone(),
271
16
            padding_stream,
272
16
            incoming_filter,
273
16
            &self.allowed_stream_cmds,
274
16
            &memquota,
275
        )
276
16
        .map_err(into_internal!("Failed to start circuit reactor"))?;
277

            
278
16
        let mut circuit_stream_tx = self.circuit_stream_tx.clone();
279
        // Start the reactor in a task.
280
16
        let () = runtime.spawn(async move {
281
16
            if let Err(e) = circuit_stream_tx.send(Box::new(incoming_streams)).await {
282
                warn_report!(e, "IncomingStream handler disappeared?!");
283
                // If we get here, it means the relay stream handler task has gone away,
284
                // so there won't be anything handling the incoming streams.
285
                //
286
                // The reactor is dropped, making the RelayCirc returned below
287
                // in the RelayCircComponents unusable
288
                // (RelayCirc::is_closing() will return `true`).
289
                drop(reactor);
290
            } else {
291
                // Only spawn the circuit reactor if the incoming stream handler was
292
                // able to receive our message
293
16
                match reactor.run().await {
294
6
                    Ok(()) => {}
295
                    Err(e) => {
296
                        debug_report!(e, "Relay circuit reactor exited with an error");
297
                    }
298
                }
299
            }
300
6
        })?;
301

            
302
16
        Ok((
303
16
            handshake_components.response,
304
16
            RelayCircComponents {
305
16
                circ,
306
16
                sender,
307
16
                padding_ctrl,
308
16
            },
309
16
        ))
310
30
    }
311

            
312
    /// The handshake code for a CREATE_FAST request.
313
4
    async fn handle_create_fast(
314
4
        &self,
315
4
        msg: &CreateFast,
316
6
    ) -> Result<CompletedHandshakeComponents, HandleCreateError> {
317
        // TODO(relay): We might want to offload this to a CPU worker in the future.
318
4
        let (keygen, handshake_msg) = CreateFastServer::server(
319
4
            &mut rand::rng(),
320
            // The CREATE_FAST handshake doesn't accept or return extensions,
321
            // so this `AuxDataReply` is a no-op.
322
4
            &mut |_: &()| Some(()),
323
            // The CREATE_FAST handshake doesn't use any keys.
324
4
            &[],
325
4
            msg.handshake(),
326
        )?;
327

            
328
4
        let circ_net_params = self
329
4
            .circ_net_params
330
4
            .read()
331
4
            .expect("rwlock poisoned")
332
4
            .clone();
333

            
334
        // No subprotocols are requested during a CREATE_FAST handshake.
335
4
        let subprotos = HandshakeSubprotocols::default();
336

            
337
4
        let hop_settings = HopSettings::from_handshake_params(
338
4
            circ_net_params,
339
            // CREATE_FAST always uses fixed-window flow control.
340
4
            AlgorithmDiscriminants::FixedWindow,
341
4
            subprotos,
342
        )?;
343

            
344
4
        let crypt = Tor1RelayCrypto::construct(keygen)
345
4
            .map_err(into_internal!("Circuit crypt state construction failed"))?;
346

            
347
4
        let (crypto_out, crypto_in, _binding) = split_relay_layer(crypt);
348

            
349
4
        let response = CreatedFast::new(handshake_msg);
350
4
        let response = CreateResponse::CreatedFast(response);
351

            
352
4
        trace!("Completed CREATE_FAST handshake");
353

            
354
4
        Ok(CompletedHandshakeComponents {
355
4
            response,
356
4
            hop_settings,
357
4
            crypto_out,
358
4
            crypto_in,
359
4
        })
360
4
    }
361

            
362
    /// The handshake code for a CREATE2 ntor (non-v3) request.
363
10
    async fn handle_create2_ntor(
364
10
        &self,
365
10
        msg_body: &[u8],
366
10
        our_rsa_id: &RsaIdentity,
367
15
    ) -> Result<CompletedHandshakeComponents, HandleCreateError> {
368
10
        let ntor_keys = self.ntor_keys(|k| {
369
10
            NtorSecretKey::new(k.secret().clone(), *k.public().inner(), *our_rsa_id)
370
10
        });
371

            
372
        // TODO(relay): We might want to offload this to a CPU worker in the future.
373
10
        let (keygen, handshake_msg) = NtorServer::server(
374
10
            &mut rand::rng(),
375
            // The ntor (non-v3) handshake doesn't accept or return extensions,
376
            // so this `AuxDataReply` is a no-op.
377
10
            &mut |_: &()| Some(()),
378
10
            ntor_keys.as_ref(),
379
10
            msg_body,
380
4
        )?;
381

            
382
6
        let circ_net_params = self
383
6
            .circ_net_params
384
6
            .read()
385
6
            .expect("rwlock poisoned")
386
6
            .clone();
387

            
388
        // No subprotocols are requested during an ntor (non-v3) handshake.
389
6
        let subprotos = HandshakeSubprotocols::default();
390

            
391
6
        let hop_settings = HopSettings::from_handshake_params(
392
6
            circ_net_params,
393
            // CREATE2 with ntor (non-v3) always uses fixed-window flow control.
394
6
            AlgorithmDiscriminants::FixedWindow,
395
6
            subprotos,
396
        )?;
397

            
398
6
        let crypt = Tor1RelayCrypto::construct(keygen)
399
6
            .map_err(into_internal!("Circuit crypt state construction failed"))?;
400

            
401
6
        let (crypto_out, crypto_in, _binding) = split_relay_layer(crypt);
402

            
403
6
        let response = Created2::new(handshake_msg);
404
6
        let response = CreateResponse::Created2(response);
405

            
406
6
        trace!("Completed ntor handshake");
407

            
408
6
        Ok(CompletedHandshakeComponents {
409
6
            response,
410
6
            hop_settings,
411
6
            crypto_out,
412
6
            crypto_in,
413
6
        })
414
10
    }
415

            
416
    /// The handshake code for a CREATE2 ntor-v3 request.
417
12
    async fn handle_create2_ntorv3(
418
12
        &self,
419
12
        msg_body: &[u8],
420
12
        our_ed25519_id: &Ed25519Identity,
421
18
    ) -> Result<CompletedHandshakeComponents, HandleCreateError> {
422
12
        let ntor_keys = self.ntor_keys(|k| {
423
12
            NtorV3SecretKey::new(k.secret().clone(), *k.public().inner(), *our_ed25519_id)
424
12
        });
425

            
426
12
        let circ_net_params = self
427
12
            .circ_net_params
428
12
            .read()
429
12
            .expect("rwlock poisoned")
430
12
            .clone();
431

            
432
        // These extensions can be negotiated during the handshake.
433
12
        let mut cc_algorithm = AlgorithmDiscriminants::FixedWindow;
434

            
435
        // These subprotocols were requested during the handshake.
436
        // They are not validated.
437
12
        let mut subprotos = SubprotocolRequest::default();
438

            
439
        // Helper which processes extension requests and returns any responses.
440
        // Returns `None` if the handshake should fail.
441
12
        let mut ext_reply_fn = |client_exts: &[CircRequestExt]| {
442
6
            let mut response_exts = Vec::new();
443

            
444
            // https://spec.torproject.org/tor-spec/create-created-cells.html#additional-data
445
            //
446
            // > Unless otherwise specified in the documentation for an extension type:
447
            // > - [...]
448
            // > - Parties MUST ignore any occurrence of an extension with a given type after the first such occurrence.
449
            //
450
            // TODO: Is there something nicer that we can do here?
451
            // We could use accessors like `ExtList::get_cc_request()`
452
            // which iterate over the extension list for each extension,
453
            // but using an enum match like we do below is kind of nice.
454
6
            let mut handled_cc_request = false;
455
6
            let mut handled_subproto_request = false;
456

            
457
6
            for ext in client_exts {
458
                match ext {
459
                    CircRequestExt::CcRequest(CcRequest { .. }) => {
460
                        if handled_cc_request {
461
                            continue;
462
                        }
463
                        handled_cc_request = true;
464

            
465
                        cc_algorithm = AlgorithmDiscriminants::Vegas;
466

            
467
                        let sendme_inc: u8 = circ_net_params.cc.cwnd.sendme_inc();
468
                        let response = CcResponse::new(sendme_inc);
469
                        response_exts.push(CircResponseExt::CcResponse(response));
470
                    }
471
                    // The given `SubprotocolRequest` stores a list of `NumberedSubver`,
472
                    // but a circuit extension request is limited to 255 bytes (127 subprotocols).
473
                    // So while a malicious client could send us a lot of invalid subprotocols,
474
                    // this limit prevents this list from being excessively large.
475
                    CircRequestExt::SubprotocolRequest(subproto_request) => {
476
                        if handled_subproto_request {
477
                            continue;
478
                        }
479
                        handled_subproto_request = true;
480

            
481
                        // We don't check the requested subprotocols here.
482
                        subprotos = subproto_request.clone();
483
                    }
484
                    CircRequestExt::Unrecognized(ext) => {
485
                        // https://spec.torproject.org/tor-spec/create-created-cells.html#additional-data
486
                        //
487
                        // > Parties MUST ignore extensions with `EXT_FIELD_TYPE` bodies they do not recognize.
488
                        debug!(
489
                            ?ext,
490
                            "CREATE2 ntor-v3 handshake requested unrecognized extension",
491
                        );
492
                    }
493
                    ext => {
494
                        // https://spec.torproject.org/tor-spec/create-created-cells.html#additional-data
495
                        //
496
                        // > Parties MUST ignore extensions with `EXT_FIELD_TYPE` bodies they do not recognize.
497
                        //
498
                        // We recognize this but don't know what to do with it.
499
                        // We haven't implemented it, or it doesn't make sense
500
                        // (for example `CircRequestExt::ProofOfWork`).
501
                        // So we'll just behave as if we don't recognize it.
502
                        debug!(
503
                            ?ext,
504
                            "CREATE2 ntor-v3 handshake requested unsupported extension",
505
                        );
506
                    }
507
                }
508
            }
509

            
510
6
            Some(response_exts)
511
6
        };
512

            
513
        // TODO(relay): We might want to offload this to a CPU worker in the future.
514
12
        let (keygen, handshake_msg) = NtorV3Server::server(
515
12
            &mut rand::rng(),
516
12
            &mut ext_reply_fn,
517
12
            ntor_keys.as_ref(),
518
12
            msg_body,
519
6
        )?;
520

            
521
        // Ensure that the client did not request invalid/unsupported subprotocols.
522
6
        let subprotos = HandshakeSubprotocols::try_from_request(subprotos)?;
523

            
524
6
        let hop_settings =
525
6
            HopSettings::from_handshake_params(circ_net_params, cc_algorithm, subprotos)?;
526

            
527
6
        let (crypto_out, crypto_in, _binding) = if subprotos.relay_crypt_cgo {
528
            let crypt = CgoRelayCrypto::construct(keygen)
529
                .map_err(into_internal!("Circuit crypt state construction failed"))?;
530
            split_relay_layer(crypt)
531
        } else {
532
6
            let crypt = Tor1RelayCrypto::construct(keygen)
533
6
                .map_err(into_internal!("Circuit crypt state construction failed"))?;
534
6
            split_relay_layer(crypt)
535
        };
536

            
537
6
        let response = Created2::new(handshake_msg);
538
6
        let response = CreateResponse::Created2(response);
539

            
540
6
        trace!(?cc_algorithm, ?subprotos, "Completed ntor-v3 handshake");
541

            
542
6
        Ok(CompletedHandshakeComponents {
543
6
            response,
544
6
            hop_settings,
545
6
            crypto_out,
546
6
            crypto_in,
547
6
        })
548
12
    }
549

            
550
    /// Helper to get the ntor keypairs after some transformation `map`.
551
    ///
552
    /// The `map` transformation must be fast since it blocks a read lock.
553
    /// The returned keys are sorted with the most recent key first.
554
    ///
555
    /// It would be nice if this just returned an iterator,
556
    /// but the read lock prevents this.
557
22
    fn ntor_keys<T>(&self, map: impl FnMut(&RelayNtorKeypair) -> T) -> impl AsRef<[T]> {
558
22
        let ntor_keys = self.ntor_keys.read().expect("rwlock poisoned");
559
22
        let ntor_keys = [Some(ntor_keys.latest()), ntor_keys.previous()];
560
22
        ntor_keys
561
22
            .into_iter()
562
22
            .flatten()
563
22
            .map(map)
564
22
            .collect::<SmallVec<[T; 2]>>()
565
22
    }
566
}
567

            
568
/// A receiver of [`Stream`]s (one for each incoming circuit),
569
/// where each `Stream` produces [`IncomingStream`]s for that circuit.
570
///
571
// Note: in theory, it would be nice if we could get rid of this type altogether.
572
// In an ideal world, I would've instead
573
//
574
//   * added a `RelayCirc::take_incoming_streams()` method for obtaining
575
//     the futures::Stream of IncomingStream of that circuit
576
//   * made the CreateRequestHandler send each Arc<RelayCirc> over to arti-relay for handling
577
//   * made arti-relay obtain the futures::Stream<Item = IncomingStream> of each RelayCirc
578
//     by calling `RelayCirc::take_incoming_streams()`
579
//
580
// However, that would involve adding some locking/interior mutability within RelayCirc
581
// (which is always behind an Arc), or extending mq_queue::Receiver to be Clone,
582
// which would be tricky to pull off (see the comment on mq_queue::Receiver about this).
583
pub struct CircuitIncomingStreamReceiver {
584
    /// The receiver for the [`Stream`]s of `IncomingStream` of all circuits.
585
    ///
586
    /// Receives one [`Stream`] (of tor streams) per circuit.
587
    /// Each of these will be handled in a new task.
588
    circuit_stream_rx: mpsc::Receiver<<Self as Stream>::Item>,
589
}
590

            
591
impl Stream for CircuitIncomingStreamReceiver {
592
    // TODO: it would be nice if we could return a type-erased Stream here
593
    // (impl Stream<...>), but impl Trait in associated types is unstable.
594
    // See rust issue #63063 <https://github.com/rust-lang/rust/issues/63063>
595
    type Item = Box<dyn Stream<Item = IncomingStream> + Send + Sync + Unpin>;
596

            
597
    fn poll_next(
598
        mut self: std::pin::Pin<&mut Self>,
599
        cx: &mut std::task::Context<'_>,
600
    ) -> std::task::Poll<Option<Self::Item>> {
601
        use futures::StreamExt as _;
602

            
603
        self.circuit_stream_rx.poll_next_unpin(cx)
604
    }
605
}
606

            
607
/// Helper function to split a `RelayLayer` into forward and backward type-erased trait objects.
608
16
fn split_relay_layer<F, B>(
609
16
    crypt: impl RelayLayer<F, B>,
610
16
) -> (
611
16
    Box<dyn OutboundRelayLayer + Send>,
612
16
    Box<dyn InboundRelayLayer + Send>,
613
16
    CircuitBinding,
614
16
)
615
16
where
616
16
    F: OutboundRelayLayer + Send + 'static,
617
16
    B: InboundRelayLayer + Send + 'static,
618
{
619
16
    let (crypto_out, crypto_in, binding) = crypt.split_relay_layer();
620
16
    let (crypto_out, crypto_in) = (Box::new(crypto_out), Box::new(crypto_in));
621

            
622
16
    (crypto_out, crypto_in, binding)
623
16
}
624

            
625
/// An error that occurred while handling a CREATE* request.
626
#[derive(Debug, thiserror::Error)]
627
enum HandleCreateError {
628
    /// Circuit relay handshake failed.
629
    #[error("Circuit relay handshake failed")]
630
    Handshake(#[from] RelayHandshakeError),
631
    /// Circuit relay handshake failed.
632
    #[error("Failed to process the circuit relay handshake parameters")]
633
    HandshakeParameters(#[from] HandshakeParamsError),
634
    /// Requested subprotocols which aren't supported.
635
    #[error("Client requested subprotocol(s) which aren't supported")]
636
    HandshakeSubprotocols(#[from] InvalidHandshakeSubprotocolError),
637
    /// The requested handshake type is unsupported.
638
    #[error("Unsupported handshake type {0}")]
639
    Create2HandshakeType(HandshakeType),
640
    /// A memquota error.
641
    #[error("Memquota error")]
642
    Memquota(#[from] tor_memquota::Error),
643
    /// Error when spawning a task.
644
    #[error("Runtime task spawn error")]
645
    Spawn(#[from] futures::task::SpawnError),
646
    /// An internal error.
647
    ///
648
    /// Note that other variants (such as `Handshake` containing a [`RelayHandshakeError`])
649
    /// may themselves contain internal errors.
650
    #[error("Internal error")]
651
    Internal(#[from] tor_error::Bug),
652
}
653

            
654
impl HasKind for HandleCreateError {
655
14
    fn kind(&self) -> ErrorKind {
656
14
        match self {
657
10
            Self::Handshake(e) => e.kind(),
658
            Self::HandshakeParameters(e) => e.kind(),
659
            Self::HandshakeSubprotocols(e) => e.kind(),
660
4
            Self::Create2HandshakeType(_) => ErrorKind::NotImplemented,
661
            Self::Memquota(e) => e.kind(),
662
            Self::Spawn(e) => e.kind(),
663
            Self::Internal(_) => ErrorKind::Internal,
664
        }
665
14
    }
666
}
667

            
668
/// The components of a completed CREATE* handshake.
669
struct CompletedHandshakeComponents {
670
    /// The message to send in response.
671
    response: CreateResponse,
672
    /// The negotiated hop settings.
673
    hop_settings: HopSettings,
674
    /// Outbound onion crypto.
675
    crypto_out: Box<dyn OutboundRelayLayer + Send>,
676
    /// Inbound onion crypto.
677
    crypto_in: Box<dyn InboundRelayLayer + Send>,
678
}
679

            
680
/// A collection of objects built for a new relay circuit.
681
pub(crate) struct RelayCircComponents {
682
    /// The relay circuit handle.
683
    pub(crate) circ: Arc<RelayCirc>,
684
    /// Used to send data from the channel to the circuit reactor.
685
    pub(crate) sender: CircuitRxSender,
686
    /// The circuit's padding controller.
687
    pub(crate) padding_ctrl: PaddingController,
688
}
689

            
690
/// Congestion control network parameters.
691
#[derive(Debug, Clone)]
692
#[allow(clippy::exhaustive_structs)]
693
pub struct CongestionControlNetParams {
694
    /// Fixed-window algorithm parameters.
695
    pub fixed_window: FixedWindowParams,
696

            
697
    /// Vegas algorithm parameters for exit circuits.
698
    // NOTE: In this module we are handling CREATE* cells,
699
    // which only happens for non-hs circuits.
700
    // So we don't need to store the vegas hs parameters here.
701
    pub vegas_exit: VegasParams,
702

            
703
    /// Congestion window parameters.
704
    pub cwnd: CongestionWindowParams,
705

            
706
    /// RTT calculation parameters.
707
    pub rtt: RoundTripEstimatorParams,
708

            
709
    /// Flow control parameters to use for all streams on this circuit.
710
    pub flow_ctrl: FlowCtrlParameters,
711
}
712

            
713
impl CongestionControlNetParams {
714
    #[cfg(test)]
715
    // These have been copied from C-tor.
716
86
    pub(crate) fn defaults_for_tests() -> Self {
717
86
        Self {
718
86
            fixed_window: FixedWindowParams::defaults_for_tests(),
719
86
            vegas_exit: VegasParams::defaults_for_tests(),
720
86
            cwnd: CongestionWindowParams::defaults_for_tests(),
721
86
            rtt: RoundTripEstimatorParams::defaults_for_tests(),
722
86
            flow_ctrl: FlowCtrlParameters::defaults_for_tests(),
723
86
        }
724
86
    }
725
}
726

            
727
/// Network consensus parameters for handling incoming circuits.
728
///
729
/// Unlike `CircParameters`,
730
/// this is unopinionated and contains all relevant consensus parameters,
731
/// which is needed when handling an incoming CREATE* request where the
732
/// circuit origin chooses the type/settings
733
/// (for example congestion control type) of the circuit.
734
#[derive(Debug, Clone)]
735
#[allow(clippy::exhaustive_structs)]
736
pub struct CircNetParameters {
737
    /// Congestion control network parameters.
738
    pub cc: CongestionControlNetParams,
739
}
740

            
741
/// An [`IncomingStreamRequestFilter`] factory for building [`IncomingStreamRequestFilter`]s.
742
///
743
/// Each time a new circuit is opened, the [`CreateRequestHandler`] calls
744
/// [`IncomingStreamRequestFilterFactory::current_filter`] to build
745
/// an [`IncomingStreamRequestFilter`] for the circuit.
746
pub trait IncomingStreamRequestFilterFactory {
747
    /// Return the [`IncomingStreamRequestFilter`] to apply to the incoming stream requests
748
    /// arriving on a circuit.
749
    fn current_filter(&self) -> Box<dyn IncomingStreamRequestFilter>;
750
}
751

            
752
impl<F> IncomingStreamRequestFilterFactory for F
753
where
754
    F: Fn() -> Box<dyn IncomingStreamRequestFilter>,
755
{
756
16
    fn current_filter(&self) -> Box<dyn IncomingStreamRequestFilter> {
757
16
        (self)()
758
16
    }
759
}
760

            
761
#[cfg(test)]
762
mod test {
763
    // @@ begin test lint list maintained by maint/add_warning @@
764
    #![allow(clippy::bool_assert_comparison)]
765
    #![allow(clippy::clone_on_copy)]
766
    #![allow(clippy::dbg_macro)]
767
    #![allow(clippy::mixed_attributes_style)]
768
    #![allow(clippy::print_stderr)]
769
    #![allow(clippy::print_stdout)]
770
    #![allow(clippy::single_char_pattern)]
771
    #![allow(clippy::unwrap_used)]
772
    #![allow(clippy::unchecked_time_subtraction)]
773
    #![allow(clippy::useless_vec)]
774
    #![allow(clippy::needless_pass_by_value)]
775
    #![allow(clippy::string_slice)] // See arti#2571
776
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
777

            
778
    use tor_cell::chancell::msg::{AnyChanMsg, Create2, CreateFast, HandshakeType};
779
    use tor_cell::chancell::{AnyChanCell, ChanCmd, ChanMsg as _};
780
    use tor_rtcompat::test_with_one_runtime;
781

            
782
    use crate::channel::test_utils;
783
    use crate::circuit::CircParameters;
784

            
785
    /// Test the CREATE_FAST handshake.
786
    #[test]
787
    fn create_fast() {
788
        test_with_one_runtime!(|rt| async move {
789
            let mut conn_inspector = test_utils::ConnInspector::new();
790

            
791
            let (client_chan, relay_chan, _circuit_stream_rx, _target_builder) =
792
                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
793

            
794
            let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
795

            
796
            let circ_params = CircParameters::default();
797

            
798
            let tunnel = pending_tunnel
799
                .create_firsthop_fast(circ_params)
800
                .await
801
                .unwrap();
802

            
803
            // Client sent a CREATE_FAST, relay responded with a CREATED_FAST.
804
            assert_eq!(
805
                conn_inspector.try_client_cell().unwrap().msg().cmd(),
806
                ChanCmd::CREATE_FAST,
807
            );
808
            assert_eq!(
809
                conn_inspector.try_relay_cell().unwrap().msg().cmd(),
810
                ChanCmd::CREATED_FAST,
811
            );
812

            
813
            drop(tunnel);
814

            
815
            assert_eq!(
816
                conn_inspector.client_cell().await.unwrap().msg().cmd(),
817
                ChanCmd::DESTROY,
818
            );
819

            
820
            // Wait for both channels to close (ignoring any channel errors).
821
            let wait_fut =
822
                futures::future::join(client_chan.wait_for_close(), relay_chan.wait_for_close());
823
            drop((client_chan, relay_chan));
824
            let _ = wait_fut.await;
825

            
826
            // We don't expect any other messages to have been sent.
827
            assert!(conn_inspector.try_client_cell().is_none());
828
            assert!(conn_inspector.try_relay_cell().is_none());
829
        });
830
    }
831

            
832
    /// Test the CREATE_FAST handshake, but with a modified handshake payload.
833
    #[test]
834
    fn create_fast_fail() {
835
        test_with_one_runtime!(|rt| async move {
836
            let mut conn_inspector = test_utils::ConnInspector::new();
837

            
838
            // Rewrite any client-sent CREATE_FAST messages to cause the client to fail the
839
            // handshake when processing the CREATED_FAST.
840
            conn_inspector.set_client_cell_modifier(|cell: &mut AnyChanCell| {
841
                let circ_id = cell.circid();
842
                if let AnyChanMsg::CreateFast(msg) = cell.msg() {
843
                    let mut new_handshake = msg.handshake().to_vec();
844

            
845
                    // Flip all bits.
846
                    for byte in &mut new_handshake {
847
                        *byte = !*byte;
848
                    }
849

            
850
                    // Reassemble the CREATE_FAST cell with the incorrect handshake body.
851
                    let new_msg = CreateFast::new(new_handshake);
852
                    *cell = AnyChanCell::new(circ_id, AnyChanMsg::CreateFast(new_msg));
853
                }
854
            });
855

            
856
            let (client_chan, relay_chan, _circuit_stream_rx, _target_builder) =
857
                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
858

            
859
            let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
860

            
861
            let circ_params = CircParameters::default();
862

            
863
            // I don't think it's possible to modify the CREATE_FAST cell to make the relay fail the
864
            // handshake (the CREATE_FAST payload consists of only random bytes),
865
            // so the relay will respond successfully with a CREATED_FAST.
866
            // But the client will fail since the CREATED_FAST will contain garbage bytes.
867

            
868
            // The relay successfully processed the handshake,
869
            // but the client fails to validate the handshake since the handshake data was modified.
870
            assert!(matches!(
871
                pending_tunnel.create_firsthop_fast(circ_params).await,
872
                Err(crate::Error::BadCircHandshakeAuth),
873
            ));
874

            
875
            // Client sent a CREATE_FAST, relay responded with a CREATED_FAST.
876
            assert_eq!(
877
                conn_inspector.try_client_cell().unwrap().msg().cmd(),
878
                ChanCmd::CREATE_FAST,
879
            );
880
            assert_eq!(
881
                conn_inspector.try_relay_cell().unwrap().msg().cmd(),
882
                ChanCmd::CREATED_FAST,
883
            );
884

            
885
            // Since the `create_firsthop_fast()` failed above,
886
            // the client should have sent a DESTROY.
887
            assert_eq!(
888
                conn_inspector.client_cell().await.unwrap().msg().cmd(),
889
                ChanCmd::DESTROY,
890
            );
891

            
892
            // Wait for both channels to close (ignoring any channel errors).
893
            let wait_fut =
894
                futures::future::join(client_chan.wait_for_close(), relay_chan.wait_for_close());
895
            drop((client_chan, relay_chan));
896
            let _ = wait_fut.await;
897

            
898
            // We don't expect any other messages to have been sent.
899
            assert!(conn_inspector.try_client_cell().is_none());
900
            assert!(conn_inspector.try_relay_cell().is_none());
901
        });
902
    }
903

            
904
    /// Test the client with the "Relay=1" subprotocol (corresponds to the TAP handshake),
905
    /// which Arti doesn't support.
906
    #[test]
907
    fn tap() {
908
        test_with_one_runtime!(|rt| async move {
909
            let mut conn_inspector = test_utils::ConnInspector::new();
910

            
911
            let (client_chan, _relay_chan, _circuit_stream_rx, mut target_builder) =
912
                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
913

            
914
            let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
915

            
916
            let circ_params = CircParameters::default();
917

            
918
            // https://spec.torproject.org/tor-spec/subprotocol-versioning.html
919
            // 1 = RELAY_BASE
920
            let protocols = "Relay=1".parse().unwrap();
921
            let target = target_builder.protocols(protocols).build().unwrap();
922

            
923
            // TODO: This should fail since we don't support TAP handshakes.
924
            // But the channel will do an ntor handshake anyway even though it's not supported.
925
            // https://gitlab.torproject.org/tpo/core/arti/-/work_items/2489
926
            let _tunnel = pending_tunnel
927
                .create_firsthop(&target, circ_params)
928
                .await
929
                .unwrap();
930

            
931
            // TODO: As above, this is wrong.
932
            assert_eq!(
933
                conn_inspector.try_client_cell().unwrap().msg().cmd(),
934
                ChanCmd::CREATE2,
935
            );
936
            assert_eq!(
937
                conn_inspector.try_relay_cell().unwrap().msg().cmd(),
938
                ChanCmd::CREATED2,
939
            );
940
        });
941
    }
942

            
943
    /// Test the CREATE2 ntor handshake.
944
    #[test]
945
    fn ntor() {
946
        test_with_one_runtime!(|rt| async move {
947
            let mut conn_inspector = test_utils::ConnInspector::new();
948

            
949
            let (client_chan, relay_chan, _circuit_stream_rx, mut target_builder) =
950
                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
951

            
952
            // https://spec.torproject.org/tor-spec/subprotocol-versioning.html
953
            // 2 = RELAY_NTOR
954
            // 3 = RELAY_EXTEND_IPv6
955
            for relay_version in [2, 3] {
956
                let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
957

            
958
                let circ_params = CircParameters::default();
959

            
960
                let protocols = format!("Relay=2-{relay_version}").parse().unwrap();
961
                let target = target_builder.protocols(protocols).build().unwrap();
962

            
963
                let tunnel = pending_tunnel
964
                    .create_firsthop(&target, circ_params)
965
                    .await
966
                    .unwrap();
967

            
968
                let client_cell = conn_inspector.try_client_cell().unwrap().msg().clone();
969
                let relay_cell = conn_inspector.try_relay_cell().unwrap().msg().clone();
970

            
971
                // Client sent a CREATE2, relay responded with a CREATED2.
972
                assert_eq!(client_cell.cmd(), ChanCmd::CREATE2);
973
                assert_eq!(relay_cell.cmd(), ChanCmd::CREATED2);
974

            
975
                // Check that it was an ntor handshake.
976
                let AnyChanMsg::Create2(client_cell) = client_cell else {
977
                    unreachable!("CREATE2 checked above");
978
                };
979
                assert_eq!(client_cell.handshake_type(), HandshakeType::NTOR);
980

            
981
                drop(tunnel);
982

            
983
                assert_eq!(
984
                    conn_inspector.client_cell().await.unwrap().msg().cmd(),
985
                    ChanCmd::DESTROY,
986
                );
987

            
988
                // We don't expect any other messages to have been sent.
989
                assert!(conn_inspector.try_client_cell().is_none());
990
                assert!(conn_inspector.try_relay_cell().is_none());
991
            }
992

            
993
            // Wait for both channels to close (ignoring any channel errors).
994
            let wait_fut =
995
                futures::future::join(client_chan.wait_for_close(), relay_chan.wait_for_close());
996
            drop((client_chan, relay_chan));
997
            let _ = wait_fut.await;
998

            
999
            // We don't expect any other messages to have been sent.
            assert!(conn_inspector.try_client_cell().is_none());
            assert!(conn_inspector.try_relay_cell().is_none());
        });
    }
    /// Test the CREATE2 ntor handshake, but with a modified handshake payload.
    #[test]
    fn ntor_fail() {
        test_with_one_runtime!(|rt| async move {
            let mut conn_inspector = test_utils::ConnInspector::new();
            // Rewrite any client-sent CREATE2 messages to cause the relay to fail the handshake.
            conn_inspector.set_client_cell_modifier(|cell: &mut AnyChanCell| {
                let circ_id = cell.circid();
                if let AnyChanMsg::Create2(msg) = cell.msg() {
                    let mut new_body = msg.body().to_vec();
                    // Flip some arbitrarily chosen byte.
                    new_body[10] = !new_body[10];
                    // Reassemble the CREATE2 cell with the incorrect handshake body.
                    let new_msg = Create2::new(msg.handshake_type(), new_body);
                    *cell = AnyChanCell::new(circ_id, AnyChanMsg::Create2(new_msg));
                }
            });
            let (client_chan, relay_chan, _circuit_stream_rx, mut target_builder) =
                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
            // https://spec.torproject.org/tor-spec/subprotocol-versioning.html
            // 2 = RELAY_NTOR
            // 3 = RELAY_EXTEND_IPv6
            for relay_version in [2, 3] {
                let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
                let circ_params = CircParameters::default();
                let protocols = format!("Relay=2-{relay_version}").parse().unwrap();
                let target = target_builder.protocols(protocols).build().unwrap();
                // The relay refuses the circuit handshake since the CREATED2 cell had some
                // intentional issue with the handshake body.
                assert!(matches!(
                    pending_tunnel.create_firsthop(&target, circ_params).await,
                    Err(crate::Error::CircRefused(_)),
                ));
                // Client sent a CREATE2, relay responded with a DESTROY.
                assert_eq!(
                    conn_inspector.try_client_cell().unwrap().msg().cmd(),
                    ChanCmd::CREATE2,
                );
                assert_eq!(
                    conn_inspector.try_relay_cell().unwrap().msg().cmd(),
                    ChanCmd::DESTROY,
                );
                // We don't expect any other messages to have been sent.
                assert!(conn_inspector.try_client_cell().is_none());
                assert!(conn_inspector.try_relay_cell().is_none());
            }
            // Wait for both channels to close (ignoring any channel errors).
            let wait_fut =
                futures::future::join(client_chan.wait_for_close(), relay_chan.wait_for_close());
            drop((client_chan, relay_chan));
            let _ = wait_fut.await;
            // We don't expect any other messages to have been sent.
            assert!(conn_inspector.try_client_cell().is_none());
            assert!(conn_inspector.try_relay_cell().is_none());
        });
    }
    /// Test the CREATE2 ntor-v3 handshake.
    #[test]
    fn ntor_v3() {
        test_with_one_runtime!(|rt| async move {
            let mut conn_inspector = test_utils::ConnInspector::new();
            let (client_chan, relay_chan, _circuit_stream_rx, mut target_builder) =
                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
            // https://spec.torproject.org/tor-spec/subprotocol-versioning.html
            // 4 = RELAY_NTORV3
            // 5 = RELAY_NEGOTIATE_SUBPROTO
            // 6 = RELAY_CRYPT_CGO
            for relay_version in [4, 5, 6] {
                let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
                let circ_params = CircParameters::default();
                let protocols = format!("Relay=4-{relay_version}").parse().unwrap();
                let target = target_builder.protocols(protocols).build().unwrap();
                let tunnel = pending_tunnel
                    .create_firsthop(&target, circ_params)
                    .await
                    .unwrap();
                let client_cell = conn_inspector.try_client_cell().unwrap().msg().clone();
                let relay_cell = conn_inspector.try_relay_cell().unwrap().msg().clone();
                // Client sent a CREATE2, relay responded with a CREATED2.
                assert_eq!(client_cell.cmd(), ChanCmd::CREATE2);
                assert_eq!(relay_cell.cmd(), ChanCmd::CREATED2);
                // Check that it was an ntor-v3 handshake.
                let AnyChanMsg::Create2(client_cell) = client_cell else {
                    unreachable!("CREATE2 checked above");
                };
                assert_eq!(client_cell.handshake_type(), HandshakeType::NTOR_V3);
                // TODO: It would be nice if we had a way to check that CGO was in use when
                // `relay_version` is >=6, but I don't see a nice way to do that.
                drop(tunnel);
                assert_eq!(
                    conn_inspector.client_cell().await.unwrap().msg().cmd(),
                    ChanCmd::DESTROY,
                );
                // We don't expect any other messages to have been sent.
                assert!(conn_inspector.try_client_cell().is_none());
                assert!(conn_inspector.try_relay_cell().is_none());
            }
            // Wait for both channels to close (ignoring any channel errors).
            let wait_fut =
                futures::future::join(client_chan.wait_for_close(), relay_chan.wait_for_close());
            drop((client_chan, relay_chan));
            let _ = wait_fut.await;
            // We don't expect any other messages to have been sent.
            assert!(conn_inspector.try_client_cell().is_none());
            assert!(conn_inspector.try_relay_cell().is_none());
        });
    }
    /// Test the CREATE2 ntor-v3 handshake, but with a modified handshake payload.
    #[test]
    fn ntor_v3_fail() {
        test_with_one_runtime!(|rt| async move {
            let mut conn_inspector = test_utils::ConnInspector::new();
            // Rewrite any client-sent CREATE2 messages to cause the relay to fail the handshake.
            conn_inspector.set_client_cell_modifier(|cell: &mut AnyChanCell| {
                let circ_id = cell.circid();
                if let AnyChanMsg::Create2(msg) = cell.msg() {
                    let mut new_body = msg.body().to_vec();
                    // Flip some arbitrarily chosen byte.
                    new_body[10] = !new_body[10];
                    // Reassemble the CREATE2 cell with the incorrect handshake body.
                    let new_msg = Create2::new(msg.handshake_type(), new_body);
                    *cell = AnyChanCell::new(circ_id, AnyChanMsg::Create2(new_msg));
                }
            });
            let (client_chan, relay_chan, _circuit_stream_rx, mut target_builder) =
                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
            // https://spec.torproject.org/tor-spec/subprotocol-versioning.html
            // 4 = RELAY_NTORV3
            // 5 = RELAY_NEGOTIATE_SUBPROTO
            // 6 = RELAY_CRYPT_CGO
            for relay_version in [4, 5, 6] {
                let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
                let circ_params = CircParameters::default();
                let protocols = format!("Relay=4-{relay_version}").parse().unwrap();
                let target = target_builder.protocols(protocols).build().unwrap();
                // The relay refuses the circuit handshake since the CREATED2 cell had some
                // intentional issue with the handshake body.
                assert!(matches!(
                    pending_tunnel.create_firsthop(&target, circ_params).await,
                    Err(crate::Error::CircRefused(_)),
                ));
                // Client sent a CREATE2, relay responded with a DESTROY.
                assert_eq!(
                    conn_inspector.try_client_cell().unwrap().msg().cmd(),
                    ChanCmd::CREATE2,
                );
                assert_eq!(
                    conn_inspector.try_relay_cell().unwrap().msg().cmd(),
                    ChanCmd::DESTROY,
                );
                // We don't expect any other messages to have been sent.
                assert!(conn_inspector.try_client_cell().is_none());
                assert!(conn_inspector.try_relay_cell().is_none());
            }
            // Wait for both channels to close (ignoring any channel errors).
            let wait_fut =
                futures::future::join(client_chan.wait_for_close(), relay_chan.wait_for_close());
            drop((client_chan, relay_chan));
            let _ = wait_fut.await;
            // We don't expect any other messages to have been sent.
            assert!(conn_inspector.try_client_cell().is_none());
            assert!(conn_inspector.try_relay_cell().is_none());
        });
    }
    /// Test the CREATE2 handshake, but with an invalid handshake type.
    #[test]
    fn create2_invalid_handshake_fail() {
        test_with_one_runtime!(|rt| async move {
            let mut conn_inspector = test_utils::ConnInspector::new();
            // Rewrite any client-sent CREATE2 messages to cause the relay to fail the handshake.
            conn_inspector.set_client_cell_modifier(|cell: &mut AnyChanCell| {
                let circ_id = cell.circid();
                if let AnyChanMsg::Create2(msg) = cell.msg() {
                    // Reassemble the CREATE2 cell with the incorrect handshake type.
                    let new_msg = tor_cell::chancell::msg::Create2::new(99.into(), msg.body());
                    *cell = AnyChanCell::new(circ_id, AnyChanMsg::Create2(new_msg));
                }
            });
            let (client_chan, relay_chan, _circuit_stream_rx, mut target_builder) =
                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
            // https://spec.torproject.org/tor-spec/subprotocol-versioning.html
            // 2 = RELAY_NTOR
            // 3 = RELAY_EXTEND_IPv6
            // 4 = RELAY_NTORV3
            // 5 = RELAY_NEGOTIATE_SUBPROTO
            // 6 = RELAY_CRYPT_CGO
            for relay_version in [2, 6] {
                let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
                let circ_params = CircParameters::default();
                let protocols = format!("Relay=2-{relay_version}").parse().unwrap();
                let target = target_builder.protocols(protocols).build().unwrap();
                // The relay refuses the circuit handshake since the CREATED2 cell had some
                // unrecognized handshake type.
                assert!(matches!(
                    pending_tunnel.create_firsthop(&target, circ_params).await,
                    Err(crate::Error::CircRefused(_)),
                ));
                // Client sent a CREATE2, relay responded with a DESTROY.
                assert_eq!(
                    conn_inspector.try_client_cell().unwrap().msg().cmd(),
                    ChanCmd::CREATE2,
                );
                assert_eq!(
                    conn_inspector.try_relay_cell().unwrap().msg().cmd(),
                    ChanCmd::DESTROY,
                );
                // We don't expect any other messages to have been sent.
                assert!(conn_inspector.try_client_cell().is_none());
                assert!(conn_inspector.try_relay_cell().is_none());
            }
            // Wait for both channels to close (ignoring any channel errors).
            let wait_fut =
                futures::future::join(client_chan.wait_for_close(), relay_chan.wait_for_close());
            drop((client_chan, relay_chan));
            let _ = wait_fut.await;
            // We don't expect any other messages to have been sent.
            assert!(conn_inspector.try_client_cell().is_none());
            assert!(conn_inspector.try_relay_cell().is_none());
        });
    }
}