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::{NtorServer, NtorServerKeys};
24
use crate::crypto::handshake::ntor_v3::{NtorV3Server, NtorV3ServerKeys};
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::curve25519;
47
use tor_llcrypto::pk::ed25519::Ed25519Identity;
48
use tor_llcrypto::pk::rsa::RsaIdentity;
49
use tor_memquota::mq_queue::ChannelSpec as _;
50
use tor_memquota::mq_queue::MpscSpec;
51
use tor_relay_crypto::pk::{RelayNtorKeypair, RelayNtorKeys};
52
use tor_rtcompat::SpawnExt as _;
53
use tor_rtcompat::{DynTimeProvider, Runtime};
54
use tracing::{debug, trace};
55

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

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

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

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

            
136
43
        let circuit_stream_rx = CircuitIncomingStreamReceiver {
137
43
            circuit_stream_rx: stream_rx,
138
43
        };
139

            
140
43
        (handler, circuit_stream_rx)
141
43
    }
142

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

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

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

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

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

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

            
230
8
        let memquota = CircuitAccount::new(memquota)?;
231

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

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

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

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

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

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

            
303
8
        Ok((
304
8
            handshake_components.response,
305
8
            RelayCircComponents {
306
8
                circ,
307
8
                sender,
308
8
                padding_ctrl,
309
8
            },
310
8
        ))
311
15
    }
312

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

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

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

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

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

            
348
2
        let (crypto_out, crypto_in, _binding) = split_relay_layer(crypt);
349

            
350
2
        let response = CreatedFast::new(handshake_msg);
351
2
        let response = CreateResponse::CreatedFast(response);
352

            
353
2
        trace!("Completed CREATE_FAST handshake");
354

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

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

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

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

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

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

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

            
400
3
        let (crypto_out, crypto_in, _binding) = split_relay_layer(crypt);
401

            
402
3
        let response = Created2::new(handshake_msg);
403
3
        let response = CreateResponse::Created2(response);
404

            
405
3
        trace!("Completed ntor handshake");
406

            
407
3
        Ok(CompletedHandshakeComponents {
408
3
            response,
409
3
            hop_settings,
410
3
            crypto_out,
411
3
            crypto_in,
412
3
        })
413
5
    }
414

            
415
    /// The handshake code for a CREATE2 ntor-v3 request.
416
6
    async fn handle_create2_ntorv3(
417
6
        &self,
418
6
        msg_body: &[u8],
419
6
        our_ed25519_id: &Ed25519Identity,
420
6
    ) -> Result<CompletedHandshakeComponents, HandleCreateError> {
421
6
        let ntor_keys =
422
6
            self.with_ntor_keys(|k| NtorV3SecretKey::new(our_ed25519_id, Arc::clone(k)));
423

            
424
6
        let circ_net_params = self
425
6
            .circ_net_params
426
6
            .read()
427
6
            .expect("rwlock poisoned")
428
6
            .clone();
429

            
430
        // These extensions can be negotiated during the handshake.
431
6
        let mut cc_algorithm = AlgorithmDiscriminants::FixedWindow;
432

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

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

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

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

            
463
                        cc_algorithm = AlgorithmDiscriminants::Vegas;
464

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

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

            
508
3
            Some(response_exts)
509
3
        };
510

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

            
519
        // Ensure that the client did not request invalid/unsupported subprotocols.
520
3
        let subprotos = HandshakeSubprotocols::try_from_request(subprotos)?;
521

            
522
3
        let hop_settings =
523
3
            HopSettings::from_handshake_params(circ_net_params, cc_algorithm, subprotos)?;
524

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

            
535
3
        let response = Created2::new(handshake_msg);
536
3
        let response = CreateResponse::Created2(response);
537

            
538
3
        trace!(?cc_algorithm, ?subprotos, "Completed ntor-v3 handshake");
539

            
540
3
        Ok(CompletedHandshakeComponents {
541
3
            response,
542
3
            hop_settings,
543
3
            crypto_out,
544
3
            crypto_in,
545
3
        })
546
6
    }
547

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

            
566
/// The server's RSA identity and ntor keypair.
567
#[derive(derive_more::Constructor)]
568
struct NtorSecretKey<'a> {
569
    /// The server's RSA identity.
570
    ident: &'a RsaIdentity,
571
    /// The server's ntor keypair.
572
    keys: Arc<RelayNtorKeypair>,
