Skip to main content

tor_proto/crypto/cell/
cgo.rs

1//! Implementation for Counter Galois Onion (CGO) relay cell encryption
2//!
3//! CGO is an improved approach for encrypting relay cells, with better support
4//! for tagging resistance, better forward secrecy, and other improvements.
5//! It is described in [a paper][CGO] by Degabriele, Melloni, Münch, and Stam,
6//! and specified in [proposal 359].
7//!
8//! CGO is based on a construction called "UIV+",
9//! which provides the "robust pseudorandom permutation" security definition.
10//! Notably, _encryption_ with UIV+ is non-malleable (and hence tagging resistant),
11//! whereas _decryption_ with UIV+ is malleable (and hence not tagging resistant).
12//!
13//! [CGO]: https://eprint.iacr.org/2025/583
14//! [proposal 359]: https://spec.torproject.org/proposals/359-cgo-redux.html
15//
16// Implementation note: For naming, I'm trying to use the symbols from the paper
17// and the spec (which should be the same) wherever possible.
18
19#![allow(dead_code)] // TODO CGO: Remove this once we actually use CGO encryption.
20
21use aes::{Aes128, Aes128Dec, Aes128Enc, Aes256, Aes256Dec, Aes256Enc};
22use cipher::{BlockCipher, BlockDecrypt, BlockEncrypt, BlockSizeUser, StreamCipher as _};
23use digest::KeyInit;
24use polyval::{Polyval, universal_hash::UniversalHash};
25use tor_cell::{
26    chancell::{CELL_DATA_LEN, ChanCmd},
27    relaycell::msg::SendmeTag,
28};
29use tor_error::internal;
30use zeroize::Zeroizing;
31
32use super::{CryptInit, RelayCellBody};
33use crate::{client::circuit::CircuitBinding, util::ct};
34
35/// Size of CGO tag, in bytes.
36const CGO_TAG_LEN: usize = 16;
37/// Size of CGO payload, in bytes.
38const CGO_PAYLOAD_LEN: usize = CELL_DATA_LEN - CGO_TAG_LEN;
39
40/// Size of CGO additional data, in bytes.
41///
42/// This is used to encode whether the cell command is `RELAY`` or `RELAY_EARLY`.
43const CGO_AD_LEN: usize = 16;
44
45/// Size of the "H" tweak passed to the UIV+ construction.
46const HLEN_UIV: usize = CGO_TAG_LEN + CGO_AD_LEN;
47
48/// Block length.
49/// Used by various types.
50const BLK_LEN: usize = 16;
51/// Block length as a typenum; used to parameterize some types
52/// that use ArrayLen.
53type BlockLen = typenum::U16;
54/// A single block.  Used as input to various functions.
55type Block = [u8; BLK_LEN];
56
57/// Helper trait to define the features we need from a block cipher,
58/// and make our "where" declarations smaller.
59///
60/// Not sealed because it is never used outside of this crate.
61#[cfg_attr(feature = "bench", visibility::make(pub))]
62pub(crate) trait BlkCipher:
63    BlockCipher + KeyInit + BlockSizeUser<BlockSize = BlockLen> + Clone
64{
65    /// Length of the key used by this block cipher.
66    const KEY_LEN: usize;
67}
68
69/// Helper trait to define the features we need from a block cipher,
70/// and make our "where" declarations smaller.
71///
72/// Not sealed because it is never used outside of this crate.
73#[cfg_attr(feature = "bench", visibility::make(pub))]
74pub(crate) trait BlkCipherEnc: BlkCipher + BlockEncrypt {}
75
76/// Helper trait to define the features we need from a block cipher,
77/// and make our "where" declarations smaller.
78///
79/// Not sealed because it is never used outside of this crate.
80#[cfg_attr(feature = "bench", visibility::make(pub))]
81pub(crate) trait BlkCipherDec: BlkCipher + BlockDecrypt {}
82
83impl BlkCipher for Aes128 {
84    const KEY_LEN: usize = 16;
85}
86impl BlkCipherEnc for Aes128 {}
87impl BlkCipherDec for Aes128 {}
88impl BlkCipher for Aes128Enc {
89    const KEY_LEN: usize = 16;
90}
91impl BlkCipherEnc for Aes128Enc {}
92impl BlkCipher for Aes128Dec {
93    const KEY_LEN: usize = 16;
94}
95impl BlkCipherDec for Aes128Dec {}
96
97impl BlkCipher for Aes256 {
98    const KEY_LEN: usize = 32;
99}
100impl BlkCipherEnc for Aes256 {}
101impl BlkCipherDec for Aes256 {}
102impl BlkCipher for Aes256Enc {
103    const KEY_LEN: usize = 32;
104}
105impl BlkCipherEnc for Aes256Enc {}
106impl BlkCipher for Aes256Dec {
107    const KEY_LEN: usize = 32;
108}
109impl BlkCipherDec for Aes256Dec {}
110
111/// Define a tweakable block cipher.
112mod et {
113    use super::*;
114
115    /// Type of the tweak accepted by the tweakable block cipher.
116    ///
117    /// (This might seem like a weird way to express `&[u8; TLEN_ET]`,
118    /// but it _is_ the way that the UIV construction will provide the tweak.)
119    pub(super) type EtTweak<'a> = (&'a [u8; CGO_TAG_LEN], u8, &'a [u8; CGO_PAYLOAD_LEN]);
120    /// Total length of EtTweak fields.
121    pub(super) const TLEN_ET: usize = CGO_TAG_LEN + 1 + CGO_PAYLOAD_LEN;
122
123    /// Implementation for an LRW2 tweakable block cipher,
124    /// with block length of [`BLK_LEN`],
125    /// and specialized tweak of type [`EtTweak`].
126    ///
127    /// Corresponds to ET in the specification.
128    #[derive(Clone)]
129    pub(super) struct EtCipher<BC: BlkCipher> {
130        /// Underlying block cipher
131        kb: BC,
132        /// Universal hash, initialized with the key KU.
133        ku: Polyval,
134    }
135    impl<BC: BlkCipher> EtCipher<BC> {
136        /// Helper: Given a tweak, compute the blinding value we will use
137        /// for encrypting or decryption.
138        fn compute_tweak_hash(&self, tweak: EtTweak<'_>) -> Zeroizing<Block> {
139            // We want to compute the UH(KU, tweak.0 | tweak.1 | tweak.2).
140            // This implementation is optimized to avoid excessive data copying.
141            let mut ku = self.ku.clone();
142
143            let mut block1 = Zeroizing::new([0_u8; 16]);
144            block1[0] = tweak.1;
145            block1[1..16].copy_from_slice(&tweak.2[0..15]);
146            ku.update(&[(*tweak.0).into(), (*block1).into()]);
147            ku.update_padded(&tweak.2[15..]);
148            Zeroizing::new(ku.finalize().into())
149        }
150    }
151    impl<BC: BlkCipherEnc> EtCipher<BC> {
152        /// Encrypt `block` in-place, using `tweak`.
153        pub(super) fn encrypt(&self, tweak: EtTweak<'_>, block: &mut Block) {
154            // ENC_ET((KB,KU), T, M) = UH(KU,T) ^ ENC_BC(KB, M ^ UH(KU,T))
155            let tag: Zeroizing<[u8; 16]> = self.compute_tweak_hash(tweak);
156            xor_into(block, &tag);
157            self.kb.encrypt_block(block.into());
158            xor_into(block, &tag);
159        }
160    }
161    impl<BC: BlkCipherDec> EtCipher<BC> {
162        /// Decrypt `block` in-place, using `tweak`.
163        pub(super) fn decrypt(&self, tweak: EtTweak<'_>, block: &mut Block) {
164            // DEC_ET((KB,KU), T, M) = UH(KU,T) ^ DEC_BC(KB, M ^ UH(KU,T))
165            let tag: Zeroizing<[u8; 16]> = self.compute_tweak_hash(tweak);
166            xor_into(block, &tag);
167            self.kb.decrypt_block(block.into());
168            xor_into(block, &tag);
169        }
170    }
171    impl<BC: BlkCipher> CryptInit for EtCipher<BC> {
172        fn seed_len() -> usize {
173            BC::key_size() + polyval::KEY_SIZE
174        }
175        fn initialize(seed: &[u8]) -> crate::Result<Self> {
176            // TODO PERF: Here and throughout, these initialize functions do more checking than we
177            // necessarily need.  We should see if we can simplify them.
178            if seed.len() != Self::seed_len() {
179                return Err(internal!("Invalid seed length").into());
180            }
181            let (kb, ku) = seed.split_at(BC::key_size());
182            let ku: &[u8; 16] = ku
183                .try_into()
184                .expect("Incorrect key size, even though it was validated!?");
185            Ok(Self {
186                kb: BC::new(kb.into()),
187                ku: Polyval::new(ku.into()),
188            })
189        }
190    }
191}
192
193/// Define a tweakable pseudorandom stream generator.
194mod prf {
195    use tor_error::internal;
196
197    use super::*;
198
199    /// The type used as a tweak for this PRF.
200    type PrfTweak = [u8; 16];
201    /// Length of the PRF's output when used with t=0.
202    const PRF_N0_LEN: usize = CGO_PAYLOAD_LEN;
203    /// Offset of the PRF's output when used with t=1.
204    const PRF_N1_OFFSET: usize = 31 * 16;
205    const _: () = assert!(PRF_N1_OFFSET >= PRF_N0_LEN);
206
207    /// Pseudorandom function based on CTR128, Polyval, and an underlying block cipher.
208    //
209    // Definition: PRF((K, B), T, t) = CTR_{nt}(K, UH(B, T) + (t * C)).
210    //   where t is 0 or 1 and C is 31.
211    #[derive(Clone)]
212    pub(super) struct Prf<BC: BlkCipherEnc> {
213        /// The underlying block cipher, initialized with the key "K"
214        k: BC,
215        /// Thu underlying universal hash, initialized with the key "B"
216        b: Polyval,
217    }
218    impl<BC: BlkCipherEnc> Prf<BC> {
219        /// Helper: Return a stream cipher, initialized with an IV corresponding
220        /// to `tweak` and an offset corresponding to `t`.
221        fn cipher(&self, tweak: &PrfTweak, t: bool) -> ctr::Ctr128BE<BC> {
222            use {
223                cipher::{InnerIvInit as _, StreamCipherSeek as _},
224                ctr::CtrCore,
225            };
226            let mut b = self.b.clone(); // TODO PERF: Clone cost here, and below.
227            b.update(&[(*tweak).into()]);
228            let mut iv = b.finalize();
229            *iv.last_mut().expect("no last element?") &= 0xC0; // Clear the low six bits.
230            let iv: [u8; 16] = iv.into(); // work around hybridarray/genericarray mismatch.
231            let mut cipher: ctr::Ctr128BE<BC> = cipher::StreamCipherCoreWrapper::from_core(
232                CtrCore::inner_iv_init(self.k.clone(), (&iv).into()),
233            );
234            if t {
235                debug_assert_eq!(cipher.current_pos::<u32>(), 0_u32);
236                cipher.seek(PRF_N1_OFFSET);
237            }
238
239            cipher
240        }
241
242        /// Apply the cipherstream from this Prf to `out`, with tweak parameter `tweak`
243        /// and offset parameter `t=0`.
244        pub(super) fn xor_n0_stream(&self, tweak: &PrfTweak, out: &mut [u8; PRF_N0_LEN]) {
245            let mut stream = self.cipher(tweak, false);
246            stream.apply_keystream(out);
247        }
248
249        /// Return a vector containing `n` bytes of this Prf, with tweak
250        /// parameter `tweak` and offset parameter `t=1`.
251        pub(super) fn get_n1_stream(&self, tweak: &PrfTweak, n: usize) -> Zeroizing<Vec<u8>> {
252            let mut output = Zeroizing::new(vec![0_u8; n]);
253            self.cipher(tweak, true).apply_keystream(output.as_mut());
254            output
255        }
256    }
257
258    impl<BC: BlkCipherEnc> CryptInit for Prf<BC> {
259        fn seed_len() -> usize {
260            BC::key_size() + polyval::KEY_SIZE
261        }
262        fn initialize(seed: &[u8]) -> crate::Result<Self> {
263            if seed.len() != Self::seed_len() {
264                return Err(internal!("Invalid seed length").into());
265            }
266            let (k, b) = seed.split_at(BC::key_size());
267            let b: &[u8; 16] = b
268                .try_into()
269                .expect("Incorrect key size, even though it was validated!?");
270            Ok(Self {
271                k: BC::new(k.into()),
272                b: Polyval::new(b.into()),
273            })
274        }
275    }
276}
277
278/// Define the UIV+ tweakable wide-block cipher.
279///
280/// This construction is a "rugged pseudorandom permutation"; see above.
281mod uiv {
282    use super::*;
283
284    /// Type of tweak used as input to the UIV encryption and decryption algorithms.
285    pub(super) type UivTweak<'a> = (&'a [u8; BLK_LEN], u8);
286
287    /// Keys for a UIV cipher.
288    #[derive(Clone)]
289    pub(super) struct Uiv<EtBC: BlkCipher, PrfBC: BlkCipherEnc> {
290        /// Tweakable block cipher key; corresponds to J in the specification.
291        j: et::EtCipher<EtBC>,
292        /// PRF keys; corresponds to S in the specification.
293        s: prf::Prf<PrfBC>,
294
295        /// Testing only: a copy of our current key material.
296        ///
297        /// (Used because otherwise, we cannot extract keys from our components,
298        /// but we _do_ need to test that our key update code works sensibly.)
299        #[cfg(test)]
300        pub(super) keys: Zeroizing<Vec<u8>>,
301    }
302
303    /// Helper: split a mutable cell body into the left-hand (tag) and
304    /// right-hand (body) parts.
305    fn split(
306        cell_body: &mut [u8; CELL_DATA_LEN],
307    ) -> (&mut [u8; CGO_TAG_LEN], &mut [u8; CGO_PAYLOAD_LEN]) {
308        //TODO PERF: Make sure that there is no actual checking done here!
309        let (left, right) = cell_body.split_at_mut(CGO_TAG_LEN);
310        (
311            left.try_into().expect("split_at_mut returned wrong size!"),
312            right.try_into().expect("split_at_mut returned wrong size!"),
313        )
314    }
315
316    impl<EtBC: BlkCipherEnc, PrfBC: BlkCipherEnc> Uiv<EtBC, PrfBC> {
317        /// Encrypt `cell_body`, using the provided `tweak`.
318        ///
319        /// Corresponds to `ENC_UIV.`
320        pub(super) fn encrypt(&self, tweak: UivTweak<'_>, cell_body: &mut [u8; CELL_DATA_LEN]) {
321            // ENC_UIV((J,S), H, (X_L,X_R)):
322            //     Y_L <-- ENC_ET(J, (H || X_R), X_L)
323            //     Y_R <-- X_R ^ PRF_n0(S, Y_L, 0)
324            //     return (Y_L, Y_R)
325            let (left, right) = split(cell_body);
326            self.j.encrypt((tweak.0, tweak.1, right), left);
327            self.s.xor_n0_stream(left, right);
328        }
329    }
330    impl<EtBC: BlkCipherDec, PrfBC: BlkCipherEnc> Uiv<EtBC, PrfBC> {
331        /// Decrypt `cell_body`, using the provided `tweak`.
332        ///
333        /// Corresponds to `DEC_UIV`.
334        pub(super) fn decrypt(&self, tweak: UivTweak<'_>, cell_body: &mut [u8; CELL_DATA_LEN]) {
335            // DEC_UIV((J,S), H, (Y_L,Y_R)):
336            //    X_R <-- Y_R xor PRF_n0(S, Y_L, 0)
337            //    X_L <-- DEC_ET(J, (H || X_R), Y_L)
338            //    return (X_L, X_R)
339            let (left, right) = split(cell_body);
340            self.s.xor_n0_stream(left, right);
341            self.j.decrypt((tweak.0, tweak.1, right), left);
342        }
343    }
344    impl<EtBC: BlkCipher, PrfBC: BlkCipherEnc> Uiv<EtBC, PrfBC> {
345        /// Modify this Uiv, and the provided nonce, so that its current state
346        /// cannot be recovered.
347        ///
348        /// Corresponds to `UPDATE_UIV`
349        pub(super) fn update(&mut self, nonce: &mut [u8; BLK_LEN]) {
350            // UPDATE_UIV((J,S), N):
351            //     ((J',S'), N') = PRF_{n1}(S, N, 1)
352            //     return ((J', S'), N')
353
354            // TODO PERF: We could allocate significantly less here, by using
355            // reinitialize functions, and by not actually expanding the key
356            // stream.
357            let n_bytes = Self::seed_len() + BLK_LEN;
358            let seed = self.s.get_n1_stream(nonce, n_bytes);
359            #[cfg(test)]
360            {
361                self.keys = Zeroizing::new(seed[..Self::seed_len()].to_vec());
362            }
363            let (j, s, n) = Self::split_seed(&seed);
364            self.j = et::EtCipher::initialize(j).expect("Invalid slice len");
365            self.s = prf::Prf::initialize(s).expect("invalid slice len");
366            nonce[..].copy_from_slice(n);
367        }
368
369        /// Helper: divide seed into J, S, and N.
370        fn split_seed(seed: &[u8]) -> (&[u8], &[u8], &[u8]) {
371            let len_j = et::EtCipher::<EtBC>::seed_len();
372            let len_s = prf::Prf::<PrfBC>::seed_len();
373            (
374                &seed[0..len_j],
375                &seed[len_j..len_j + len_s],
376                &seed[len_j + len_s..],
377            )
378        }
379    }
380
381    impl<EtBC: BlkCipher, PrfBC: BlkCipherEnc> CryptInit for Uiv<EtBC, PrfBC> {
382        fn seed_len() -> usize {
383            super::et::EtCipher::<EtBC>::seed_len() + super::prf::Prf::<PrfBC>::seed_len()
384        }
385        fn initialize(seed: &[u8]) -> crate::Result<Self> {
386            if seed.len() != Self::seed_len() {
387                return Err(internal!("Invalid seed length").into());
388            }
389            #[cfg(test)]
390            let keys = Zeroizing::new(seed.to_vec());
391            let (j, s, n) = Self::split_seed(seed);
392            debug_assert!(n.is_empty());
393            Ok(Self {
394                j: et::EtCipher::initialize(j)?,
395                s: prf::Prf::initialize(s)?,
396                #[cfg(test)]
397                keys,
398            })
399        }
400    }
401}
402
403/// Xor all bytes from `input` into `output`.
404fn xor_into<const N: usize>(output: &mut [u8; N], input: &[u8; N]) {
405    for i in 0..N {
406        output[i] ^= input[i];
407    }
408}
409
410/// Helper: return the first `BLK_LEN` bytes of a slice as an array.
411///
412/// TODO PERF: look for other ways to express this, and/or make sure that it
413/// compiles down to something minimal.
414#[inline]
415fn first_block(bytes: &[u8]) -> &[u8; BLK_LEN] {
416    bytes[0..BLK_LEN].try_into().expect("Slice too short!")
417}
418
419/// State of a single direction of a CGO layer, at the client or at a relay.
420#[derive(Clone)]
421struct CryptState<EtBC: BlkCipher, PrfBC: BlkCipherEnc> {
422    /// The current key "K" for this direction.
423    uiv: uiv::Uiv<EtBC, PrfBC>,
424    /// The current nonce value "N" for this direction.
425    nonce: Zeroizing<[u8; BLK_LEN]>,
426    /// The current tag value "T'" for this direction.
427    tag: Zeroizing<[u8; BLK_LEN]>,
428}
429
430impl<EtBC: BlkCipher, PrfBC: BlkCipherEnc> CryptInit for CryptState<EtBC, PrfBC> {
431    fn seed_len() -> usize {
432        uiv::Uiv::<EtBC, PrfBC>::seed_len() + BLK_LEN
433    }
434    /// Construct this state from a seed of the appropriate length.
435    fn initialize(seed: &[u8]) -> crate::Result<Self> {
436        if seed.len() != Self::seed_len() {
437            return Err(internal!("Invalid seed length").into());
438        }
439        let (j_s, n) = seed.split_at(uiv::Uiv::<EtBC, PrfBC>::seed_len());
440        Ok(Self {
441            uiv: uiv::Uiv::initialize(j_s)?,
442            nonce: Zeroizing::new(n.try_into().expect("invalid splice length")),
443            tag: Zeroizing::new([0; BLK_LEN]),
444        })
445    }
446}
447
448/// An instance of CGO used for outbound client encryption.
449#[cfg_attr(feature = "bench", visibility::make(pub))]
450#[derive(Clone, derive_more::From)]
451pub(crate) struct ClientOutbound<EtBC, PrfBC>(CryptState<EtBC, PrfBC>)
452where
453    EtBC: BlkCipherDec,
454    PrfBC: BlkCipherEnc;
455impl<EtBC, PrfBC> super::OutboundClientLayer for ClientOutbound<EtBC, PrfBC>
456where
457    EtBC: BlkCipherDec,
458    PrfBC: BlkCipherEnc,
459{
460    fn originate_for(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag {
461        cell.0[0..BLK_LEN].copy_from_slice(&self.0.nonce[..]);
462        self.encrypt_outbound(cmd, cell);
463        self.0.uiv.update(&mut self.0.nonce);
464        SendmeTag::try_from(&cell.0[0..BLK_LEN]).expect("Block length not a valid sendme tag.")
465    }
466    fn encrypt_outbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) {
467        // TODO PERF: consider swap here.
468        let t_new: [u8; BLK_LEN] = *first_block(&*cell.0);
469
470        // Note use of decrypt here: Client operations always use _decrypt_,
471        // and relay operations always use _encrypt_.
472        self.0.uiv.decrypt((&self.0.tag, cmd.into()), &mut cell.0);
473        *self.0.tag = t_new;
474    }
475}
476
477/// An instance of CGO used for inbound client encryption.
478#[cfg_attr(feature = "bench", visibility::make(pub))]
479#[derive(Clone, derive_more::From)]
480pub(crate) struct ClientInbound<EtBC, PrfBC>(CryptState<EtBC, PrfBC>)
481where
482    EtBC: BlkCipherDec,
483    PrfBC: BlkCipherEnc;
484impl<EtBC, PrfBC> super::InboundClientLayer for ClientInbound<EtBC, PrfBC>
485where
486    EtBC: BlkCipherDec,
487    PrfBC: BlkCipherEnc,
488{
489    fn decrypt_inbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag> {
490        let mut t_orig: [u8; BLK_LEN] = *first_block(&*cell.0);
491        // let t_orig_orig = t_orig;
492
493        // Note use of decrypt here: Client operations always use _decrypt_,
494        // and relay operations always use _encrypt_.
495        self.0.uiv.decrypt((&self.0.tag, cmd.into()), &mut cell.0);
496        *self.0.tag = t_orig;
497        if ct::bytes_eq(&cell.0[..CGO_TAG_LEN], &self.0.nonce[..]) {
498            self.0.uiv.update(&mut t_orig);
499            *self.0.nonce = t_orig;
500            // assert_eq!(self.0.tag[..BLK_LEN], t_orig_orig[..]);
501            Some((*self.0.tag).into())
502        } else {
503            None
504        }
505    }
506}
507
508/// An instance of CGO used for outbound (away from the client) relay encryption.
509#[cfg_attr(feature = "bench", visibility::make(pub))]
510#[derive(Clone, derive_more::From)]
511pub(crate) struct RelayOutbound<EtBC, PrfBC>(CryptState<EtBC, PrfBC>)
512where
513    EtBC: BlkCipherEnc,
514    PrfBC: BlkCipherEnc;
515impl<EtBC, PrfBC> super::OutboundRelayLayer for RelayOutbound<EtBC, PrfBC>
516where
517    EtBC: BlkCipherEnc,
518    PrfBC: BlkCipherEnc,
519{
520    fn decrypt_outbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag> {
521        let tag = SendmeTag::try_from(&cell.0[0..BLK_LEN]).expect("Invalid sendme length");
522        // Note use of encrypt here: Client operations always use _decrypt_,
523        // and relay operations always use _encrypt_.
524        self.0.uiv.encrypt((&self.0.tag, cmd.into()), &mut cell.0);
525        *self.0.tag = *first_block(&*cell.0);
526        if ct::bytes_eq(self.0.tag.as_ref(), &self.0.nonce[..]) {
527            self.0.uiv.update(&mut self.0.nonce);
528            Some(tag)
529        } else {
530            None
531        }
532    }
533}
534
535/// An instance of CGO used for inbound (towards the client) relay encryption.
536#[cfg_attr(feature = "bench", visibility::make(pub))]
537#[derive(Clone, derive_more::From)]
538pub(crate) struct RelayInbound<EtBC, PrfBC>(CryptState<EtBC, PrfBC>)
539where
540    EtBC: BlkCipherEnc,
541    PrfBC: BlkCipherEnc;
542impl<EtBC, PrfBC> super::InboundRelayLayer for RelayInbound<EtBC, PrfBC>
543where
544    EtBC: BlkCipherEnc,
545    PrfBC: BlkCipherEnc,
546{
547    fn originate(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag {
548        cell.0[0..BLK_LEN].copy_from_slice(&self.0.nonce[..]);
549        self.encrypt_inbound(cmd, cell);
550        self.0.nonce.copy_from_slice(&cell.0[0..BLK_LEN]);
551        self.0.uiv.update(&mut self.0.nonce);
552        // assert_eq!(self.0.tag[..BLK_LEN], cell.0[0..BLK_LEN]);
553        (*self.0.tag).into()
554    }
555    fn encrypt_inbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) {
556        // Note use of encrypt here: Client operations always use _decrypt_,
557        // and relay operations always use _encrypt_.
558        self.0.uiv.encrypt((&self.0.tag, cmd.into()), &mut cell.0);
559        *self.0.tag = *first_block(&*cell.0);
560    }
561}
562
563/// A set of cryptographic information as shared by the client and a single relay,
564/// and
565#[cfg_attr(feature = "bench", visibility::make(pub))]
566#[derive(Clone)]
567pub(crate) struct CryptStatePair<EtBC, PrfBC>
568where
569    EtBC: BlkCipher,
570    PrfBC: BlkCipherEnc,
571{
572    /// State for the outbound direction (away from client)
573    outbound: CryptState<EtBC, PrfBC>,
574    /// State for the inbound direction (towards client)
575    inbound: CryptState<EtBC, PrfBC>,
576    /// Circuit binding information.
577    binding: CircuitBinding,
578}
579
580impl<EtBC, PrfBC> CryptInit for CryptStatePair<EtBC, PrfBC>
581where
582    EtBC: BlkCipher,
583    PrfBC: BlkCipherEnc,
584{
585    fn seed_len() -> usize {
586        CryptState::<EtBC, PrfBC>::seed_len() * 2 + crate::crypto::binding::CIRC_BINDING_LEN
587    }
588    fn initialize(seed: &[u8]) -> crate::Result<Self> {
589        const {
590            // can't use assert_eq!() in const
591            assert!(EtBC::KEY_LEN == PrfBC::KEY_LEN);
592        }
593        if seed.len() != Self::seed_len() {
594            return Err(internal!("Invalid seed length").into());
595        }
596        let slen = CryptState::<EtBC, PrfBC>::seed_len();
597        let (outb, inb, binding) = (&seed[0..slen], &seed[slen..slen * 2], &seed[slen * 2..]);
598        Ok(Self {
599            outbound: CryptState::initialize(outb)?,
600            inbound: CryptState::initialize(inb)?,
601            binding: binding.try_into().expect("Invalid slice length"),
602        })
603    }
604}
605
606impl<EtBC, PrfBC> super::ClientLayer<ClientOutbound<EtBC, PrfBC>, ClientInbound<EtBC, PrfBC>>
607    for CryptStatePair<EtBC, PrfBC>
608where
609    EtBC: BlkCipherDec,
610    PrfBC: BlkCipherEnc,
611{
612    fn split_client_layer(
613        self,
614    ) -> (
615        ClientOutbound<EtBC, PrfBC>,
616        ClientInbound<EtBC, PrfBC>,
617        CircuitBinding,
618    ) {
619        (self.outbound.into(), self.inbound.into(), self.binding)
620    }
621}
622
623impl<EtBC, PrfBC> super::RelayLayer<RelayOutbound<EtBC, PrfBC>, RelayInbound<EtBC, PrfBC>>
624    for CryptStatePair<EtBC, PrfBC>
625where
626    EtBC: BlkCipherEnc,
627    PrfBC: BlkCipherEnc,
628{
629    fn split_relay_layer(
630        self,
631    ) -> (
632        RelayOutbound<EtBC, PrfBC>,
633        RelayInbound<EtBC, PrfBC>,
634        CircuitBinding,
635    ) {
636        (self.outbound.into(), self.inbound.into(), self.binding)
637    }
638}
639
640/// Benchmark utilities for the `cgo` module.
641#[cfg(feature = "bench")]
642pub mod bench_utils {
643    pub use super::ClientInbound;
644    pub use super::ClientOutbound;
645    pub use super::CryptStatePair;
646    pub use super::RelayInbound;
647    pub use super::RelayOutbound;
648
649    /// The throughput for a relay cell in bytes with the CGO scheme.
650    pub const CGO_THROUGHPUT: u64 = 488;
651}
652
653#[cfg(test)]
654mod test {
655    // @@ begin test lint list maintained by maint/add_warning @@
656    #![allow(clippy::bool_assert_comparison)]
657    #![allow(clippy::clone_on_copy)]
658    #![allow(clippy::dbg_macro)]
659    #![allow(clippy::mixed_attributes_style)]
660    #![allow(clippy::print_stderr)]
661    #![allow(clippy::print_stdout)]
662    #![allow(clippy::single_char_pattern)]
663    #![allow(clippy::unwrap_used)]
664    #![allow(clippy::unchecked_time_subtraction)]
665    #![allow(clippy::useless_vec)]
666    #![allow(clippy::needless_pass_by_value)]
667    #![allow(clippy::string_slice)] // See arti#2571
668    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
669
670    use crate::crypto::cell::{
671        InboundRelayLayer, OutboundClientCrypt, OutboundClientLayer, OutboundRelayLayer,
672    };
673
674    use super::*;
675    use hex_literal::hex;
676    use rand::RngExt as _;
677    use tor_basic_utils::test_rng::testing_rng;
678
679    #[test]
680    fn testvec_xor() {
681        let mut b: [u8; 20] = *b"turning and turning ";
682        let s = b"in the widening gyre";
683        xor_into(&mut b, s);
684        assert_eq!(b[..], hex!("1d1b521a010b4757080a014e1d1b154e0e171545"));
685    }
686
687    #[test]
688    fn testvec_polyval() {
689        use polyval::Polyval;
690        use polyval::universal_hash::UniversalHash;
691
692        // Test vectors from RFC8452 worked example in appendix A.
693        let h = hex!("25629347589242761d31f826ba4b757b");
694        let x_1 = hex!("4f4f95668c83dfb6401762bb2d01a262");
695        let x_2 = hex!("d1a24ddd2721d006bbe45f20d3c9f362");
696
697        let mut hash = Polyval::new(&h.into());
698        hash.update(&[x_1.into(), x_2.into()]);
699        let result: [u8; 16] = hash.finalize().into();
700        assert_eq!(result, hex!("f7a3b47b846119fae5b7866cf5e5b77e"));
701    }
702
703    // These True/False constants are here to make our test data parse without changes.
704    #[allow(non_upper_case_globals)]
705    const False: bool = false;
706    #[allow(non_upper_case_globals)]
707    const True: bool = true;
708    include!("../../../testdata/cgo_et.rs");
709    include!("../../../testdata/cgo_prf.rs");
710    include!("../../../testdata/cgo_uiv.rs");
711    include!("../../../testdata/cgo_relay.rs");
712    include!("../../../testdata/cgo_client.rs");
713
714    /// Decode s as a N-byte hex string, or panic.
715    fn unhex<const N: usize>(s: &str) -> [u8; N] {
716        hex::decode(s).unwrap().try_into().unwrap()
717    }
718
719    #[test]
720    fn testvec_et() {
721        for (encrypt, keys, tweak, input, expect_output) in ET_TEST_VECTORS {
722            let keys: [u8; 32] = unhex(keys);
723            let tweak: [u8; et::TLEN_ET] = unhex(tweak);
724            let mut block: [u8; 16] = unhex(input);
725            let expect_output: [u8; 16] = unhex(expect_output);
726            let et: et::EtCipher<Aes128> = et::EtCipher::initialize(&keys).unwrap();
727            let tweak = (
728                tweak[0..16].try_into().unwrap(),
729                tweak[16],
730                &tweak[17..].try_into().unwrap(),
731            );
732            if *encrypt {
733                et.encrypt(tweak, &mut block);
734            } else {
735                et.decrypt(tweak, &mut block);
736            }
737            assert_eq!(block, expect_output);
738        }
739    }
740
741    #[test]
742    fn testvec_prf() {
743        for (keys, offset, tweak, expect_output) in PRF_TEST_VECTORS {
744            let keys: [u8; 32] = unhex(keys);
745            assert!([0, 1].contains(offset));
746            let tweak: [u8; 16] = unhex(tweak);
747            let expect_output = hex::decode(expect_output).unwrap();
748            let prf: prf::Prf<Aes128> = prf::Prf::initialize(&keys).unwrap();
749            if *offset == 0 {
750                assert_eq!(expect_output.len(), CGO_PAYLOAD_LEN);
751                let mut data = [0_u8; CGO_PAYLOAD_LEN];
752                prf.xor_n0_stream(&tweak, &mut data);
753                assert_eq!(expect_output[..], data[..]);
754            } else {
755                let data = prf.get_n1_stream(&tweak, expect_output.len());
756                assert_eq!(expect_output[..], data[..]);
757            }
758        }
759    }
760
761    #[test]
762    fn testvec_uiv() {
763        for (encrypt, keys, tweak, left, right, (expect_left, expect_right)) in UIV_TEST_VECTORS {
764            let keys: [u8; 64] = unhex(keys);
765            let tweak: [u8; 17] = unhex(tweak);
766            let mut cell: [u8; 509] = unhex(&format!("{left}{right}"));
767            let expected: [u8; 509] = unhex(&format!("{expect_left}{expect_right}"));
768
769            let uiv: uiv::Uiv<Aes128, Aes128> = uiv::Uiv::initialize(&keys).unwrap();
770            let htweak = (tweak[0..16].try_into().unwrap(), tweak[16]);
771            if *encrypt {
772                uiv.encrypt(htweak, &mut cell);
773            } else {
774                uiv.decrypt(htweak, &mut cell);
775            }
776            assert_eq!(cell, expected);
777        }
778    }
779
780    #[test]
781    fn testvec_uiv_update() {
782        let mut rng = testing_rng();
783
784        for (keys, nonce, (expect_keys, expect_nonce)) in UIV_UPDATE_TEST_VECTORS {
785            let keys: [u8; 64] = unhex(keys);
786            let mut nonce: [u8; 16] = unhex(nonce);
787            let mut uiv: uiv::Uiv<Aes128, Aes128> = uiv::Uiv::initialize(&keys).unwrap();
788            let expect_keys: [u8; 64] = unhex(expect_keys);
789            let expect_nonce: [u8; 16] = unhex(expect_nonce);
790            uiv.update(&mut nonce);
791            assert_eq!(&nonce, &expect_nonce);
792            assert_eq!(&uiv.keys[..], &expect_keys[..]);
793
794            // Make sure that we can get the same results when we initialize a new UIV with the keys
795            // allegedly used to reinitialize this one.
796            let uiv2: uiv::Uiv<Aes128, Aes128> = uiv::Uiv::initialize(&uiv.keys[..]).unwrap();
797
798            let tweak: [u8; 16] = rng.random();
799            let cmd = rng.random();
800            let mut msg1: [u8; CELL_DATA_LEN] = rng.random();
801            let mut msg2 = msg1.clone();
802
803            uiv.encrypt((&tweak, cmd), &mut msg1);
804            uiv2.encrypt((&tweak, cmd), &mut msg2);
805        }
806    }
807
808    #[test]
809    fn testvec_cgo_relay() {
810        for (inbound, (k, n, tprime), ad, t, c, output) in CGO_RELAY_TEST_VECTORS {
811            let k_n: [u8; 80] = unhex(&format!("{k}{n}"));
812            let tprime: [u8; 16] = unhex(tprime);
813            let ad: [u8; 1] = unhex(ad);
814            let msg: [u8; CELL_DATA_LEN] = unhex(&format!("{t}{c}"));
815            let mut msg = RelayCellBody(Box::new(msg));
816
817            let mut state = CryptState::<Aes128, Aes128>::initialize(&k_n).unwrap();
818            *state.tag = tprime;
819            let state = if *inbound {
820                let mut s = RelayInbound::from(state);
821                s.encrypt_inbound(ad[0].into(), &mut msg);
822                s.0
823            } else {
824                let mut s = RelayOutbound::from(state);
825                s.decrypt_outbound(ad[0].into(), &mut msg);
826                s.0
827            };
828
829            // expected values
830            let ((ex_k, ex_n, ex_tprime), (ex_t, ex_c)) = output;
831            let ex_msg: [u8; CELL_DATA_LEN] = unhex(&format!("{ex_t}{ex_c}"));
832            let ex_k: [u8; 64] = unhex(ex_k);
833            let ex_n: [u8; 16] = unhex(ex_n);
834            let ex_tprime: [u8; 16] = unhex(ex_tprime);
835            assert_eq!(&ex_msg[..], &msg.0[..]);
836            assert_eq!(&state.uiv.keys[..], &ex_k[..]);
837            assert_eq!(&state.nonce[..], &ex_n[..]);
838            assert_eq!(&state.tag[..], &ex_tprime[..]);
839        }
840    }
841
842    #[test]
843    fn testvec_cgo_relay_originate() {
844        for ((k, n, tprime), ad, m, output) in CGO_RELAY_ORIGINATE_TEST_VECTORS {
845            let k_n: [u8; 80] = unhex(&format!("{k}{n}"));
846            let tprime: [u8; 16] = unhex(tprime);
847            let ad: [u8; 1] = unhex(ad);
848            let msg_body: [u8; CGO_PAYLOAD_LEN] = unhex(m);
849            let mut msg = [0_u8; CELL_DATA_LEN];
850            msg[16..].copy_from_slice(&msg_body[..]);
851            let mut msg = RelayCellBody(Box::new(msg));
852
853            let mut state = CryptState::<Aes128, Aes128>::initialize(&k_n).unwrap();
854            *state.tag = tprime;
855            let mut state = RelayInbound::from(state);
856            state.originate(ad[0].into(), &mut msg);
857            let state = state.0;
858
859            let ((ex_k, ex_n, ex_tprime), (ex_t, ex_c)) = output;
860            let ex_msg: [u8; CELL_DATA_LEN] = unhex(&format!("{ex_t}{ex_c}"));
861            let ex_k: [u8; 64] = unhex(ex_k);
862            let ex_n: [u8; 16] = unhex(ex_n);
863            let ex_tprime: [u8; 16] = unhex(ex_tprime);
864            assert_eq!(&ex_msg[..], &msg.0[..]);
865            assert_eq!(&state.uiv.keys[..], &ex_k[..]);
866            assert_eq!(&state.nonce[..], &ex_n[..]);
867            assert_eq!(&state.tag[..], &ex_tprime[..]);
868        }
869    }
870
871    #[test]
872    fn testvec_cgo_client_originate() {
873        for (ss, hop, ad, m, output) in CGO_CLIENT_ORIGINATE_TEST_VECTORS {
874            assert!(*hop > 0); // the test vectors are 1-indexed.
875            let mut client = OutboundClientCrypt::new();
876            let mut individual_layers = Vec::new();
877            for (k, n, tprime) in ss {
878                let k_n: [u8; 80] = unhex(&format!("{k}{n}"));
879                let tprime: [u8; 16] = unhex(tprime);
880                let mut state = CryptState::<Aes128, Aes128>::initialize(&k_n).unwrap();
881                *state.tag = tprime;
882                client.add_layer(Box::new(ClientOutbound::from(state.clone())));
883                individual_layers.push(ClientOutbound::from(state));
884            }
885
886            let ad: [u8; 1] = unhex(ad);
887            let msg_body: [u8; CGO_PAYLOAD_LEN] = unhex(m);
888            let mut msg = [0_u8; CELL_DATA_LEN];
889            msg[16..].copy_from_slice(&msg_body[..]);
890            let mut msg = RelayCellBody(Box::new(msg));
891            let mut msg2 = msg.clone();
892
893            // Encrypt using the OutboundClientCrypt object...
894            client
895                .encrypt(ad[0].into(), &mut msg, (*hop - 1).into())
896                .unwrap();
897            // And a second time manually, using individual_layers.
898            //
899            // (We do this so we can actually inspect that their internal state matches the test vectors.)
900            {
901                let hop_idx = usize::from(*hop) - 1;
902                individual_layers[hop_idx].originate_for(ad[0].into(), &mut msg2);
903                for idx in (0..hop_idx).rev() {
904                    individual_layers[idx].encrypt_outbound(ad[0].into(), &mut msg2);
905                }
906            }
907            assert_eq!(&msg.0[..], &msg2.0[..]);
908
909            let (ex_ss, (ex_t, ex_c)) = output;
910            let ex_msg: [u8; CELL_DATA_LEN] = unhex(&format!("{ex_t}{ex_c}"));
911            assert_eq!(&ex_msg[..], &msg.0[..]);
912
913            for (layer, (ex_k, ex_n, ex_tprime)) in individual_layers.iter().zip(ex_ss.iter()) {
914                let state = &layer.0;
915                let ex_k: [u8; 64] = unhex(ex_k);
916                let ex_n: [u8; 16] = unhex(ex_n);
917                let ex_tprime: [u8; 16] = unhex(ex_tprime);
918
919                assert_eq!(&state.uiv.keys[..], &ex_k[..]);
920                assert_eq!(&state.nonce[..], &ex_n[..]);
921                assert_eq!(&state.tag[..], &ex_tprime);
922            }
923        }
924    }
925}