1
//! Relay cell cryptography
2
//!
3
//! The Tor protocol centers around "RELAY cells", which are transmitted through
4
//! the network along circuits.  The client that creates a circuit shares two
5
//! different sets of keys and state with each of the relays on the circuit: one
6
//! for "outbound" traffic, and one for "inbound" traffic.
7
//!
8
//! So for example, if a client creates a 3-hop circuit with relays R1, R2, and
9
//! R3, the client has:
10
//!   * An "inbound" cryptographic state shared with R1.
11
//!   * An "inbound" cryptographic state shared with R2.
12
//!   * An "inbound" cryptographic state shared with R3.
13
//!   * An "outbound" cryptographic state shared with R1.
14
//!   * An "outbound" cryptographic state shared with R2.
15
//!   * An "outbound" cryptographic state shared with R3.
16
//!
17
//! In this module at least, we'll call each of these state objects a "layer" of
18
//! the circuit's encryption.
19
//!
20
//! The Tor specification does not describe these layer objects very explicitly.
21
//! In the current relay cryptography protocol, each layer contains:
22
//!    * A keyed AES-CTR state. (AES-128 or AES-256)  This cipher uses a key
23
//!      called `Kf` or `Kb` in the spec, where `Kf` is a "forward" key used in
24
//!      the outbound direction, and `Kb` is a "backward" key used in the
25
//!      inbound direction.
26
//!    * A running digest. (SHA1 or SHA3)  This digest is initialized with a
27
//!      value called `Df` or `Db` in the spec.
28
//!
29
//! This `crypto::cell` module itself provides traits and implementations that
30
//! should work for all current future versions of the relay cell crypto design.
31
//! The current Tor protocols are instantiated in a `tor1` submodule.
32

            
33
#[cfg(feature = "bench")]
34
pub(crate) mod bench_utils;
35
#[cfg(feature = "counter-galois-onion")]
36
pub(crate) mod cgo;
37
pub(crate) mod tor1;
38

            
39
use crate::{Error, Result};
40
use derive_deftly::Deftly;
41
use tor_cell::{
42
    chancell::{BoxedCellBody, ChanCmd},
43
    relaycell::msg::SendmeTag,
44
};
45
use tor_memquota::derive_deftly_template_HasMemoryCost;
46

            
47
use super::binding::CircuitBinding;
48

            
49
/// Type for the body of a relay cell.
50
#[cfg_attr(feature = "bench", visibility::make(pub))]
51
#[derive(Clone, derive_more::From, derive_more::Into)]
52
pub(crate) struct RelayCellBody(BoxedCellBody);
53

            
54
impl AsRef<[u8]> for RelayCellBody {
55
1810
    fn as_ref(&self) -> &[u8] {
56
1810
        &self.0[..]
57
1810
    }
58
}
59
impl AsMut<[u8]> for RelayCellBody {
60
4150
    fn as_mut(&mut self) -> &mut [u8] {
61
4150
        &mut self.0[..]
62
4150
    }
63
}
64

            
65
/// Represents the ability for one hop of a circuit's cryptographic state to be
66
/// initialized from a given seed.
67
#[cfg_attr(feature = "bench", visibility::make(pub))]
68
pub(crate) trait CryptInit: Sized {
69
    /// Return the number of bytes that this state will require.
70
    fn seed_len() -> usize;
71
    /// Construct this state from a seed of the appropriate length.
72
    fn initialize(seed: &[u8]) -> Result<Self>;
73
    /// Initialize this object from a key generator.
74
60
    fn construct<K: super::handshake::KeyGenerator>(keygen: K) -> Result<Self> {
75
60
        let seed = keygen.expand(Self::seed_len())?;
76
60
        Self::initialize(&seed[..])
77
60
    }
78
}
79

            
80
/// A paired object containing the inbound and outbound cryptographic layers
81
/// used by a client to communicate with a single hop on one of its circuits.
82
///
83
/// TODO: Maybe we should fold this into CryptInit.
84
#[cfg_attr(feature = "bench", visibility::make(pub))]
85
pub(crate) trait ClientLayer<F, B>
86
where
87
    F: OutboundClientLayer,