573
}
574

            
575
impl<'a> NtorServerKeys for NtorSecretKey<'a> {
576
8
    fn ident(&self) -> &RsaIdentity {
577
8
        self.ident
578
8
    }
579

            
580
8
    fn public_key(&self) -> &curve25519::PublicKey {
581
8
        self.keys.public_raw()
582
8
    }
583

            
584
3
    fn secret_key(&self) -> &curve25519::StaticSecret {
585
3
        self.keys.secret()
586
3
    }
587
}
588

            
589
/// The server's Ed25519 identity and ntor keypair.
590
#[derive(derive_more::Constructor)]
591
struct NtorV3SecretKey<'a> {
592
    /// The server's Ed25519 identity.
593
    ident: &'a Ed25519Identity,
594
    /// The server's ntor keypair.
595
    keys: Arc<RelayNtorKeypair>,
596
}
597

            
598
impl<'a> NtorV3ServerKeys for NtorV3SecretKey<'a> {
599
15
    fn ident(&self) -> &Ed25519Identity {
600
15
        self.ident
601
15
    }
602

            
603
15
    fn public_key(&self) -> &curve25519::PublicKey {
604
15
        self.keys.public_raw()
605
15
    }
606

            
607
3
    fn secret_key(&self) -> &curve25519::StaticSecret {
608
3
        self.keys.secret()
609
3
    }
610
}
611

            
612
/// A receiver of [`Stream`]s (one for each incoming circuit),
613
/// where each `Stream` produces [`IncomingStream`]s for that circuit.
614
///
615
// Note: in theory, it would be nice if we could get rid of this type altogether.
616
// In an ideal world, I would've instead
617
//
618
//   * added a `RelayCirc::take_incoming_streams()` method for obtaining
619
//     the futures::Stream of IncomingStream of that circuit
620
//   * made the CreateRequestHandler send each Arc<RelayCirc> over to arti-relay for handling
621
//   * made arti-relay obtain the futures::Stream<Item = IncomingStream> of each RelayCirc
622
//     by calling `RelayCirc::take_incoming_streams()`
623
//
624
// However, that would involve adding some locking/interior mutability within RelayCirc
625
// (which is always behind an Arc), or extending mq_queue::Receiver to be Clone,
626
// which would be tricky to pull off (see the comment on mq_queue::Receiver about this).
627
pub struct CircuitIncomingStreamReceiver {
628
    /// The receiver for the [`Stream`]s of `IncomingStream` of all circuits.
629
    ///
630
    /// Receives one [`Stream`] (of tor streams) per circuit.
631
    /// Each of these will be handled in a new task.
632
    circuit_stream_rx: mpsc::Receiver<<Self as Stream>::Item>,
633
}
634

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

            
641
    fn poll_next(
642
        mut self: std::pin::Pin<&mut Self>,
643
        cx: &mut std::task::Context<'_>,
644
    ) -> std::task::Poll<Option<Self::Item>> {
645
        use futures::StreamExt as _;
646

            
647
        self.circuit_stream_rx.poll_next_unpin(cx)
648
    }
649
}
650

            
651
/// Helper function to split a `RelayLayer` into forward and backward type-erased trait objects.
652
8
fn split_relay_layer<F, B>(
653
8
    crypt: impl RelayLayer<F, B>,
654
8
) -> (
655
8
    Box<dyn OutboundRelayLayer + Send>,
656
8
    Box<dyn InboundRelayLayer + Send>,
657
8
    CircuitBinding,
658
8
)
659
8
where
660
8
    F: OutboundRelayLayer + Send + 'static,
661
8
    B: InboundRelayLayer + Send + 'static,
