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
33use crate::{Error, Result};
34use derive_deftly::Deftly;
35use tor_cell::chancell::BoxedCellBody;
36use tor_error::internal;
37use tor_memquota::derive_deftly_template_HasMemoryCost;
38
39use digest::generic_array::GenericArray;
40
41use super::binding::CircuitBinding;
42
43/// Type for the body of a relay cell.
44#[derive(Clone, derive_more::From, derive_more::Into)]
45pub(crate) struct RelayCellBody(BoxedCellBody);
46
47impl AsRef<[u8]> for RelayCellBody {
48    fn as_ref(&self) -> &[u8] {
49        &self.0[..]
50    }
51}
52impl AsMut<[u8]> for RelayCellBody {
53    fn as_mut(&mut self) -> &mut [u8] {
54        &mut self.0[..]
55    }
56}
57
58/// Represents the ability for one hop of a circuit's cryptographic state to be
59/// initialized from a given seed.
60pub(crate) trait CryptInit: Sized {
61    /// Return the number of bytes that this state will require.
62    fn seed_len() -> usize;
63    /// Construct this state from a seed of the appropriate length.
64    fn initialize(seed: &[u8]) -> Result<Self>;
65    /// Initialize this object from a key generator.
66    fn construct<K: super::handshake::KeyGenerator>(keygen: K) -> Result<Self> {
67        let seed = keygen.expand(Self::seed_len())?;
68        Self::initialize(&seed[..])
69    }
70}
71
72/// A paired object containing the inbound and outbound cryptographic layers
73/// used by a client to communicate with a single hop on one of its circuits.
74///
75/// TODO: Maybe we should fold this into CryptInit.
76pub(crate) trait ClientLayer<F, B>
77where
78    F: OutboundClientLayer,
79    B: InboundClientLayer,
80{
81    /// Consume this ClientLayer and return a paired forward and reverse
82    /// crypto layer, and a [`CircuitBinding`] object
83    fn split(self) -> (F, B, CircuitBinding);
84}
85
86/// Represents a relay's view of the crypto state on a given circuit.
87#[allow(dead_code)] // TODO #1383 ????
88pub(crate) trait RelayCrypt {
89    /// Prepare a RelayCellBody to be sent towards the client.
90    fn originate(&mut self, cell: &mut RelayCellBody);
91    /// Encrypt a RelayCellBody that is moving towards the client.
92    fn encrypt_inbound(&mut self, cell: &mut RelayCellBody);
93    /// Decrypt a RelayCellBody that is moving towards the client.
94    ///
95    /// Return true if it is addressed to us.
96    fn decrypt_outbound(&mut self, cell: &mut RelayCellBody) -> bool;
97}
98
99/// A client's view of the cryptographic state shared with a single relay on a
100/// circuit, as used for outbound cells.
101pub(crate) trait OutboundClientLayer {
102    /// Prepare a RelayCellBody to be sent to the relay at this layer, and
103    /// encrypt it.
104    ///
105    /// Return the authentication tag.
106    fn originate_for(&mut self, cell: &mut RelayCellBody) -> &[u8];
107    /// Encrypt a RelayCellBody to be decrypted by this layer.
108    fn encrypt_outbound(&mut self, cell: &mut RelayCellBody);
109}
110
111/// A client's view of the crypto state shared with a single relay on a circuit,
112/// as used for inbound cells.
113pub(crate) trait InboundClientLayer {
114    /// Decrypt a CellBody that passed through this layer.
115    ///
116    /// Return an authentication tag if this layer is the originator.
117    fn decrypt_inbound(&mut self, cell: &mut RelayCellBody) -> Option<&[u8]>;
118}
119
120/// Type to store hop indices on a circuit.
121///
122/// Hop indices are zero-based: "0" denotes the first hop on the circuit.
123#[derive(Copy, Clone, Eq, PartialEq, Debug, Deftly)]
124#[derive_deftly(HasMemoryCost)]
125pub struct HopNum(u8);
126
127impl HopNum {
128    /// Return an object that implements [`Display`](std::fmt::Display) for printing `HopNum`s.
129    ///
130    /// This will display the `HopNum` as a 1-indexed value (the string representation of the first
131    /// hop is `"#1"`).
132    ///
133    /// To display the zero-based underlying representation of the `HopNum`, use
134    /// [`Debug`](std::fmt::Debug).
135    pub fn display(&self) -> HopNumDisplay {
136        HopNumDisplay(*self)
137    }
138}
139
140/// A helper for displaying [`HopNum`]s.
141///
142/// The [`Display`](std::fmt::Display) of this type displays the `HopNum` as a 1-based index
143/// prefixed with the number sign (`#`). For example, the string representation of the first hop is
144/// `"#1"`.
145#[derive(Copy, Clone, Eq, PartialEq, Debug)]
146pub struct HopNumDisplay(HopNum);
147
148impl std::fmt::Display for HopNumDisplay {
149    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
150        let hop_num: u8 = self.0.into();
151
152        write!(f, "#{}", hop_num + 1)
153    }
154}
155
156impl From<HopNum> for u8 {
157    fn from(hop: HopNum) -> u8 {
158        hop.0
159    }
160}
161
162impl From<u8> for HopNum {
163    fn from(v: u8) -> HopNum {
164        HopNum(v)
165    }
166}
167
168impl From<HopNum> for usize {
169    fn from(hop: HopNum) -> usize {
170        hop.0 as usize
171    }
172}
173
174/// A client's view of the cryptographic state for an entire
175/// constructed circuit, as used for sending cells.
176pub(crate) struct OutboundClientCrypt {
177    /// Vector of layers, one for each hop on the circuit, ordered from the
178    /// closest hop to the farthest.
179    layers: Vec<Box<dyn OutboundClientLayer + Send>>,
180}
181
182/// A client's view of the cryptographic state for an entire
183/// constructed circuit, as used for receiving cells.
184pub(crate) struct InboundClientCrypt {
185    /// Vector of layers, one for each hop on the circuit, ordered from the
186    /// closest hop to the farthest.
187    layers: Vec<Box<dyn InboundClientLayer + Send>>,
188}
189
190/// The length of the tag that we include (with this algorithm) in an
191/// authenticated SENDME message.
192pub(crate) const SENDME_TAG_LEN: usize = 20;
193
194impl OutboundClientCrypt {
195    /// Return a new (empty) OutboundClientCrypt.
196    pub(crate) fn new() -> Self {
197        OutboundClientCrypt { layers: Vec::new() }
198    }
199    /// Prepare a cell body to sent away from the client.
200    ///
201    /// The cell is prepared for the `hop`th hop, and then encrypted with
202    /// the appropriate keys.
203    ///
204    /// On success, returns a reference to tag that should be expected
205    /// for an authenticated SENDME sent in response to this cell.
206    pub(crate) fn encrypt(
207        &mut self,
208        cell: &mut RelayCellBody,
209        hop: HopNum,
210    ) -> Result<&[u8; SENDME_TAG_LEN]> {
211        let hop: usize = hop.into();
212        if hop >= self.layers.len() {
213            return Err(Error::NoSuchHop);
214        }
215
216        let mut layers = self.layers.iter_mut().take(hop + 1).rev();
217        let first_layer = layers.next().ok_or(Error::NoSuchHop)?;
218        let tag = first_layer.originate_for(cell);
219        for layer in layers {
220            layer.encrypt_outbound(cell);
221        }
222        Ok(tag.try_into().expect("wrong SENDME digest size"))
223    }
224
225    /// Add a new layer to this OutboundClientCrypt
226    pub(crate) fn add_layer(&mut self, layer: Box<dyn OutboundClientLayer + Send>) {
227        assert!(self.layers.len() < u8::MAX as usize);
228        self.layers.push(layer);
229    }
230
231    /// Return the number of layers configured on this OutboundClientCrypt.
232    pub(crate) fn n_layers(&self) -> usize {
233        self.layers.len()
234    }
235}
236
237impl InboundClientCrypt {
238    /// Return a new (empty) InboundClientCrypt.
239    pub(crate) fn new() -> Self {
240        InboundClientCrypt { layers: Vec::new() }
241    }
242    /// Decrypt an incoming cell that is coming to the client.
243    ///
244    /// On success, return which hop was the originator of the cell.
245    // TODO(nickm): Use a real type for the tag, not just `&[u8]`.
246    pub(crate) fn decrypt(&mut self, cell: &mut RelayCellBody) -> Result<(HopNum, &[u8])> {
247        for (hopnum, layer) in self.layers.iter_mut().enumerate() {
248            if let Some(tag) = layer.decrypt_inbound(cell) {
249                let hopnum = HopNum(u8::try_from(hopnum).expect("Somehow > 255 hops"));
250                return Ok((hopnum, tag));
251            }
252        }
253        Err(Error::BadCellAuth)
254    }
255    /// Add a new layer to this InboundClientCrypt
256    pub(crate) fn add_layer(&mut self, layer: Box<dyn InboundClientLayer + Send>) {
257        assert!(self.layers.len() < u8::MAX as usize);
258        self.layers.push(layer);
259    }
260
261    /// Return the number of layers configured on this InboundClientCrypt.
262    ///
263    /// TODO: use HopNum
264    #[allow(dead_code)]
265    pub(crate) fn n_layers(&self) -> usize {
266        self.layers.len()
267    }
268}
269
270/// Standard Tor relay crypto, as instantiated for RELAY cells.
271pub(crate) type Tor1RelayCrypto<RCF> =
272    tor1::CryptStatePair<tor_llcrypto::cipher::aes::Aes128Ctr, tor_llcrypto::d::Sha1, RCF>;
273
274/// Standard Tor relay crypto, as instantiated for the HSv3 protocol.
275///
276/// (The use of SHA3 is ridiculously overkill.)
277#[cfg(feature = "hs-common")]
278pub(crate) type Tor1Hsv3RelayCrypto<RCF> =
279    tor1::CryptStatePair<tor_llcrypto::cipher::aes::Aes256Ctr, tor_llcrypto::d::Sha3_256, RCF>;
280
281/// An implementation of Tor's current relay cell cryptography.
282///
283/// These are not very good algorithms; they were the best we could come up with
284/// in ~2002.  They are somewhat inefficient, and vulnerable to tagging attacks.
285/// They should get replaced within the next several years.  For information on
286/// some older proposed alternatives so far, see proposals 261, 295, and 298.
287///
288/// I am calling this design `tor1`; it does not have a generally recognized
289/// name.
290pub(crate) mod tor1 {
291    use std::marker::PhantomData;
292
293    use crate::crypto::binding::CIRC_BINDING_LEN;
294
295    use super::*;
296    use cipher::{KeyIvInit, StreamCipher};
297    use digest::Digest;
298    use tor_cell::relaycell::{RelayCellFields, RelayCellFormatTrait};
299    use typenum::Unsigned;
300
301    /// A CryptState represents one layer of shared cryptographic state between
302    /// a relay and a client for a single hop, in a single direction.
303    ///
304    /// For example, if a client makes a 3-hop circuit, then it will have 6
305    /// `CryptState`s, one for each relay, for each direction of communication.
306    ///
307    /// Note that although `CryptState` implements [`OutboundClientLayer`],
308    /// [`InboundClientLayer`], and [`RelayCrypt`], any single `CryptState`
309    /// instance will only be used for one of these roles.
310    ///
311    /// It is parameterized on a stream cipher and a digest type: most circuits
312    /// will use AES-128-CTR and SHA1, but v3 onion services use AES-256-CTR and
313    /// SHA-3.
314    pub(crate) struct CryptState<SC: StreamCipher, D: Digest + Clone, RCF: RelayCellFormatTrait> {
315        /// Stream cipher for en/decrypting cell bodies.
316        ///
317        /// This cipher is the one keyed with Kf or Kb in the spec.
318        cipher: SC,
319        /// Digest for authenticating cells to/from this hop.
320        ///
321        /// This digest is the one keyed with Df or Db in the spec.
322        digest: D,
323        /// Most recent digest value generated by this crypto.
324        last_digest_val: GenericArray<u8, D::OutputSize>,
325        /// The format used for relay cells at this layer.
326        relay_cell_format: PhantomData<RCF>,
327    }
328
329    /// A pair of CryptStates shared between a client and a relay, one for the
330    /// outbound (away from the client) direction, and one for the inbound
331    /// (towards the client) direction.
332    pub(crate) struct CryptStatePair<SC: StreamCipher, D: Digest + Clone, RCF: RelayCellFormatTrait> {
333        /// State for en/decrypting cells sent away from the client.
334        fwd: CryptState<SC, D, RCF>,
335        /// State for en/decrypting cells sent towards the client.
336        back: CryptState<SC, D, RCF>,
337        /// A circuit binding key.
338        binding: CircuitBinding,
339    }
340
341    impl<SC: StreamCipher + KeyIvInit, D: Digest + Clone, RCF: RelayCellFormatTrait> CryptInit
342        for CryptStatePair<SC, D, RCF>
343    {
344        fn seed_len() -> usize {
345            SC::KeySize::to_usize() * 2 + D::OutputSize::to_usize() * 2 + CIRC_BINDING_LEN
346        }
347        fn initialize(mut seed: &[u8]) -> Result<Self> {
348            // This corresponds to the use of the KDF algorithm as described in
349            // tor-spec 5.2.2
350            if seed.len() != Self::seed_len() {
351                return Err(Error::from(internal!(
352                    "seed length {} was invalid",
353                    seed.len()
354                )));
355            }
356
357            // Advances `seed` by `n` bytes, returning the advanced bytes
358            let mut take_seed = |n: usize| -> &[u8] {
359                let res = &seed[..n];
360                seed = &seed[n..];
361                res
362            };
363
364            let dlen = D::OutputSize::to_usize();
365            let keylen = SC::KeySize::to_usize();
366
367            let df = take_seed(dlen);
368            let db = take_seed(dlen);
369            let kf = take_seed(keylen);
370            let kb = take_seed(keylen);
371            let binding_key = take_seed(CIRC_BINDING_LEN);
372
373            let fwd = CryptState {
374                cipher: SC::new(kf.into(), &Default::default()),
375                digest: D::new().chain_update(df),
376                last_digest_val: GenericArray::default(),
377                relay_cell_format: PhantomData,
378            };
379            let back = CryptState {
380                cipher: SC::new(kb.into(), &Default::default()),
381                digest: D::new().chain_update(db),
382                last_digest_val: GenericArray::default(),
383                relay_cell_format: PhantomData,
384            };
385            let binding = CircuitBinding::try_from(binding_key)?;
386
387            Ok(CryptStatePair { fwd, back, binding })
388        }
389    }
390
391    impl<SC, D, RCF> ClientLayer<CryptState<SC, D, RCF>, CryptState<SC, D, RCF>>
392        for CryptStatePair<SC, D, RCF>
393    where
394        SC: StreamCipher,
395        D: Digest + Clone,
396        RCF: RelayCellFormatTrait,
397    {
398        fn split(
399            self,
400        ) -> (
401            CryptState<SC, D, RCF>,
402            CryptState<SC, D, RCF>,
403            CircuitBinding,
404        ) {
405            (self.fwd, self.back, self.binding)
406        }
407    }
408
409    impl<SC: StreamCipher, D: Digest + Clone, RCF: RelayCellFormatTrait> RelayCrypt
410        for CryptStatePair<SC, D, RCF>
411    {
412        fn originate(&mut self, cell: &mut RelayCellBody) {
413            let mut d_ignored = GenericArray::default();
414            cell.set_digest::<_, RCF>(&mut self.back.digest, &mut d_ignored);
415        }
416        fn encrypt_inbound(&mut self, cell: &mut RelayCellBody) {
417            // This is describe in tor-spec 5.5.3.1, "Relaying Backward at Onion Routers"
418            self.back.cipher.apply_keystream(cell.as_mut());
419        }
420        fn decrypt_outbound(&mut self, cell: &mut RelayCellBody) -> bool {
421            // This is describe in tor-spec 5.5.2.2, "Relaying Forward at Onion Routers"
422            self.fwd.cipher.apply_keystream(cell.as_mut());
423            let mut d_ignored = GenericArray::default();
424            cell.is_recognized::<_, RCF>(&mut self.fwd.digest, &mut d_ignored)
425        }
426    }
427
428    impl<SC: StreamCipher, D: Digest + Clone, RCF: RelayCellFormatTrait> OutboundClientLayer
429        for CryptState<SC, D, RCF>
430    {
431        fn originate_for(&mut self, cell: &mut RelayCellBody) -> &[u8] {
432            cell.set_digest::<_, RCF>(&mut self.digest, &mut self.last_digest_val);
433            self.encrypt_outbound(cell);
434            // Note that we truncate the authentication tag here if we are using
435            // a digest with a more-than-20-byte length.
436            &self.last_digest_val[..SENDME_TAG_LEN]
437        }
438        fn encrypt_outbound(&mut self, cell: &mut RelayCellBody) {
439            // This is a single iteration of the loop described in tor-spec
440            // 5.5.2.1, "routing away from the origin."
441            self.cipher.apply_keystream(&mut cell.0[..]);
442        }
443    }
444
445    impl<SC: StreamCipher, D: Digest + Clone, RCF: RelayCellFormatTrait> InboundClientLayer
446        for CryptState<SC, D, RCF>
447    {
448        fn decrypt_inbound(&mut self, cell: &mut RelayCellBody) -> Option<&[u8]> {
449            // This is a single iteration of the loop described in tor-spec
450            // 5.5.3, "routing to the origin."
451            self.cipher.apply_keystream(&mut cell.0[..]);
452            if cell.is_recognized::<_, RCF>(&mut self.digest, &mut self.last_digest_val) {
453                Some(&self.last_digest_val[..SENDME_TAG_LEN])
454            } else {
455                None
456            }
457        }
458    }
459
460    /// Functions on RelayCellBody that implement the digest/recognized
461    /// algorithm.
462    ///
463    /// The current relay crypto protocol uses two wholly inadequate fields to
464    /// see whether a cell is intended for its current recipient: a two-byte
465    /// "recognized" field that needs to be all-zero; and a four-byte "digest"
466    /// field containing a running digest of all cells (for this recipient) to
467    /// this one, seeded with an initial value (either Df or Db in the spec).
468    ///
469    /// These operations is described in tor-spec section 6.1 "Relay cells"
470    impl RelayCellBody {
471        /// Returns the byte slice of the `recognized` field.
472        fn recognized<RCF: RelayCellFormatTrait>(&self) -> &[u8] {
473            &self.0[RCF::FIELDS::RECOGNIZED_RANGE]
474        }
475        /// Returns the mut byte slice of the `recognized` field.
476        fn recognized_mut<RCF: RelayCellFormatTrait>(&mut self) -> &mut [u8] {
477            &mut self.0[RCF::FIELDS::RECOGNIZED_RANGE]
478        }
479        /// Returns the byte slice of the `digest` field.
480        fn digest<RCF: RelayCellFormatTrait>(&self) -> &[u8] {
481            &self.0[RCF::FIELDS::DIGEST_RANGE]
482        }
483        /// Returns the mut byte slice of the `digest` field.
484        fn digest_mut<RCF: RelayCellFormatTrait>(&mut self) -> &mut [u8] {
485            &mut self.0[RCF::FIELDS::DIGEST_RANGE]
486        }
487        /// Prepare a cell body by setting its digest and recognized field.
488        fn set_digest<D: Digest + Clone, RCF: RelayCellFormatTrait>(
489            &mut self,
490            d: &mut D,
491            used_digest: &mut GenericArray<u8, D::OutputSize>,
492        ) {
493            self.recognized_mut::<RCF>().fill(0); // Set 'Recognized' to zero
494            self.digest_mut::<RCF>().fill(0); // Set Digest to zero
495
496            d.update(&self.0[..]);
497            // TODO(nickm) can we avoid this clone?  Probably not.
498            *used_digest = d.clone().finalize();
499            let used_digest_prefix = &used_digest[0..RCF::FIELDS::DIGEST_RANGE.len()];
500            self.digest_mut::<RCF>().copy_from_slice(used_digest_prefix);
501        }
502        /// Check whether this just-decrypted cell is now an authenticated plaintext.
503        ///
504        /// This method returns true if the `recognized` field is all zeros, and if the
505        /// `digest` field is a digest of the correct material.
506        ///
507        /// If this method returns false, then either further decryption is required,
508        /// or the cell is corrupt.
509        // TODO #1336: Further optimize and/or benchmark this.
510        fn is_recognized<D: Digest + Clone, RCF: RelayCellFormatTrait>(
511            &self,
512            d: &mut D,
513            rcvd: &mut GenericArray<u8, D::OutputSize>,
514        ) -> bool {
515            use crate::util::ct;
516
517            // Validate 'Recognized' field
518            if !ct::is_zero(self.recognized::<RCF>()) {
519                return false;
520            }
521
522            // Now also validate the 'Digest' field:
523
524            let mut dtmp = d.clone();
525            // Add bytes up to the 'Digest' field
526            dtmp.update(&self.0[..RCF::FIELDS::DIGEST_RANGE.start]);
527            // Add zeroes where the 'Digest' field is
528            dtmp.update(RCF::FIELDS::EMPTY_DIGEST);
529            // Add the rest of the bytes
530            dtmp.update(&self.0[RCF::FIELDS::DIGEST_RANGE.end..]);
531            // Clone the digest before finalize destroys it because we will use
532            // it in the future
533            let dtmp_clone = dtmp.clone();
534            let result = dtmp.finalize();
535
536            if ct::bytes_eq(
537                self.digest::<RCF>(),
538                &result[0..RCF::FIELDS::DIGEST_RANGE.len()],
539            ) {
540                // Copy useful things out of this cell (we keep running digest)
541                *d = dtmp_clone;
542                *rcvd = result;
543                return true;
544            }
545
546            false
547        }
548    }
549
550    /// Benchmark utilities for the `tor1` module.
551    #[cfg(feature = "bench")]
552    pub(crate) mod bench_utils {
553        use super::*;
554
555        /// Public wrapper around the `RelayCellBody` struct.
556        #[repr(transparent)]
557        pub struct RelayBody(pub(in crate::crypto) RelayCellBody);
558
559        impl From<[u8; 509]> for RelayBody {
560            fn from(body: [u8; 509]) -> Self {
561                let body = Box::new(body);
562                Self(body.into())
563            }
564        }
565
566        impl RelayBody {
567            /// Public wrapper around the `set_digest` method of the `RelayCellBody` struct.
568            pub fn set_digest<D: Digest + Clone, RCF: RelayCellFormatTrait>(
569                &mut self,
570                d: &mut D,
571                used_digest: &mut GenericArray<u8, D::OutputSize>,
572            ) {
573                self.0.set_digest::<D, RCF>(d, used_digest);
574            }
575
576            /// Public wrapper around the `is_recognized` method of the `RelayCellBody` struct.
577            pub fn is_recognized<D: Digest + Clone, RCF: RelayCellFormatTrait>(
578                &self,
579                d: &mut D,
580                rcvd: &mut GenericArray<u8, D::OutputSize>,
581            ) -> bool {
582                self.0.is_recognized::<D, RCF>(d, rcvd)
583            }
584        }
585    }
586}
587
588#[cfg(test)]
589mod test {
590    // @@ begin test lint list maintained by maint/add_warning @@
591    #![allow(clippy::bool_assert_comparison)]
592    #![allow(clippy::clone_on_copy)]
593    #![allow(clippy::dbg_macro)]
594    #![allow(clippy::mixed_attributes_style)]
595    #![allow(clippy::print_stderr)]
596    #![allow(clippy::print_stdout)]
597    #![allow(clippy::single_char_pattern)]
598    #![allow(clippy::unwrap_used)]
599    #![allow(clippy::unchecked_duration_subtraction)]
600    #![allow(clippy::useless_vec)]
601    #![allow(clippy::needless_pass_by_value)]
602    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
603    use super::*;
604    use rand::RngCore;
605    use tor_basic_utils::test_rng::testing_rng;
606    use tor_bytes::SecretBuf;
607    use tor_cell::relaycell::RelayCellFormatV0;
608
609    fn add_layers(
610        cc_out: &mut OutboundClientCrypt,
611        cc_in: &mut InboundClientCrypt,
612        // TODO #1067: test other formats
613        pair: Tor1RelayCrypto<RelayCellFormatV0>,
614    ) {
615        let (outbound, inbound, _) = pair.split();
616        cc_out.add_layer(Box::new(outbound));
617        cc_in.add_layer(Box::new(inbound));
618    }
619
620    #[test]
621    fn roundtrip() {
622        // Take canned keys and make sure we can do crypto correctly.
623        use crate::crypto::handshake::ShakeKeyGenerator as KGen;
624        fn s(seed: &[u8]) -> SecretBuf {
625            seed.to_vec().into()
626        }
627
628        let seed1 = s(b"hidden we are free");
629        let seed2 = s(b"free to speak, to free ourselves");
630        let seed3 = s(b"free to hide no more");
631
632        let mut cc_out = OutboundClientCrypt::new();
633        let mut cc_in = InboundClientCrypt::new();
634        let pair = Tor1RelayCrypto::construct(KGen::new(seed1.clone())).unwrap();
635        add_layers(&mut cc_out, &mut cc_in, pair);
636        let pair = Tor1RelayCrypto::construct(KGen::new(seed2.clone())).unwrap();
637        add_layers(&mut cc_out, &mut cc_in, pair);
638        let pair = Tor1RelayCrypto::construct(KGen::new(seed3.clone())).unwrap();
639        add_layers(&mut cc_out, &mut cc_in, pair);
640
641        assert_eq!(cc_in.n_layers(), 3);
642        assert_eq!(cc_out.n_layers(), 3);
643
644        let mut r1 = Tor1RelayCrypto::<RelayCellFormatV0>::construct(KGen::new(seed1)).unwrap();
645        let mut r2 = Tor1RelayCrypto::<RelayCellFormatV0>::construct(KGen::new(seed2)).unwrap();
646        let mut r3 = Tor1RelayCrypto::<RelayCellFormatV0>::construct(KGen::new(seed3)).unwrap();
647
648        let mut rng = testing_rng();
649        for _ in 1..300 {
650            // outbound cell
651            let mut cell = Box::new([0_u8; 509]);
652            let mut cell_orig = [0_u8; 509];
653            rng.fill_bytes(&mut cell_orig);
654            cell.copy_from_slice(&cell_orig);
655            let mut cell = cell.into();
656            let _tag = cc_out.encrypt(&mut cell, 2.into()).unwrap();
657            assert_ne!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
658            assert!(!r1.decrypt_outbound(&mut cell));
659            assert!(!r2.decrypt_outbound(&mut cell));
660            assert!(r3.decrypt_outbound(&mut cell));
661
662            assert_eq!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
663
664            // inbound cell
665            let mut cell = Box::new([0_u8; 509]);
666            let mut cell_orig = [0_u8; 509];
667            rng.fill_bytes(&mut cell_orig);
668            cell.copy_from_slice(&cell_orig);
669            let mut cell = cell.into();
670
671            r3.originate(&mut cell);
672            r3.encrypt_inbound(&mut cell);
673            r2.encrypt_inbound(&mut cell);
674            r1.encrypt_inbound(&mut cell);
675            let (layer, _tag) = cc_in.decrypt(&mut cell).unwrap();
676            assert_eq!(layer, 2.into());
677            assert_eq!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
678
679            // TODO: Test tag somehow.
680        }
681
682        // Try a failure: sending a cell to a nonexistent hop.
683        {
684            let mut cell = Box::new([0_u8; 509]).into();
685            let err = cc_out.encrypt(&mut cell, 10.into());
686            assert!(matches!(err, Err(Error::NoSuchHop)));
687        }
688
689        // Try a failure: A junk cell with no correct auth from any layer.
690        {
691            let mut cell = Box::new([0_u8; 509]).into();
692            let err = cc_in.decrypt(&mut cell);
693            assert!(matches!(err, Err(Error::BadCellAuth)));
694        }
695    }
696
697    // From tor's test_relaycrypt.c
698
699    #[test]
700    fn testvec() {
701        use digest::XofReader;
702        use digest::{ExtendableOutput, Update};
703
704        // (The ....s at the end here are the KH ca)
705        const K1: &[u8; 92] =
706            b"    'My public key is in this signed x509 object', said Tom assertively.      (N-PREG-VIRYL)";
707        const K2: &[u8; 92] =
708            b"'Let's chart the pedal phlanges in the tomb', said Tom cryptographically.  (PELCG-GBR-TENCU)";
709        const K3: &[u8; 92] =
710            b"     'Segmentation fault bugs don't _just happen_', said Tom seethingly.        (P-GUVAT-YL)";
711
712        const SEED: &[u8;108] = b"'You mean to tell me that there's a version of Sha-3 with no limit on the output length?', said Tom shakily.";
713
714        // These test vectors were generated from Tor.
715        let data: &[(usize, &str)] = &include!("../../testdata/cell_crypt.rs");
716
717        let mut cc_out = OutboundClientCrypt::new();
718        let mut cc_in = InboundClientCrypt::new();
719        let pair = Tor1RelayCrypto::<RelayCellFormatV0>::initialize(&K1[..]).unwrap();
720        add_layers(&mut cc_out, &mut cc_in, pair);
721        let pair = Tor1RelayCrypto::<RelayCellFormatV0>::initialize(&K2[..]).unwrap();
722        add_layers(&mut cc_out, &mut cc_in, pair);
723        let pair = Tor1RelayCrypto::<RelayCellFormatV0>::initialize(&K3[..]).unwrap();
724        add_layers(&mut cc_out, &mut cc_in, pair);
725
726        let mut xof = tor_llcrypto::d::Shake256::default();
727        xof.update(&SEED[..]);
728        let mut stream = xof.finalize_xof();
729
730        let mut j = 0;
731        for cellno in 0..51 {
732            let mut body = Box::new([0_u8; 509]);
733            body[0] = 2; // command: data.
734            body[4] = 1; // streamid: 1.
735            body[9] = 1; // length: 498
736            body[10] = 242;
737            stream.read(&mut body[11..]);
738
739            let mut cell = body.into();
740            let _ = cc_out.encrypt(&mut cell, 2.into());
741
742            if cellno == data[j].0 {
743                let expected = hex::decode(data[j].1).unwrap();
744                assert_eq!(cell.as_ref(), &expected[..]);
745                j += 1;
746            }
747        }
748    }
749
750    #[test]
751    fn hop_num_display() {
752        for i in 0..10 {
753            let hop_num = HopNum::from(i);
754            let expect = format!("#{}", i + 1);
755
756            assert_eq!(expect, hop_num.display().to_string());
757        }
758    }
759}