88
    B: InboundClientLayer,
89
{
90
    /// Consume this ClientLayer and return a paired forward and reverse
91
    /// crypto layer, and a [`CircuitBinding`] object
92
    fn split_client_layer(self) -> (F, B, CircuitBinding);
93
}
94

            
95
/// A paired object containing the inbound and outbound cryptographic layers
96
/// used by a relay to implement a client's circuits.
97
///
98
#[allow(dead_code)] // To be used by relays.
99
#[cfg_attr(feature = "bench", visibility::make(pub))]
100
pub(crate) trait RelayLayer<F, B>
101
where
102
    F: OutboundRelayLayer,
103
    B: InboundRelayLayer,
104
{
105
    /// Consume this ClientLayer and return a paired forward and reverse
106
    /// crypto layers, and a [`CircuitBinding`] object
107
    fn split_relay_layer(self) -> (F, B, CircuitBinding);
108
}
109

            
110
/// Represents a relay's view of the inbound crypto state on a given circuit.
111
#[allow(dead_code)] // Relays are not yet implemented.
112
#[cfg_attr(feature = "bench", visibility::make(pub))]
113
pub(crate) trait InboundRelayLayer {
114
    /// Prepare a RelayCellBody to be sent towards the client,
115
    /// and encrypt it.
116
    ///
117
    /// Return the authentication tag.
118
    fn originate(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag;
119
    /// Encrypt a RelayCellBody that is moving towards the client.
120
    fn encrypt_inbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody);
121
}
122

            
123
/// Represent a relay's view of the outbound crypto state on a given circuit.
124
#[allow(dead_code)]
125
#[cfg_attr(feature = "bench", visibility::make(pub))]
126
pub(crate) trait OutboundRelayLayer {
127
    /// Decrypt a RelayCellBody that is moving towards the client.
128
    ///
129
    /// Return an authentication tag if it is addressed to us.
130
    fn decrypt_outbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag>;
131
}
132

            
133
/// A client's view of the cryptographic state shared with a single relay on a
134
/// circuit, as used for outbound cells.
135
#[cfg_attr(feature = "bench", visibility::make(pub))]
136
pub(crate) trait OutboundClientLayer {
137
    /// Prepare a RelayCellBody to be sent to the relay at this layer, and
138
    /// encrypt it.
139
    ///
140
    /// Return the authentication tag.
141
    fn originate_for(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag;
142
    /// Encrypt a RelayCellBody to be decrypted by this layer.
143
    fn encrypt_outbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody);
144
}
145

            
146
/// A client's view of the crypto state shared with a single relay on a circuit,
147
/// as used for inbound cells.
148
#[cfg_attr(feature = "bench", visibility::make(pub))]
149
pub(crate) trait InboundClientLayer {
150
    /// Decrypt a CellBody that passed through this layer.
151
    ///
152
    /// Return an authentication tag if this layer is the originator.
153
    fn decrypt_inbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag>;
154
}
155

            
156
/// Type to store hop indices on a circuit.
157
///
158
/// Hop indices are zero-based: "0" denotes the first hop on the circuit.
159
#[derive(Copy, Clone, Eq, PartialEq, Debug, Deftly)]
160
#[derive_deftly(HasMemoryCost)]
161
pub struct HopNum(u8);
162

            
163
impl HopNum {
164
    /// Return an object that implements [`Display`](std::fmt::Display) for printing `HopNum`s.
165
    ///
166
    /// This will display the `HopNum` as a 1-indexed value (the string representation of the first
167
    /// hop is `"#1"`).
168
    ///
169
    /// To display the zero-based underlying representation of the `HopNum`, use
170
    /// [`Debug`](std::fmt::Debug).
171
52
    pub fn display(&self) -> HopNumDisplay {
172
52
        HopNumDisplay(*self)
173
52
    }
174
}
175

            
176
/// A helper for displaying [`HopNum`]s.
177
///
178
/// The [`Display`](std::fmt::Display) of this type displays the `HopNum` as a 1-based index
179
/// prefixed with the number sign (`#`). For example, the string representation of the first hop is
180
/// `"#1"`.
181
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
182
pub struct HopNumDisplay(HopNum);
183

            
184
impl std::fmt::Display for HopNumDisplay {
185
52
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
186
52
        let hop_num: u8 = self.0.into();
187
52

            
188
52
        write!(f, "#{}", hop_num + 1)
189
52
    }
