tor_proto/crypto/
cell.rs

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")]
34pub(crate) mod bench_utils;
35#[cfg(feature = "counter-galois-onion")]
36pub(crate) mod cgo;
37pub(crate) mod tor1;
38
39use crate::{Error, Result};
40use derive_deftly::Deftly;
41use tor_cell::{
42    chancell::{BoxedCellBody, ChanCmd},
43    relaycell::msg::SendmeTag,
44};
45use tor_memquota::derive_deftly_template_HasMemoryCost;
46
47use super::binding::CircuitBinding;
48
49/// Type for the body of a relay cell.
50#[derive(Clone, derive_more::From, derive_more::Into)]
51pub(crate) struct RelayCellBody(BoxedCellBody);
52
53impl AsRef<[u8]> for RelayCellBody {
54    fn as_ref(&self) -> &[u8] {
55        &self.0[..]
56    }
57}
58impl AsMut<[u8]> for RelayCellBody {
59    fn as_mut(&mut self) -> &mut [u8] {
60        &mut self.0[..]
61    }
62}
63
64/// Represents the ability for one hop of a circuit's cryptographic state to be
65/// initialized from a given seed.
66pub(crate) trait CryptInit: Sized {
67    /// Return the number of bytes that this state will require.
68    fn seed_len() -> usize;
69    /// Construct this state from a seed of the appropriate length.
70    fn initialize(seed: &[u8]) -> Result<Self>;
71    /// Initialize this object from a key generator.
72    fn construct<K: super::handshake::KeyGenerator>(keygen: K) -> Result<Self> {
73        let seed = keygen.expand(Self::seed_len())?;
74        Self::initialize(&seed[..])
75    }
76}
77
78/// A paired object containing the inbound and outbound cryptographic layers
79/// used by a client to communicate with a single hop on one of its circuits.
80///
81/// TODO: Maybe we should fold this into CryptInit.
82pub(crate) trait ClientLayer<F, B>
83where
84    F: OutboundClientLayer,
85    B: InboundClientLayer,
86{
87    /// Consume this ClientLayer and return a paired forward and reverse
88    /// crypto layer, and a [`CircuitBinding`] object
89    fn split_client_layer(self) -> (F, B, CircuitBinding);
90}
91
92/// A paired object containing the inbound and outbound cryptographic layers
93/// used by a relay to implement a client's circuits.
94///
95#[allow(dead_code)] // To be used by relays.
96pub(crate) trait RelayLayer<F, B>
97where
98    F: OutboundRelayLayer,
99    B: InboundRelayLayer,
100{
101    /// Consume this ClientLayer and return a paired forward and reverse
102    /// crypto layers, and a [`CircuitBinding`] object
103    fn split_relay_layer(self) -> (F, B, CircuitBinding);
104}
105
106/// Represents a relay's view of the inbound crypto state on a given circuit.
107#[allow(dead_code)] // Relays are not yet implemented.
108pub(crate) trait InboundRelayLayer {
109    /// Prepare a RelayCellBody to be sent towards the client,
110    /// and encrypt it.
111    ///
112    /// Return the authentication tag.
113    fn originate(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag;
114    /// Encrypt a RelayCellBody that is moving towards the client.
115    fn encrypt_inbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody);
116}
117
118/// Represent a relay's view of the outbound crypto state on a given circuit.
119#[allow(dead_code)]
120pub(crate) trait OutboundRelayLayer {
121    /// Decrypt a RelayCellBody that is moving towards the client.
122    ///
123    /// Return an authentication tag if it is addressed to us.
124    fn decrypt_outbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag>;
125}
126
127/// A client's view of the cryptographic state shared with a single relay on a
128/// circuit, as used for outbound cells.
129pub(crate) trait OutboundClientLayer {
130    /// Prepare a RelayCellBody to be sent to the relay at this layer, and
131    /// encrypt it.
132    ///
133    /// Return the authentication tag.
134    fn originate_for(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag;
135    /// Encrypt a RelayCellBody to be decrypted by this layer.
136    fn encrypt_outbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody);
137}
138
139/// A client's view of the crypto state shared with a single relay on a circuit,
140/// as used for inbound cells.
141pub(crate) trait InboundClientLayer {
142    /// Decrypt a CellBody that passed through this layer.
143    ///
144    /// Return an authentication tag if this layer is the originator.
145    fn decrypt_inbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag>;
146}
147
148/// Type to store hop indices on a circuit.
149///
150/// Hop indices are zero-based: "0" denotes the first hop on the circuit.
151#[derive(Copy, Clone, Eq, PartialEq, Debug, Deftly)]
152#[derive_deftly(HasMemoryCost)]
153pub struct HopNum(u8);
154
155impl HopNum {
156    /// Return an object that implements [`Display`](std::fmt::Display) for printing `HopNum`s.
157    ///
158    /// This will display the `HopNum` as a 1-indexed value (the string representation of the first
159    /// hop is `"#1"`).
160    ///
161    /// To display the zero-based underlying representation of the `HopNum`, use
162    /// [`Debug`](std::fmt::Debug).
163    pub fn display(&self) -> HopNumDisplay {
164        HopNumDisplay(*self)
165    }
166}
167
168/// A helper for displaying [`HopNum`]s.
169///
170/// The [`Display`](std::fmt::Display) of this type displays the `HopNum` as a 1-based index
171/// prefixed with the number sign (`#`). For example, the string representation of the first hop is
172/// `"#1"`.
173#[derive(Copy, Clone, Eq, PartialEq, Debug)]
174pub struct HopNumDisplay(HopNum);
175
176impl std::fmt::Display for HopNumDisplay {
177    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
178        let hop_num: u8 = self.0.into();
179
180        write!(f, "#{}", hop_num + 1)
181    }
182}
183
184impl From<HopNum> for u8 {
185    fn from(hop: HopNum) -> u8 {
186        hop.0
187    }
188}
189
190impl From<u8> for HopNum {
191    fn from(v: u8) -> HopNum {
192        HopNum(v)
193    }
194}
195
196impl From<HopNum> for usize {
197    fn from(hop: HopNum) -> usize {
198        hop.0 as usize
199    }
200}
201
202/// A client's view of the cryptographic state for an entire
203/// constructed circuit, as used for sending cells.
204pub(crate) struct OutboundClientCrypt {
205    /// Vector of layers, one for each hop on the circuit, ordered from the
206    /// closest hop to the farthest.
207    layers: Vec<Box<dyn OutboundClientLayer + Send>>,
208}
209
210/// A client's view of the cryptographic state for an entire
211/// constructed circuit, as used for receiving cells.
212pub(crate) struct InboundClientCrypt {
213    /// Vector of layers, one for each hop on the circuit, ordered from the
214    /// closest hop to the farthest.
215    layers: Vec<Box<dyn InboundClientLayer + Send>>,
216}
217
218impl OutboundClientCrypt {
219    /// Return a new (empty) OutboundClientCrypt.
220    pub(crate) fn new() -> Self {
221        OutboundClientCrypt { layers: Vec::new() }
222    }
223    /// Prepare a cell body to sent away from the client.
224    ///
225    /// The cell is prepared for the `hop`th hop, and then encrypted with
226    /// the appropriate keys.
227    ///
228    /// On success, returns a reference to tag that should be expected
229    /// for an authenticated SENDME sent in response to this cell.
230    pub(crate) fn encrypt(
231        &mut self,
232        cmd: ChanCmd,
233        cell: &mut RelayCellBody,
234        hop: HopNum,
235    ) -> Result<SendmeTag> {
236        let hop: usize = hop.into();
237        if hop >= self.layers.len() {
238            return Err(Error::NoSuchHop);
239        }
240
241        let mut layers = self.layers.iter_mut().take(hop + 1).rev();
242        let first_layer = layers.next().ok_or(Error::NoSuchHop)?;
243        let tag = first_layer.originate_for(cmd, cell);
244        for layer in layers {
245            layer.encrypt_outbound(cmd, cell);
246        }
247        Ok(tag)
248    }
249
250    /// Add a new layer to this OutboundClientCrypt
251    pub(crate) fn add_layer(&mut self, layer: Box<dyn OutboundClientLayer + Send>) {
252        assert!(self.layers.len() < u8::MAX as usize);
253        self.layers.push(layer);
254    }
255
256    /// Return the number of layers configured on this OutboundClientCrypt.
257    pub(crate) fn n_layers(&self) -> usize {
258        self.layers.len()
259    }
260}
261
262impl InboundClientCrypt {
263    /// Return a new (empty) InboundClientCrypt.
264    pub(crate) fn new() -> Self {
265        InboundClientCrypt { layers: Vec::new() }
266    }
267    /// Decrypt an incoming cell that is coming to the client.
268    ///
269    /// On success, return which hop was the originator of the cell.
270    // TODO(nickm): Use a real type for the tag, not just `&[u8]`.
271    pub(crate) fn decrypt(
272        &mut self,
273        cmd: ChanCmd,
274        cell: &mut RelayCellBody,
275    ) -> Result<(HopNum, SendmeTag)> {
276        for (hopnum, layer) in self.layers.iter_mut().enumerate() {
277            if let Some(tag) = layer.decrypt_inbound(cmd, cell) {
278                let hopnum = HopNum(u8::try_from(hopnum).expect("Somehow > 255 hops"));
279                return Ok((hopnum, tag));
280            }
281        }
282        Err(Error::BadCellAuth)
283    }
284    /// Add a new layer to this InboundClientCrypt
285    pub(crate) fn add_layer(&mut self, layer: Box<dyn InboundClientLayer + Send>) {
286        assert!(self.layers.len() < u8::MAX as usize);
287        self.layers.push(layer);
288    }
289
290    /// Return the number of layers configured on this InboundClientCrypt.
291    ///
292    /// TODO: use HopNum
293    #[allow(dead_code)]
294    pub(crate) fn n_layers(&self) -> usize {
295        self.layers.len()
296    }
297}
298
299/// Standard Tor relay crypto, as instantiated for RELAY cells.
300pub(crate) type Tor1RelayCrypto =
301    tor1::CryptStatePair<tor_llcrypto::cipher::aes::Aes128Ctr, tor_llcrypto::d::Sha1>;
302
303/// Standard Tor relay crypto, as instantiated for the HSv3 protocol.
304///
305/// (The use of SHA3 is ridiculously overkill.)
306#[cfg(feature = "hs-common")]
307pub(crate) type Tor1Hsv3RelayCrypto =
308    tor1::CryptStatePair<tor_llcrypto::cipher::aes::Aes256Ctr, tor_llcrypto::d::Sha3_256>;
309
310#[cfg(test)]
311mod test {
312    // @@ begin test lint list maintained by maint/add_warning @@
313    #![allow(clippy::bool_assert_comparison)]
314    #![allow(clippy::clone_on_copy)]
315    #![allow(clippy::dbg_macro)]
316    #![allow(clippy::mixed_attributes_style)]
317    #![allow(clippy::print_stderr)]
318    #![allow(clippy::print_stdout)]
319    #![allow(clippy::single_char_pattern)]
320    #![allow(clippy::unwrap_used)]
321    #![allow(clippy::unchecked_duration_subtraction)]
322    #![allow(clippy::useless_vec)]
323    #![allow(clippy::needless_pass_by_value)]
324    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
325
326    use super::*;
327    use rand::{seq::IndexedRandom as _, RngCore};
328    use tor_basic_utils::{test_rng::testing_rng, RngExt as _};
329    use tor_bytes::SecretBuf;
330    use tor_cell::relaycell::RelayCellFormat;
331
332    pub(crate) fn add_layers(
333        cc_out: &mut OutboundClientCrypt,
334        cc_in: &mut InboundClientCrypt,
335        pair: Tor1RelayCrypto,
336    ) {
337        let (outbound, inbound, _) = pair.split_client_layer();
338        cc_out.add_layer(Box::new(outbound));
339        cc_in.add_layer(Box::new(inbound));
340    }
341
342    #[test]
343    fn roundtrip() {
344        // Take canned keys and make sure we can do crypto correctly.
345        use crate::crypto::handshake::ShakeKeyGenerator as KGen;
346        fn s(seed: &[u8]) -> SecretBuf {
347            seed.to_vec().into()
348        }
349
350        let seed1 = s(b"hidden we are free");
351        let seed2 = s(b"free to speak, to free ourselves");
352        let seed3 = s(b"free to hide no more");
353
354        let mut cc_out = OutboundClientCrypt::new();
355        let mut cc_in = InboundClientCrypt::new();
356        let pair = Tor1RelayCrypto::construct(KGen::new(seed1.clone())).unwrap();
357        add_layers(&mut cc_out, &mut cc_in, pair);
358        let pair = Tor1RelayCrypto::construct(KGen::new(seed2.clone())).unwrap();
359        add_layers(&mut cc_out, &mut cc_in, pair);
360        let pair = Tor1RelayCrypto::construct(KGen::new(seed3.clone())).unwrap();
361        add_layers(&mut cc_out, &mut cc_in, pair);
362
363        assert_eq!(cc_in.n_layers(), 3);
364        assert_eq!(cc_out.n_layers(), 3);
365
366        let (mut r1f, mut r1b, _) = Tor1RelayCrypto::construct(KGen::new(seed1))
367            .unwrap()
368            .split_relay_layer();
369        let (mut r2f, mut r2b, _) = Tor1RelayCrypto::construct(KGen::new(seed2))
370            .unwrap()
371            .split_relay_layer();
372        let (mut r3f, mut r3b, _) = Tor1RelayCrypto::construct(KGen::new(seed3))
373            .unwrap()
374            .split_relay_layer();
375        let cmd = ChanCmd::RELAY;
376
377        let mut rng = testing_rng();
378        for _ in 1..300 {
379            // outbound cell
380            let mut cell = Box::new([0_u8; 509]);
381            let mut cell_orig = [0_u8; 509];
382            rng.fill_bytes(&mut cell_orig);
383            cell.copy_from_slice(&cell_orig);
384            let mut cell = cell.into();
385            let _tag = cc_out.encrypt(cmd, &mut cell, 2.into()).unwrap();
386            assert_ne!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
387            assert!(r1f.decrypt_outbound(cmd, &mut cell).is_none());
388            assert!(r2f.decrypt_outbound(cmd, &mut cell).is_none());
389            assert!(r3f.decrypt_outbound(cmd, &mut cell).is_some());
390
391            assert_eq!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
392
393            // inbound 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
400            r3b.originate(cmd, &mut cell);
401            r2b.encrypt_inbound(cmd, &mut cell);
402            r1b.encrypt_inbound(cmd, &mut cell);
403            let (layer, _tag) = cc_in.decrypt(cmd, &mut cell).unwrap();
404            assert_eq!(layer, 2.into());
405            assert_eq!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
406
407            // TODO: Test tag somehow.
408        }
409
410        // Try a failure: sending a cell to a nonexistent hop.
411        {
412            let mut cell = Box::new([0_u8; 509]).into();
413            let err = cc_out.encrypt(cmd, &mut cell, 10.into());
414            assert!(matches!(err, Err(Error::NoSuchHop)));
415        }
416
417        // Try a failure: A junk cell with no correct auth from any layer.
418        {
419            let mut cell = Box::new([0_u8; 509]).into();
420            let err = cc_in.decrypt(cmd, &mut cell);
421            assert!(matches!(err, Err(Error::BadCellAuth)));
422        }
423    }
424
425    #[test]
426    fn hop_num_display() {
427        for i in 0..10 {
428            let hop_num = HopNum::from(i);
429            let expect = format!("#{}", i + 1);
430
431            assert_eq!(expect, hop_num.display().to_string());
432        }
433    }
434
435    /// Helper: Clear every field in the tor1 `cell` that is reserved for cryptography by relay cell
436    /// format `version.
437    ///
438    /// We do this so that we can be sure that the _other_ fields have all been transmitted correctly.
439    fn clean_cell_fields(cell: &mut RelayCellBody, format: RelayCellFormat) {
440        use super::tor1;
441        match format {
442            RelayCellFormat::V0 => {
443                cell.0[tor1::RECOGNIZED_RANGE].fill(0);
444                cell.0[tor1::DIGEST_RANGE].fill(0);
445            }
446            RelayCellFormat::V1 => {
447                cell.0[0..16].fill(0);
448            }
449            _ => {
450                panic!("Unrecognized format!");
451            }
452        }
453    }
454
455    /// Helper: Test a single-hop message, forward from the client.
456    fn test_fwd_one_hop<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
457    where
458        CS: CryptInit + ClientLayer<CF, CB>,
459        RS: CryptInit + RelayLayer<RF, RB>,
460        CF: OutboundClientLayer,
461        CB: InboundClientLayer,
462        RF: OutboundRelayLayer,
463        RB: InboundRelayLayer,
464    {
465        let mut rng = testing_rng();
466        assert_eq!(CS::seed_len(), RS::seed_len());
467        let mut seed = vec![0; CS::seed_len()];
468        rng.fill_bytes(&mut seed[..]);
469        let (mut client, _, _) = CS::initialize(&seed).unwrap().split_client_layer();
470        let (mut relay, _, _) = RS::initialize(&seed).unwrap().split_relay_layer();
471
472        for _ in 0..5 {
473            let mut cell = RelayCellBody(Box::new([0_u8; 509]));
474            rng.fill_bytes(&mut cell.0[..]);
475            clean_cell_fields(&mut cell, format);
476            let msg_orig = cell.clone();
477
478            let ctag = client.originate_for(ChanCmd::RELAY, &mut cell);
479            assert_ne!(cell.0[16..], msg_orig.0[16..]);
480            let rtag = relay.decrypt_outbound(ChanCmd::RELAY, &mut cell);
481            clean_cell_fields(&mut cell, format);
482            assert_eq!(cell.0[..], msg_orig.0[..]);
483            assert_eq!(rtag, Some(ctag));
484        }
485    }
486
487    /// Helper: Test a single-hop message, backwards towards the client.
488    fn test_rev_one_hop<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
489    where
490        CS: CryptInit + ClientLayer<CF, CB>,
491        RS: CryptInit + RelayLayer<RF, RB>,
492        CF: OutboundClientLayer,
493        CB: InboundClientLayer,
494        RF: OutboundRelayLayer,
495        RB: InboundRelayLayer,
496    {
497        let mut rng = testing_rng();
498        assert_eq!(CS::seed_len(), RS::seed_len());
499        let mut seed = vec![0; CS::seed_len()];
500        rng.fill_bytes(&mut seed[..]);
501        let (_, mut client, _) = CS::initialize(&seed).unwrap().split_client_layer();
502        let (_, mut relay, _) = RS::initialize(&seed).unwrap().split_relay_layer();
503
504        for _ in 0..5 {
505            let mut cell = RelayCellBody(Box::new([0_u8; 509]));
506            rng.fill_bytes(&mut cell.0[..]);
507            clean_cell_fields(&mut cell, format);
508            let msg_orig = cell.clone();
509
510            let rtag = relay.originate(ChanCmd::RELAY, &mut cell);
511            assert_ne!(cell.0[16..], msg_orig.0[16..]);
512            let ctag = client.decrypt_inbound(ChanCmd::RELAY, &mut cell);
513            clean_cell_fields(&mut cell, format);
514            assert_eq!(cell.0[..], msg_orig.0[..]);
515            assert_eq!(ctag, Some(rtag));
516        }
517    }
518
519    fn test_fwd_three_hops_leaky<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
520    where
521        CS: CryptInit + ClientLayer<CF, CB>,
522        RS: CryptInit + RelayLayer<RF, RB>,
523        CF: OutboundClientLayer + Send + 'static,
524        CB: InboundClientLayer,
525        RF: OutboundRelayLayer,
526        RB: InboundRelayLayer,
527    {
528        let mut rng = testing_rng();
529        assert_eq!(CS::seed_len(), RS::seed_len());
530        let mut client = OutboundClientCrypt::new();
531        let mut relays = Vec::new();
532        for _ in 0..3 {
533            let mut seed = vec![0; CS::seed_len()];
534            rng.fill_bytes(&mut seed[..]);
535            let (client_layer, _, _) = CS::initialize(&seed).unwrap().split_client_layer();
536            let (relay_layer, _, _) = RS::initialize(&seed).unwrap().split_relay_layer();
537            client.add_layer(Box::new(client_layer));
538            relays.push(relay_layer);
539        }
540
541        'cell_loop: for _ in 0..32 {
542            let mut cell = RelayCellBody(Box::new([0_u8; 509]));
543            rng.fill_bytes(&mut cell.0[..]);
544            clean_cell_fields(&mut cell, format);
545            let msg_orig = cell.clone();
546            let cmd = *[ChanCmd::RELAY, ChanCmd::RELAY_EARLY]
547                .choose(&mut rng)
548                .unwrap();
549            let hop: u8 = rng.gen_range_checked(0_u8..=2).unwrap();
550
551            let ctag = client.encrypt(cmd, &mut cell, hop.into()).unwrap();
552
553            for r_idx in 0..=hop {
554                let rtag = relays[r_idx as usize].decrypt_outbound(cmd, &mut cell);
555                if let Some(rtag) = rtag {
556                    clean_cell_fields(&mut cell, format);
557                    assert_eq!(cell.0[..], msg_orig.0[..]);
558                    assert_eq!(rtag, ctag);
559                    continue 'cell_loop;
560                }
561            }
562            panic!("None of the relays thought that this cell was recognized!");
563        }
564    }
565
566    fn test_rev_three_hops_leaky<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
567    where
568        CS: CryptInit + ClientLayer<CF, CB>,
569        RS: CryptInit + RelayLayer<RF, RB>,
570        CF: OutboundClientLayer,
571        CB: InboundClientLayer + Send + 'static,
572        RF: OutboundRelayLayer,
573        RB: InboundRelayLayer,
574    {
575        let mut rng = testing_rng();
576        assert_eq!(CS::seed_len(), RS::seed_len());
577        let mut client = InboundClientCrypt::new();
578        let mut relays = Vec::new();
579        for _ in 0..3 {
580            let mut seed = vec![0; CS::seed_len()];
581            rng.fill_bytes(&mut seed[..]);
582            let (_, client_layer, _) = CS::initialize(&seed).unwrap().split_client_layer();
583            let (_, relay_layer, _) = RS::initialize(&seed).unwrap().split_relay_layer();
584            client.add_layer(Box::new(client_layer));
585            relays.push(relay_layer);
586        }
587
588        for _ in 0..32 {
589            let mut cell = RelayCellBody(Box::new([0_u8; 509]));
590            rng.fill_bytes(&mut cell.0[..]);
591            clean_cell_fields(&mut cell, format);
592            let msg_orig = cell.clone();
593            let cmd = *[ChanCmd::RELAY, ChanCmd::RELAY_EARLY]
594                .choose(&mut rng)
595                .unwrap();
596            let hop: u8 = rng.gen_range_checked(0_u8..=2).unwrap();
597
598            let rtag = relays[hop as usize].originate(cmd, &mut cell);
599            for r_idx in (0..hop.into()).rev() {
600                relays[r_idx as usize].encrypt_inbound(cmd, &mut cell);
601            }
602
603            let (observed_hop, ctag) = client.decrypt(cmd, &mut cell).unwrap();
604            assert_eq!(observed_hop, hop.into());
605            clean_cell_fields(&mut cell, format);
606            assert_eq!(cell.0[..], msg_orig.0[..]);
607            assert_eq!(ctag, rtag);
608        }
609    }
610
611    macro_rules! integration_tests { { $modname:ident($fmt:expr, $ctype:ty, $rtype:ty) } => {
612        mod $modname {
613            use super::*;
614            #[test]
615            fn test_fwd_one_hop() {
616                super::test_fwd_one_hop::<$ctype, $rtype, _, _, _, _>($fmt);
617            }
618            #[test]
619            fn test_rev_one_hop() {
620                super::test_rev_one_hop::<$ctype, $rtype, _, _, _, _>($fmt);
621            }
622            #[test]
623            fn test_fwd_three_hops_leaky() {
624                super::test_fwd_three_hops_leaky::<$ctype, $rtype, _, _, _, _>($fmt);
625            }
626            #[test]
627            fn test_rev_three_hops_leaky() {
628                super::test_rev_three_hops_leaky::<$ctype, $rtype, _, _, _, _>($fmt);
629            }
630        }
631    }}
632
633    integration_tests! { tor1(RelayCellFormat::V0, Tor1RelayCrypto, Tor1RelayCrypto) }
634    #[cfg(feature = "hs-common")]
635    integration_tests! { tor1_hs(RelayCellFormat::V0, Tor1Hsv3RelayCrypto, Tor1Hsv3RelayCrypto) }
636
637    #[cfg(feature = "counter-galois-onion")]
638    integration_tests! {
639        cgo_aes128(RelayCellFormat::V1,
640            cgo::CryptStatePair<aes::Aes128Dec, aes::Aes128Enc>,// client
641            cgo::CryptStatePair<aes::Aes128Enc, aes::Aes128Enc> // relay
642        )
643    }
644    #[cfg(feature = "counter-galois-onion")]
645    integration_tests! {
646        cgo_aes256(RelayCellFormat::V1,
647            cgo::CryptStatePair<aes::Aes256Dec, aes::Aes256Enc>,// client
648            cgo::CryptStatePair<aes::Aes256Enc, aes::Aes256Enc> // relay
649        )
650    }
651}