662
{
663
8
    let (crypto_out, crypto_in, binding) = crypt.split_relay_layer();
664
8
    let (crypto_out, crypto_in) = (Box::new(crypto_out), Box::new(crypto_in));
665

            
666
8
    (crypto_out, crypto_in, binding)
667
8
}
668

            
669
/// An error that occurred while handling a CREATE* request.
670
#[derive(Debug, thiserror::Error)]
671
enum HandleCreateError {
672
    /// Circuit relay handshake failed.
673
    #[error("Circuit relay handshake failed")]
674
    Handshake(#[from] RelayHandshakeError),
675
    /// Circuit relay handshake failed.
676
    #[error("Failed to process the circuit relay handshake parameters")]
677
    HandshakeParameters(#[from] HandshakeParamsError),
678
    /// Requested subprotocols which aren't supported.
679
    #[error("Client requested subprotocol(s) which aren't supported")]
680
    HandshakeSubprotocols(#[from] InvalidHandshakeSubprotocolError),
681
    /// The requested handshake type is unsupported.
682
    #[error("Unsupported handshake type {0}")]
683
    Create2HandshakeType(HandshakeType),
684
    /// A memquota error.
685
    #[error("Memquota error")]
686
    Memquota(#[from] tor_memquota::Error),
687
    /// Error when spawning a task.
688
    #[error("Runtime task spawn error")]
689
    Spawn(#[from] futures::task::SpawnError),
690
    /// An internal error.
691
    ///
692
    /// Note that other variants (such as `Handshake` containing a [`RelayHandshakeError`])
693
    /// may themselves contain internal errors.
694
    #[error("Internal error")]
695
    Internal(#[from] tor_error::Bug),
696
}
697

            
698
impl HasKind for HandleCreateError {
699
7
    fn kind(&self) -> ErrorKind {
700
7
        match self {
701
5
            Self::Handshake(e) => e.kind(),
702
            Self::HandshakeParameters(e) => e.kind(),
703
            Self::HandshakeSubprotocols(e) => e.kind(),
704
2
            Self::Create2HandshakeType(_) => ErrorKind::NotImplemented,
705
            Self::Memquota(e) => e.kind(),
706
            Self::Spawn(e) => e.kind(),
707
            Self::Internal(_) => ErrorKind::Internal,
708
        }
709
7
    }
710
}
711

            
712
/// The components of a completed CREATE* handshake.
713
struct CompletedHandshakeComponents {
714
    /// The message to send in response.
715
    response: CreateResponse,
716
    /// The negotiated hop settings.
717
    hop_settings: HopSettings,
718
    /// Outbound onion crypto.
719
    crypto_out: Box<dyn OutboundRelayLayer + Send>,
720
    /// Inbound onion crypto.
721
    crypto_in: Box<dyn InboundRelayLayer + Send>,
722
}
723

            
724
/// A collection of objects built for a new relay circuit.
725
pub(crate) struct RelayCircComponents {
726
    /// The relay circuit handle.
727
    pub(crate) circ: Arc<RelayCirc>,
728
    /// Used to send data from the channel to the circuit reactor.
729
    pub(crate) sender: CircuitRxSender,
730
    /// The circuit's padding controller.
731
    pub(crate) padding_ctrl: PaddingController,
732
}
733

            
734
/// Congestion control network parameters.
735
#[derive(Debug, Clone)]
736
#[allow(clippy::exhaustive_structs)]
737
pub struct CongestionControlNetParams {
738
    /// Fixed-window algorithm parameters.
739
    pub fixed_window: FixedWindowParams,
740

            
741
    /// Vegas algorithm parameters for exit circuits.
742
    // NOTE: In this module we are handling CREATE* cells,
743
    // which only happens for non-hs circuits.
744
    // So we don't need to store the vegas hs parameters here.
745
    pub vegas_exit: VegasParams,
746

            
747
    /// Congestion window parameters.
748
    pub cwnd: CongestionWindowParams,
749

            
750
    /// RTT calculation parameters.
751
    pub rtt: RoundTripEstimatorParams,
752

            
753
    /// Flow control parameters to use for all streams on this circuit.
754
    pub flow_ctrl: FlowCtrlParameters,
755
}
756

            
757
impl CongestionControlNetParams {
758
    #[cfg(test)]
759
    // These have been copied from C-tor.
760
43
    pub(crate) fn defaults_for_tests() -> Self {
761
43
        Self {
762
43
            fixed_window: FixedWindowParams::defaults_for_tests(),
763
43
            vegas_exit: VegasParams::defaults_for_tests(),
764
43
            cwnd: CongestionWindowParams::defaults_for_tests(),
765
43
            rtt: RoundTripEstimatorParams::defaults_for_tests(),
766
43
            flow_ctrl: FlowCtrlParameters::defaults_for_tests(),
767
43
        }
768
43
    }
769
}
770

            
771
/// Network consensus parameters for handling incoming circuits.
772
///
773
/// Unlike `CircParameters`,
774
/// this is unopinionated and contains all relevant consensus parameters,
775
/// which is needed when handling an incoming CREATE* request where the
776
/// circuit origin chooses the type/settings
777
/// (for example congestion control type) of the circuit.
778
#[derive(Debug, Clone)]
779
#[allow(clippy::exhaustive_structs)]
780
pub struct CircNetParameters {
781
    /// Congestion control network parameters.
782
    pub cc: CongestionControlNetParams,
783
}
784

            
785
/// An [`IncomingStreamRequestFilter`] factory for building [`IncomingStreamRequestFilter`]s.
786
///
787
/// Each time a new circuit is opened, the [`CreateRequestHandler`] calls
788
/// [`IncomingStreamRequestFilterFactory::current_filter`] to build
789
/// an [`IncomingStreamRequestFilter`] for the circuit.
790
pub trait IncomingStreamRequestFilterFactory {
791
    /// Return the [`IncomingStreamRequestFilter`] to apply to the incoming stream requests
792
    /// arriving on a circuit.
793
    fn current_filter(&self) -> Box<dyn IncomingStreamRequestFilter>;
794
}
795

            
796
impl<F> IncomingStreamRequestFilterFactory for F
797
where
798
    F: Fn() -> Box<dyn IncomingStreamRequestFilter>,
799
{
800
8
    fn current_filter(&self) -> Box<dyn IncomingStreamRequestFilter> {
801
8
        (self)()
802
8
    }
803
}
804

            
805
#[cfg(test)]
806
mod test {
807
    // @@ begin test lint list maintained by maint/add_warning @@
808
    #![allow(clippy::bool_assert_comparison)]
809
    #![allow(clippy::clone_on_copy)]
810
    #![allow(clippy::dbg_macro)]
811
    #![allow(clippy::mixed_attributes_style)]
812
    #![allow(clippy::print_stderr)]
813
    #![allow(clippy::print_stdout)]
814
    #![allow(clippy::single_char_pattern)]
815
    #![allow(clippy::unwrap_used)]
816
    #![allow(clippy::unchecked_time_subtraction)]
817
    #![allow(clippy::useless_vec)]
818
    #![allow(clippy::needless_pass_by_value)]
819
    #![allow(clippy::string_slice)] // See arti#2571
820
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
821

            
822
    use tor_cell::chancell::msg::{AnyChanMsg, Create2, CreateFast, HandshakeType};
823
    use tor_cell::chancell::{AnyChanCell, ChanCmd, ChanMsg as _};
824
    use tor_rtcompat::test_with_one_runtime;
825

            
826
    use crate::channel::test_utils;
827
    use crate::circuit::CircParameters;
828

            
829
    /// Test the CREATE_FAST handshake.
830
    #[test]
831
    fn create_fast() {
832
        test_with_one_runtime!(|rt| async move {
833
            let mut conn_inspector = test_utils::ConnInspector::new();
834

            
835
            let (client_chan, relay_chan, _circuit_stream_rx, _target_builder) =
836
                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
837

            
838
            let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
839

            
840
            let circ_params = CircParameters::default();
841

            
842
            let tunnel = pending_tunnel
843
                .create_firsthop_fast(circ_params)
844
                .await
845
                .unwrap();
846

            
847
            // Client sent a CREATE_FAST, relay responded with a CREATED_FAST.
848
            assert_eq!(
849
                conn_inspector.try_client_cell().unwrap().msg().cmd(),
850
                ChanCmd::CREATE_FAST,
851
            );
852
            assert_eq!(
853
                conn_inspector.try_relay_cell().unwrap().msg().cmd(),
854
                ChanCmd::CREATED_FAST,
855
            );
856

            
857
            drop(tunnel);
858

            
859
            assert_eq!(
860
                conn_inspector.client_cell().await.unwrap().msg().cmd(),
861
                ChanCmd::DESTROY,
862
            );
863

            
864
            // Wait for both channels to close (ignoring any channel errors).
865
            let wait_fut =
866
                futures::future::join(client_chan.wait_for_close(), relay_chan.wait_for_close());
867
            drop((client_chan, relay_chan));
868
            let _ = wait_fut.await;
869

            
870
            // We don't expect any other messages to have been sent.
871
            assert!(conn_inspector.try_client_cell().is_none());
872
            assert!(conn_inspector.try_relay_cell().is_none());
873
        });
874
    }
875

            
876
    /// Test the CREATE_FAST handshake, but with a modified handshake payload.
877
    #[test]
878
    fn create_fast_fail() {
879
        test_with_one_runtime!(|rt| async move {
880
            let mut conn_inspector = test_utils::ConnInspector::new();
881

            
882
            // Rewrite any client-sent CREATE_FAST messages to cause the client to fail the
883
            // handshake when processing the CREATED_FAST.
884
            conn_inspector.set_client_cell_modifier(|cell: &mut AnyChanCell| {
885
                let circ_id = cell.circid();
886
                if let AnyChanMsg::CreateFast(msg) = cell.msg() {
887
                    let mut new_handshake = msg.handshake().to_vec();
888

            
889
                    // Flip all bits.
890
                    for byte in &mut new_handshake {
891
                        *byte = !*byte;
892
                    }
893

            
894
                    // Reassemble the CREATE_FAST cell with the incorrect handshake body.
895
                    let new_msg = CreateFast::new(new_handshake);
896
                    *cell = AnyChanCell::new(circ_id, AnyChanMsg::CreateFast(new_msg));
897
                }
898
            });
899

            
900
            let (client_chan, relay_chan, _circuit_stream_rx, _target_builder) =
901
                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
902

            
903
            let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
904

            
905
            let circ_params = CircParameters::default();
906

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

            
912
            // The relay successfully processed the handshake,
913
            // but the client fails to validate the handshake since the handshake data was modified.
914
            assert!(matches!(
915
                pending_tunnel.create_firsthop_fast(circ_params).await,
916
                Err(crate::Error::BadCircHandshakeAuth),
917
            ));
918

            
919
            // Client sent a CREATE_FAST, relay responded with a CREATED_FAST.
920
            assert_eq!(
921
                conn_inspector.try_client_cell().unwrap().msg().cmd(),
922
                ChanCmd::CREATE_FAST,
923
            );
924
            assert_eq!(
925
                conn_inspector.try_relay_cell().unwrap().msg().cmd(),
926
                ChanCmd::CREATED_FAST,
927
            );
928

            
929
            // Since the `create_firsthop_fast()` failed above,
930
            // the client should have sent a DESTROY.
931
            assert_eq!(
932
                conn_inspector.client_cell().await.unwrap().msg().cmd(),
933
                ChanCmd::DESTROY,
934
            );
935

            
936
            // Wait for both channels to close (ignoring any channel errors).
937
            let wait_fut =
938
                futures::future::join(client_chan.wait_for_close(), relay_chan.wait_for_close());
939
            drop((client_chan, relay_chan));
940
            let _ = wait_fut.await;
941

            
942
            // We don't expect any other messages to have been sent.
943
            assert!(conn_inspector.try_client_cell().is_none());
944
            assert!(conn_inspector.try_relay_cell().is_none());
945
        });
946
    }
947

            
948
    /// Test the client with the "Relay=1" subprotocol (corresponds to the TAP handshake),
949
    /// which Arti doesn't support.
950
    #[test]
951
    fn tap() {
952
        test_with_one_runtime!(|rt| async move {
953
            let mut conn_inspector = test_utils::ConnInspector::new();
954

            
955
            let (client_chan, _relay_chan, _circuit_stream_rx, mut target_builder) =
956
                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
957

            
958
            let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
959

            
960
            let circ_params = CircParameters::default();
961

            
962
            // https://spec.torproject.org/tor-spec/subprotocol-versioning.html
963
            // 1 = RELAY_BASE
964
            let protocols = "Relay=1".parse().unwrap();
965
            let target = target_builder.protocols(protocols).build().unwrap();
966

            
967
            // TODO: This should fail since we don't support TAP handshakes.
968
            // But the channel will do an ntor handshake anyway even though it's not supported.
969
            // https://gitlab.torproject.org/tpo/core/arti/-/work_items/2489
970
            let _tunnel = pending_tunnel
971
                .create_firsthop(&target, circ_params)
972
                .await
973
                .unwrap();
974

            
975
            // TODO: As above, this is wrong.
976
            assert_eq!(
977
                conn_inspector.try_client_cell().unwrap().msg().cmd(),
978
                ChanCmd::CREATE2,
979
            );
980
            assert_eq!(
981
                conn_inspector.try_relay_cell().unwrap().msg().cmd(),
982
                ChanCmd::CREATED2,
983
            );
984
        });
985
    }
986

            
987
    /// Test the CREATE2 ntor handshake.
988
    #[test]
989
    fn ntor() {
990
        test_with_one_runtime!(|rt| async move {
991
            let mut conn_inspector = test_utils::ConnInspector::new();
992

            
993
            let (client_chan, relay_chan, _circuit_stream_rx, mut target_builder) =
994
                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
995

            
996
            // https://spec.torproject.org/tor-spec/subprotocol-versioning.html
997
            // 2 = RELAY_NTOR
998
            // 3 = RELAY_EXTEND_IPv6
999
            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();
                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 handshake.
                let AnyChanMsg::Create2(client_cell) = client_cell else {
                    unreachable!("CREATE2 checked above");
                };
                assert_eq!(client_cell.handshake_type(), HandshakeType::NTOR);
                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 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());
        });
    }
}