190
}
191

            
192
impl From<HopNum> for u8 {
193
412
    fn from(hop: HopNum) -> u8 {
194
412
        hop.0
195
412
    }
196
}
197

            
198
impl From<u8> for HopNum {
199
12512
    fn from(v: u8) -> HopNum {
200
12512
        HopNum(v)
201
12512
    }
202
}
203

            
204
impl From<HopNum> for usize {
205
7298
    fn from(hop: HopNum) -> usize {
206
7298
        hop.0 as usize
207
7298
    }
208
}
209

            
210
/// A client's view of the cryptographic state for an entire
211
/// constructed circuit, as used for sending cells.
212
#[cfg_attr(feature = "bench", visibility::make(pub), derive(Default))]
213
pub(crate) struct OutboundClientCrypt {
214
    /// Vector of layers, one for each hop on the circuit, ordered from the
215
    /// closest hop to the farthest.
216
    layers: Vec<Box<dyn OutboundClientLayer + Send>>,
217
}
218

            
219
/// A client's view of the cryptographic state for an entire
220
/// constructed circuit, as used for receiving cells.
221
#[cfg_attr(feature = "bench", visibility::make(pub), derive(Default))]
222
pub(crate) struct InboundClientCrypt {
223
    /// Vector of layers, one for each hop on the circuit, ordered from the
224
    /// closest hop to the farthest.
225
    layers: Vec<Box<dyn InboundClientLayer + Send>>,
226
}
227

            
228
impl OutboundClientCrypt {
229
    /// Return a new (empty) OutboundClientCrypt.
230
200
    #[cfg_attr(feature = "bench", visibility::make(pub))]
231
200
    pub(crate) fn new() -> Self {
232
200
        OutboundClientCrypt { layers: Vec::new() }
233
200
    }
234
    /// Prepare a cell body to sent away from the client.
235
    ///
236
    /// The cell is prepared for the `hop`th hop, and then encrypted with
237
    /// the appropriate keys.
238
    ///
239
    /// On success, returns a reference to tag that should be expected
240
    /// for an authenticated SENDME sent in response to this cell.
241
3874
    #[cfg_attr(feature = "bench", visibility::make(pub))]
242
3874
    pub(crate) fn encrypt(
243
3874
        &mut self,
244
3874
        cmd: ChanCmd,
245
3874
        cell: &mut RelayCellBody,
246
3874
        hop: HopNum,
247
3874
    ) -> Result<SendmeTag> {
248
3874
        let hop: usize = hop.into();
249
3874
        if hop >= self.layers.len() {
250
2
            return Err(Error::NoSuchHop);
251
3872
        }
252
3872

            
253
3872
        let mut layers = self.layers.iter_mut().take(hop + 1).rev();
254
3872
        let first_layer = layers.next().ok_or(Error::NoSuchHop)?;
255
3872
        let tag = first_layer.originate_for(cmd, cell);
256
11352
        for layer in layers {
257
7480
            layer.encrypt_outbound(cmd, cell);
258
7480
        }
259
3872
        Ok(tag)
260
3874
    }
261

            
262
    /// Add a new layer to this OutboundClientCrypt
263
528
    pub(crate) fn add_layer(&mut self, layer: Box<dyn OutboundClientLayer + Send>) {
264
528
        assert!(self.layers.len() < u8::MAX as usize);
265
528
        self.layers.push(layer);
266
528
    }
267

            
268
    /// Return the number of layers configured on this OutboundClientCrypt.
269
50
    pub(crate) fn n_layers(&self) -> usize {
270
50
        self.layers.len()
271
50
    }
272
}
273

            
274
impl InboundClientCrypt {
275
    /// Return a new (empty) InboundClientCrypt.
276
172
    #[cfg_attr(feature = "bench", visibility::make(pub))]
277
172
    pub(crate) fn new() -> Self {
278
172
        InboundClientCrypt { layers: Vec::new() }
279
172
    }
280
    /// Decrypt an incoming cell that is coming to the client.
281
    ///
282
    /// On success, return which hop was the originator of the cell.
283
    // TODO(nickm): Use a real type for the tag, not just `&[u8]`.
284
1028
    #[cfg_attr(feature = "bench", visibility::make(pub))]
285
1028
    pub(crate) fn decrypt(
286
1028
        &mut self,
287
1028
        cmd: ChanCmd,
288
1028
        cell: &mut RelayCellBody,
289
1028
    ) -> Result<(HopNum, SendmeTag)> {
290
2830
        for (hopnum, layer) in self.layers.iter_mut().enumerate() {
291
2830
            if let Some(tag) = layer.decrypt_inbound(cmd, cell) {
292
1026
                let hopnum = HopNum(u8::try_from(hopnum).expect("Somehow > 255 hops"));
293
1026
                return Ok((hopnum, tag));
294
1804
            }
295
        }
296
2
        Err(Error::BadCellAuth)
297
1028
    }
298
    /// Add a new layer to this InboundClientCrypt
299
444
    pub(crate) fn add_layer(&mut self, layer: Box<dyn InboundClientLayer + Send>) {
300
444
        assert!(self.layers.len() < u8::MAX as usize);
301
444
        self.layers.push(layer);
302
444
    }
303

            
304
    /// Return the number of layers configured on this InboundClientCrypt.
305
    ///
306
    /// TODO: use HopNum
307
    #[allow(dead_code)]
308
2
    pub(crate) fn n_layers(&self) -> usize {
309
2
        self.layers.len()
310
2
    }
311
}
312

            
313
/// Standard Tor relay crypto, as instantiated for RELAY cells.
314
pub(crate) type Tor1RelayCrypto =
315
    tor1::CryptStatePair<tor_llcrypto::cipher::aes::Aes128Ctr, tor_llcrypto::d::Sha1>;
