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