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