316

            
317
/// Standard Tor relay crypto, as instantiated for the HSv3 protocol.
318
///
319
/// (The use of SHA3 is ridiculously overkill.)
320
#[cfg(feature = "hs-common")]
321
pub(crate) type Tor1Hsv3RelayCrypto =
322
    tor1::CryptStatePair<tor_llcrypto::cipher::aes::Aes256Ctr, tor_llcrypto::d::Sha3_256>;
323

            
324
#[cfg(test)]
325
mod test {
326
    // @@ begin test lint list maintained by maint/add_warning @@
327
    #![allow(clippy::bool_assert_comparison)]
328
    #![allow(clippy::clone_on_copy)]
329
    #![allow(clippy::dbg_macro)]
330
    #![allow(clippy::mixed_attributes_style)]
331
    #![allow(clippy::print_stderr)]
332
    #![allow(clippy::print_stdout)]
333
    #![allow(clippy::single_char_pattern)]
334
    #![allow(clippy::unwrap_used)]
335
    #![allow(clippy::unchecked_duration_subtraction)]
336
    #![allow(clippy::useless_vec)]
337
    #![allow(clippy::needless_pass_by_value)]
338
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
339

            
340
    use super::*;
341
    use rand::{seq::IndexedRandom as _, RngCore};
342
    use tor_basic_utils::{test_rng::testing_rng, RngExt as _};
343
    use tor_bytes::SecretBuf;
344
    use tor_cell::relaycell::RelayCellFormat;
345

            
346
    pub(crate) fn add_layers(
347
        cc_out: &mut OutboundClientCrypt,
348
        cc_in: &mut InboundClientCrypt,
349
        pair: Tor1RelayCrypto,
350
    ) {
351
        let (outbound, inbound, _) = pair.split_client_layer();
352
        cc_out.add_layer(Box::new(outbound));
353
        cc_in.add_layer(Box::new(inbound));
354
    }
355

            
356
    #[test]
357
    fn roundtrip() {
358
        // Take canned keys and make sure we can do crypto correctly.
359
        use crate::crypto::handshake::ShakeKeyGenerator as KGen;
360
        fn s(seed: &[u8]) -> SecretBuf {
361
            seed.to_vec().into()
362
        }
363

            
364
        let seed1 = s(b"hidden we are free");
365
        let seed2 = s(b"free to speak, to free ourselves");
366
        let seed3 = s(b"free to hide no more");
367

            
368
        let mut cc_out = OutboundClientCrypt::new();
369
        let mut cc_in = InboundClientCrypt::new();
370
        let pair = Tor1RelayCrypto::construct(KGen::new(seed1.clone())).unwrap();
371
        add_layers(&mut cc_out, &mut cc_in, pair);
372
        let pair = Tor1RelayCrypto::construct(KGen::new(seed2.clone())).unwrap();
373
        add_layers(&mut cc_out, &mut cc_in, pair);
374
        let pair = Tor1RelayCrypto::construct(KGen::new(seed3.clone())).unwrap();
375
        add_layers(&mut cc_out, &mut cc_in, pair);
376

            
377
        assert_eq!(cc_in.n_layers(), 3);
378
        assert_eq!(cc_out.n_layers(), 3);
379

            
380
        let (mut r1f, mut r1b, _) = Tor1RelayCrypto::construct(KGen::new(seed1))
381
            .unwrap()
382
            .split_relay_layer();
383
        let (mut r2f, mut r2b, _) = Tor1RelayCrypto::construct(KGen::new(seed2))
384
            .unwrap()
385
            .split_relay_layer();
386
        let (mut r3f, mut r3b, _) = Tor1RelayCrypto::construct(KGen::new(seed3))
387
            .unwrap()
388
            .split_relay_layer();
389
        let cmd = ChanCmd::RELAY;
390

            
391
        let mut rng = testing_rng();
392
        for _ in 1..300 {
393
            // outbound cell
394
            let mut cell = Box::new([0_u8; 509]);
395
            let mut cell_orig = [0_u8; 509];
396
            rng.fill_bytes(&mut cell_orig);
397
            cell.copy_from_slice(&cell_orig);
398
            let mut cell = cell.into();
399
            let _tag = cc_out.encrypt(cmd, &mut cell, 2.into()).unwrap();
400
            assert_ne!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
401
            assert!(r1f.decrypt_outbound(cmd, &mut cell).is_none());
402
            assert!(r2f.decrypt_outbound(cmd, &mut cell).is_none());
403
            assert!(r3f.decrypt_outbound(cmd, &mut cell).is_some());
404

            
405
            assert_eq!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
406

            
407
            // inbound cell
408
            let mut cell = Box::new([0_u8; 509]);
409
            let mut cell_orig = [0_u8; 509];
410
            rng.fill_bytes(&mut cell_orig);
411
            cell.copy_from_slice(&cell_orig);
412
            let mut cell = cell.into();
413

            
414
            r3b.originate(cmd, &mut cell);
415
            r2b.encrypt_inbound(cmd, &mut cell);
416
            r1b.encrypt_inbound(cmd, &mut cell);
417
            let (layer, _tag) = cc_in.decrypt(cmd, &mut cell).unwrap();
418
            assert_eq!(layer, 2.into());
419
            assert_eq!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
420

            
421
            // TODO: Test tag somehow.
422
        }
423

            
424
        // Try a failure: sending a cell to a nonexistent hop.
425
        {
426
            let mut cell = Box::new([0_u8; 509]).into();
427
            let err = cc_out.encrypt(cmd, &mut cell, 10.into());
428
            assert!(matches!(err, Err(Error::NoSuchHop)));
429
        }
430

            
431
        // Try a failure: A junk cell with no correct auth from any layer.
432
        {
433
            let mut cell = Box::new([0_u8; 509]).into();
434
            let err = cc_in.decrypt(cmd, &mut cell);
435
            assert!(matches!(err, Err(Error::BadCellAuth)));
436
        }
437
    }
438

            
439
    #[test]
440
    fn hop_num_display() {
441
        for i in 0..10 {
442
            let hop_num = HopNum::from(i);
443
            let expect = format!("#{}", i + 1);
444

            
445
            assert_eq!(expect, hop_num.display().to_string());
446
        }
447
    }
448

            
449
    /// Helper: Clear every field in the tor1 `cell` that is reserved for cryptography by relay cell
450
    /// format `version.
451
    ///
452
    /// We do this so that we can be sure that the _other_ fields have all been transmitted correctly.
453
    fn clean_cell_fields(cell: &mut RelayCellBody, format: RelayCellFormat) {
454
        use super::tor1;
455
        match format {
456
            RelayCellFormat::V0 => {
457
                cell.0[tor1::RECOGNIZED_RANGE].fill(0);
458
                cell.0[tor1::DIGEST_RANGE].fill(0);
459
            }
460
            RelayCellFormat::V1 => {
461
                cell.0[0..16].fill(0);
462
            }
463
            _ => {
464
                panic!("Unrecognized format!");
465
            }
466
        }
467
    }
468

            
469
    /// Helper: Test a single-hop message, forward from the client.
470
    fn test_fwd_one_hop<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
471
    where
472
        CS: CryptInit + ClientLayer<CF, CB>,
473
        RS: CryptInit + RelayLayer<RF, RB>,
474
        CF: OutboundClientLayer,
475
        CB: InboundClientLayer,
476
        RF: OutboundRelayLayer,
477
        RB: InboundRelayLayer,
478
    {
479
        let mut rng = testing_rng();
480
        assert_eq!(CS::seed_len(), RS::seed_len());
481
        let mut seed = vec![0; CS::seed_len()];
482
        rng.fill_bytes(&mut seed[..]);
483
        let (mut client, _, _) = CS::initialize(&seed).unwrap().split_client_layer();
484
        let (mut relay, _, _) = RS::initialize(&seed).unwrap().split_relay_layer();
485

            
486
        for _ in 0..5 {
487
            let mut cell = RelayCellBody(Box::new([0_u8; 509]));
488
            rng.fill_bytes(&mut cell.0[..]);
489
            clean_cell_fields(&mut cell, format);
490
            let msg_orig = cell.clone();
491

            
492
            let ctag = client.originate_for(ChanCmd::RELAY, &mut cell);
493
            assert_ne!(cell.0[16..], msg_orig.0[16..]);
494
            let rtag = relay.decrypt_outbound(ChanCmd::RELAY, &mut cell);
495
            clean_cell_fields(&mut cell, format);
496
            assert_eq!(cell.0[..], msg_orig.0[..]);
497
            assert_eq!(rtag, Some(ctag));
498
        }
499
    }
500

            
501
    /// Helper: Test a single-hop message, backwards towards the client.
502
    fn test_rev_one_hop<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
503
    where
504
        CS: CryptInit + ClientLayer<CF, CB>,
505
        RS: CryptInit + RelayLayer<RF, RB>,
506
        CF: OutboundClientLayer,
507
        CB: InboundClientLayer,
508
        RF: OutboundRelayLayer,
509
        RB: InboundRelayLayer,
510
    {
511
        let mut rng = testing_rng();
512
        assert_eq!(CS::seed_len(), RS::seed_len());
513
        let mut seed = vec![0; CS::seed_len()];
514
        rng.fill_bytes(&mut seed[..]);
515
        let (_, mut client, _) = CS::initialize(&seed).unwrap().split_client_layer();
516
        let (_, mut relay, _) = RS::initialize(&seed).unwrap().split_relay_layer();
517

            
518
        for _ in 0..5 {
519
            let mut cell = RelayCellBody(Box::new([0_u8; 509]));
520
            rng.fill_bytes(&mut cell.0[..]);
521
            clean_cell_fields(&mut cell, format);
522
            let msg_orig = cell.clone();
523

            
524
            let rtag = relay.originate(ChanCmd::RELAY, &mut cell);
525
            assert_ne!(cell.0[16..], msg_orig.0[16..]);
526
            let ctag = client.decrypt_inbound(ChanCmd::RELAY, &mut cell);
527
            clean_cell_fields(&mut cell, format);
528
            assert_eq!(cell.0[..], msg_orig.0[..]);
529
            assert_eq!(ctag, Some(rtag));
530
        }
531
    }
532

            
533
    fn test_fwd_three_hops_leaky<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
534
    where
535
        CS: CryptInit + ClientLayer<CF, CB>,
536
        RS: CryptInit + RelayLayer<RF, RB>,
537
        CF: OutboundClientLayer + Send + 'static,
538
        CB: InboundClientLayer,
539
        RF: OutboundRelayLayer,
540
        RB: InboundRelayLayer,
541
    {
542
        let mut rng = testing_rng();
543
        assert_eq!(CS::seed_len(), RS::seed_len());
544
        let mut client = OutboundClientCrypt::new();
545
        let mut relays = Vec::new();
546
        for _ in 0..3 {
547
            let mut seed = vec![0; CS::seed_len()];
548
            rng.fill_bytes(&mut seed[..]);
549
            let (client_layer, _, _) = CS::initialize(&seed).unwrap().split_client_layer();
550
            let (relay_layer, _, _) = RS::initialize(&seed).unwrap().split_relay_layer();
551
            client.add_layer(Box::new(client_layer));
552
            relays.push(relay_layer);
553
        }
554

            
555
        'cell_loop: for _ in 0..32 {
556
            let mut cell = RelayCellBody(Box::new([0_u8; 509]));
557
            rng.fill_bytes(&mut cell.0[..]);
558
            clean_cell_fields(&mut cell, format);
559
            let msg_orig = cell.clone();
560
            let cmd = *[ChanCmd::RELAY, ChanCmd::RELAY_EARLY]
561
                .choose(&mut rng)
562
                .unwrap();
563
            let hop: u8 = rng.gen_range_checked(0_u8..=2).unwrap();
564

            
565
            let ctag = client.encrypt(cmd, &mut cell, hop.into()).unwrap();
566

            
567
            for r_idx in 0..=hop {
568
                let rtag = relays[r_idx as usize].decrypt_outbound(cmd, &mut cell);
569
                if let Some(rtag) = rtag {
570
                    clean_cell_fields(&mut cell, format);
571
                    assert_eq!(cell.0[..], msg_orig.0[..]);
572
                    assert_eq!(rtag, ctag);
573
                    continue 'cell_loop;
574
                }
575
            }
576
            panic!("None of the relays thought that this cell was recognized!");
577
        }
578
    }
