Skip to main content

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