579

            
580
    fn test_rev_three_hops_leaky<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
581
    where
582
        CS: CryptInit + ClientLayer<CF, CB>,
583
        RS: CryptInit + RelayLayer<RF, RB>,
584
        CF: OutboundClientLayer,
585
        CB: InboundClientLayer + Send + 'static,
586
        RF: OutboundRelayLayer,
587
        RB: InboundRelayLayer,
588
    {
589
        let mut rng = testing_rng();
590
        assert_eq!(CS::seed_len(), RS::seed_len());
591
        let mut client = InboundClientCrypt::new();
592
        let mut relays = Vec::new();
593
        for _ in 0..3 {
594
            let mut seed = vec![0; CS::seed_len()];
595
            rng.fill_bytes(&mut seed[..]);
596
            let (_, client_layer, _) = CS::initialize(&seed).unwrap().split_client_layer();
597
            let (_, relay_layer, _) = RS::initialize(&seed).unwrap().split_relay_layer();
598
            client.add_layer(Box::new(client_layer));
599
            relays.push(relay_layer);
600
        }
601

            
602
        for _ in 0..32 {
603
            let mut cell = RelayCellBody(Box::new([0_u8; 509]));
604
            rng.fill_bytes(&mut cell.0[..]);
605
            clean_cell_fields(&mut cell, format);
606
            let msg_orig = cell.clone();
607
            let cmd = *[ChanCmd::RELAY, ChanCmd::RELAY_EARLY]
608
                .choose(&mut rng)
609
                .unwrap();
610
            let hop: u8 = rng.gen_range_checked(0_u8..=2).unwrap();
611

            
612
            let rtag = relays[hop as usize].originate(cmd, &mut cell);
613
            for r_idx in (0..hop.into()).rev() {
614
                relays[r_idx as usize].encrypt_inbound(cmd, &mut cell);
615
            }
616

            
617
            let (observed_hop, ctag) = client.decrypt(cmd, &mut cell).unwrap();
618
            assert_eq!(observed_hop, hop.into());
619
            clean_cell_fields(&mut cell, format);
620
            assert_eq!(cell.0[..], msg_orig.0[..]);
621
            assert_eq!(ctag, rtag);
622
        }
623
    }
624

            
625
    macro_rules! integration_tests { { $modname:ident($fmt:expr, $ctype:ty, $rtype:ty) } => {
626
        mod $modname {
627
            use super::*;
628
            #[test]
629
            fn test_fwd_one_hop() {
630
                super::test_fwd_one_hop::<$ctype, $rtype, _, _, _, _>($fmt);
631
            }
632
            #[test]
633
            fn test_rev_one_hop() {
634
                super::test_rev_one_hop::<$ctype, $rtype, _, _, _, _>($fmt);
635
            }
636
            #[test]
637
            fn test_fwd_three_hops_leaky() {
638
                super::test_fwd_three_hops_leaky::<$ctype, $rtype, _, _, _, _>($fmt);
639
            }
640
            #[test]
641
            fn test_rev_three_hops_leaky() {
642
                super::test_rev_three_hops_leaky::<$ctype, $rtype, _, _, _, _>($fmt);
643
            }
644
        }
645
    }}
646

            
647
    integration_tests! { tor1(RelayCellFormat::V0, Tor1RelayCrypto, Tor1RelayCrypto) }
648
    #[cfg(feature = "hs-common")]
649
    integration_tests! { tor1_hs(RelayCellFormat::V0, Tor1Hsv3RelayCrypto, Tor1Hsv3RelayCrypto) }
650

            
651
    #[cfg(feature = "counter-galois-onion")]
652
    integration_tests! {
653
        cgo_aes128(RelayCellFormat::V1,
654
            cgo::CryptStatePair<aes::Aes128Dec, aes::Aes128Enc>,// client
655
            cgo::CryptStatePair<aes::Aes128Enc, aes::Aes128Enc> // relay
656
        )
657
    }
658
    #[cfg(feature = "counter-galois-onion")]
659
    integration_tests! {
660
        cgo_aes256(RelayCellFormat::V1,
661
            cgo::CryptStatePair<aes::Aes256Dec, aes::Aes256Enc>,// client
662
            cgo::CryptStatePair<aes::Aes256Enc, aes::Aes256Enc> // relay
663
        )
664
    }
665
}