1#[cfg(feature = "bench")]
34pub(crate) mod bench_utils;
35#[cfg(feature = "counter-galois-onion")]
36pub(crate) mod cgo;
37pub(crate) mod tor1;
38
39use crate::{Error, Result};
40use derive_deftly::Deftly;
41use tor_cell::{
42 chancell::{BoxedCellBody, ChanCmd},
43 relaycell::msg::SendmeTag,
44};
45use tor_memquota::derive_deftly_template_HasMemoryCost;
46
47use super::binding::CircuitBinding;
48
49#[cfg_attr(feature = "bench", visibility::make(pub))]
51#[derive(Clone, derive_more::From, derive_more::Into)]
52pub(crate) struct RelayCellBody(BoxedCellBody);
53
54impl AsRef<[u8]> for RelayCellBody {
55 fn as_ref(&self) -> &[u8] {
56 &self.0[..]
57 }
58}
59impl AsMut<[u8]> for RelayCellBody {
60 fn as_mut(&mut self) -> &mut [u8] {
61 &mut self.0[..]
62 }
63}
64
65#[cfg_attr(feature = "bench", visibility::make(pub))]
68pub(crate) trait CryptInit: Sized {
69 fn seed_len() -> usize;
71 fn initialize(seed: &[u8]) -> Result<Self>;
73 fn construct<K: super::handshake::KeyGenerator>(keygen: K) -> Result<Self> {
75 let seed = keygen.expand(Self::seed_len())?;
76 Self::initialize(&seed[..])
77 }
78}
79
80#[cfg_attr(feature = "bench", visibility::make(pub))]
85pub(crate) trait ClientLayer<F, B>
86where
87 F: OutboundClientLayer,
88 B: InboundClientLayer,
89{
90 fn split_client_layer(self) -> (F, B, CircuitBinding);
93}
94
95#[allow(dead_code)] #[cfg_attr(feature = "bench", visibility::make(pub))]
100pub(crate) trait RelayLayer<F, B>
101where
102 F: OutboundRelayLayer,
103 B: InboundRelayLayer,
104{
105 fn split_relay_layer(self) -> (F, B, CircuitBinding);
108}
109
110#[allow(dead_code)] #[cfg_attr(feature = "bench", visibility::make(pub))]
113pub(crate) trait InboundRelayLayer {
114 fn originate(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag;
119 fn encrypt_inbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody);
121}
122
123#[allow(dead_code)]
125#[cfg_attr(feature = "bench", visibility::make(pub))]
126pub(crate) trait OutboundRelayLayer {
127 fn decrypt_outbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag>;
131}
132
133#[cfg_attr(feature = "bench", visibility::make(pub))]
136pub(crate) trait OutboundClientLayer {
137 fn originate_for(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag;
142 fn encrypt_outbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody);
144}
145
146#[cfg_attr(feature = "bench", visibility::make(pub))]
149pub(crate) trait InboundClientLayer {
150 fn decrypt_inbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag>;
154}
155
156#[derive(Copy, Clone, Eq, PartialEq, Debug, Deftly)]
160#[derive_deftly(HasMemoryCost)]
161pub struct HopNum(u8);
162
163impl HopNum {
164 pub fn display(&self) -> HopNumDisplay {
172 HopNumDisplay(*self)
173 }
174}
175
176#[derive(Copy, Clone, Eq, PartialEq, Debug)]
182pub struct HopNumDisplay(HopNum);
183
184impl std::fmt::Display for HopNumDisplay {
185 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
186 let hop_num: u8 = self.0.into();
187
188 write!(f, "#{}", hop_num + 1)
189 }
190}
191
192impl From<HopNum> for u8 {
193 fn from(hop: HopNum) -> u8 {
194 hop.0
195 }
196}
197
198impl From<u8> for HopNum {
199 fn from(v: u8) -> HopNum {
200 HopNum(v)
201 }
202}
203
204impl From<HopNum> for usize {
205 fn from(hop: HopNum) -> usize {
206 hop.0 as usize
207 }
208}
209
210#[cfg_attr(feature = "bench", visibility::make(pub), derive(Default))]
213pub(crate) struct OutboundClientCrypt {
214 layers: Vec<Box<dyn OutboundClientLayer + Send>>,
217}
218
219#[cfg_attr(feature = "bench", visibility::make(pub), derive(Default))]
222pub(crate) struct InboundClientCrypt {
223 layers: Vec<Box<dyn InboundClientLayer + Send>>,
226}
227
228impl OutboundClientCrypt {
229 #[cfg_attr(feature = "bench", visibility::make(pub))]
231 pub(crate) fn new() -> Self {
232 OutboundClientCrypt { layers: Vec::new() }
233 }
234 #[cfg_attr(feature = "bench", visibility::make(pub))]
242 pub(crate) fn encrypt(
243 &mut self,
244 cmd: ChanCmd,
245 cell: &mut RelayCellBody,
246 hop: HopNum,
247 ) -> Result<SendmeTag> {
248 let hop: usize = hop.into();
249 if hop >= self.layers.len() {
250 return Err(Error::NoSuchHop);
251 }
252
253 let mut layers = self.layers.iter_mut().take(hop + 1).rev();
254 let first_layer = layers.next().ok_or(Error::NoSuchHop)?;
255 let tag = first_layer.originate_for(cmd, cell);
256 for layer in layers {
257 layer.encrypt_outbound(cmd, cell);
258 }
259 Ok(tag)
260 }
261
262 pub(crate) fn add_layer(&mut self, layer: Box<dyn OutboundClientLayer + Send>) {
264 assert!(self.layers.len() < u8::MAX as usize);
265 self.layers.push(layer);
266 }
267
268 pub(crate) fn n_layers(&self) -> usize {
270 self.layers.len()
271 }
272}
273
274impl InboundClientCrypt {
275 #[cfg_attr(feature = "bench", visibility::make(pub))]
277 pub(crate) fn new() -> Self {
278 InboundClientCrypt { layers: Vec::new() }
279 }
280 #[cfg_attr(feature = "bench", visibility::make(pub))]
285 pub(crate) fn decrypt(
286 &mut self,
287 cmd: ChanCmd,
288 cell: &mut RelayCellBody,
289 ) -> Result<(HopNum, SendmeTag)> {
290 for (hopnum, layer) in self.layers.iter_mut().enumerate() {
291 if let Some(tag) = layer.decrypt_inbound(cmd, cell) {
292 let hopnum = HopNum(u8::try_from(hopnum).expect("Somehow > 255 hops"));
293 return Ok((hopnum, tag));
294 }
295 }
296 Err(Error::BadCellAuth)
297 }
298 pub(crate) fn add_layer(&mut self, layer: Box<dyn InboundClientLayer + Send>) {
300 assert!(self.layers.len() < u8::MAX as usize);
301 self.layers.push(layer);
302 }
303
304 #[allow(dead_code)]
308 pub(crate) fn n_layers(&self) -> usize {
309 self.layers.len()
310 }
311}
312
313pub(crate) type Tor1RelayCrypto =
315 tor1::CryptStatePair<tor_llcrypto::cipher::aes::Aes128Ctr, tor_llcrypto::d::Sha1>;
316
317#[cfg(feature = "hs-common")]
321pub(crate) type Tor1Hsv3RelayCrypto =
322 tor1::CryptStatePair<tor_llcrypto::cipher::aes::Aes256Ctr, tor_llcrypto::d::Sha3_256>;
323
324#[cfg(feature = "counter-galois-onion")]
330pub(crate) type CgoRelayCrypto = cgo::CryptStatePair<aes::Aes128, aes::Aes128Enc>;
331
332#[cfg(test)]
333mod test {
334 #![allow(clippy::bool_assert_comparison)]
336 #![allow(clippy::clone_on_copy)]
337 #![allow(clippy::dbg_macro)]
338 #![allow(clippy::mixed_attributes_style)]
339 #![allow(clippy::print_stderr)]
340 #![allow(clippy::print_stdout)]
341 #![allow(clippy::single_char_pattern)]
342 #![allow(clippy::unwrap_used)]
343 #![allow(clippy::unchecked_duration_subtraction)]
344 #![allow(clippy::useless_vec)]
345 #![allow(clippy::needless_pass_by_value)]
346 use super::*;
349 use rand::{seq::IndexedRandom as _, RngCore};
350 use tor_basic_utils::{test_rng::testing_rng, RngExt as _};
351 use tor_bytes::SecretBuf;
352 use tor_cell::relaycell::RelayCellFormat;
353
354 pub(crate) fn add_layers(
355 cc_out: &mut OutboundClientCrypt,
356 cc_in: &mut InboundClientCrypt,
357 pair: Tor1RelayCrypto,
358 ) {
359 let (outbound, inbound, _) = pair.split_client_layer();
360 cc_out.add_layer(Box::new(outbound));
361 cc_in.add_layer(Box::new(inbound));
362 }
363
364 #[test]
365 fn roundtrip() {
366 use crate::crypto::handshake::ShakeKeyGenerator as KGen;
368 fn s(seed: &[u8]) -> SecretBuf {
369 seed.to_vec().into()
370 }
371
372 let seed1 = s(b"hidden we are free");
373 let seed2 = s(b"free to speak, to free ourselves");
374 let seed3 = s(b"free to hide no more");
375
376 let mut cc_out = OutboundClientCrypt::new();
377 let mut cc_in = InboundClientCrypt::new();
378 let pair = Tor1RelayCrypto::construct(KGen::new(seed1.clone())).unwrap();
379 add_layers(&mut cc_out, &mut cc_in, pair);
380 let pair = Tor1RelayCrypto::construct(KGen::new(seed2.clone())).unwrap();
381 add_layers(&mut cc_out, &mut cc_in, pair);
382 let pair = Tor1RelayCrypto::construct(KGen::new(seed3.clone())).unwrap();
383 add_layers(&mut cc_out, &mut cc_in, pair);
384
385 assert_eq!(cc_in.n_layers(), 3);
386 assert_eq!(cc_out.n_layers(), 3);
387
388 let (mut r1f, mut r1b, _) = Tor1RelayCrypto::construct(KGen::new(seed1))
389 .unwrap()
390 .split_relay_layer();
391 let (mut r2f, mut r2b, _) = Tor1RelayCrypto::construct(KGen::new(seed2))
392 .unwrap()
393 .split_relay_layer();
394 let (mut r3f, mut r3b, _) = Tor1RelayCrypto::construct(KGen::new(seed3))
395 .unwrap()
396 .split_relay_layer();
397 let cmd = ChanCmd::RELAY;
398
399 let mut rng = testing_rng();
400 for _ in 1..300 {
401 let mut cell = Box::new([0_u8; 509]);
403 let mut cell_orig = [0_u8; 509];
404 rng.fill_bytes(&mut cell_orig);
405 cell.copy_from_slice(&cell_orig);
406 let mut cell = cell.into();
407 let _tag = cc_out.encrypt(cmd, &mut cell, 2.into()).unwrap();
408 assert_ne!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
409 assert!(r1f.decrypt_outbound(cmd, &mut cell).is_none());
410 assert!(r2f.decrypt_outbound(cmd, &mut cell).is_none());
411 assert!(r3f.decrypt_outbound(cmd, &mut cell).is_some());
412
413 assert_eq!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
414
415 let mut cell = Box::new([0_u8; 509]);
417 let mut cell_orig = [0_u8; 509];
418 rng.fill_bytes(&mut cell_orig);
419 cell.copy_from_slice(&cell_orig);
420 let mut cell = cell.into();
421
422 r3b.originate(cmd, &mut cell);
423 r2b.encrypt_inbound(cmd, &mut cell);
424 r1b.encrypt_inbound(cmd, &mut cell);
425 let (layer, _tag) = cc_in.decrypt(cmd, &mut cell).unwrap();
426 assert_eq!(layer, 2.into());
427 assert_eq!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
428
429 }
431
432 {
434 let mut cell = Box::new([0_u8; 509]).into();
435 let err = cc_out.encrypt(cmd, &mut cell, 10.into());
436 assert!(matches!(err, Err(Error::NoSuchHop)));
437 }
438
439 {
441 let mut cell = Box::new([0_u8; 509]).into();
442 let err = cc_in.decrypt(cmd, &mut cell);
443 assert!(matches!(err, Err(Error::BadCellAuth)));
444 }
445 }
446
447 #[test]
448 fn hop_num_display() {
449 for i in 0..10 {
450 let hop_num = HopNum::from(i);
451 let expect = format!("#{}", i + 1);
452
453 assert_eq!(expect, hop_num.display().to_string());
454 }
455 }
456
457 fn clean_cell_fields(cell: &mut RelayCellBody, format: RelayCellFormat) {
462 use super::tor1;
463 match format {
464 RelayCellFormat::V0 => {
465 cell.0[tor1::RECOGNIZED_RANGE].fill(0);
466 cell.0[tor1::DIGEST_RANGE].fill(0);
467 }
468 RelayCellFormat::V1 => {
469 cell.0[0..16].fill(0);
470 }
471 _ => {
472 panic!("Unrecognized format!");
473 }
474 }
475 }
476
477 fn test_fwd_one_hop<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
479 where
480 CS: CryptInit + ClientLayer<CF, CB>,
481 RS: CryptInit + RelayLayer<RF, RB>,
482 CF: OutboundClientLayer,
483 CB: InboundClientLayer,
484 RF: OutboundRelayLayer,
485 RB: InboundRelayLayer,
486 {
487 let mut rng = testing_rng();
488 assert_eq!(CS::seed_len(), RS::seed_len());
489 let mut seed = vec![0; CS::seed_len()];
490 rng.fill_bytes(&mut seed[..]);
491 let (mut client, _, _) = CS::initialize(&seed).unwrap().split_client_layer();
492 let (mut relay, _, _) = RS::initialize(&seed).unwrap().split_relay_layer();
493
494 for _ in 0..5 {
495 let mut cell = RelayCellBody(Box::new([0_u8; 509]));
496 rng.fill_bytes(&mut cell.0[..]);
497 clean_cell_fields(&mut cell, format);
498 let msg_orig = cell.clone();
499
500 let ctag = client.originate_for(ChanCmd::RELAY, &mut cell);
501 assert_ne!(cell.0[16..], msg_orig.0[16..]);
502 let rtag = relay.decrypt_outbound(ChanCmd::RELAY, &mut cell);
503 clean_cell_fields(&mut cell, format);
504 assert_eq!(cell.0[..], msg_orig.0[..]);
505 assert_eq!(rtag, Some(ctag));
506 }
507 }
508
509 fn test_rev_one_hop<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
511 where
512 CS: CryptInit + ClientLayer<CF, CB>,
513 RS: CryptInit + RelayLayer<RF, RB>,
514 CF: OutboundClientLayer,
515 CB: InboundClientLayer,
516 RF: OutboundRelayLayer,
517 RB: InboundRelayLayer,
518 {
519 let mut rng = testing_rng();
520 assert_eq!(CS::seed_len(), RS::seed_len());
521 let mut seed = vec![0; CS::seed_len()];
522 rng.fill_bytes(&mut seed[..]);
523 let (_, mut client, _) = CS::initialize(&seed).unwrap().split_client_layer();
524 let (_, mut relay, _) = RS::initialize(&seed).unwrap().split_relay_layer();
525
526 for _ in 0..5 {
527 let mut cell = RelayCellBody(Box::new([0_u8; 509]));
528 rng.fill_bytes(&mut cell.0[..]);
529 clean_cell_fields(&mut cell, format);
530 let msg_orig = cell.clone();
531
532 let rtag = relay.originate(ChanCmd::RELAY, &mut cell);
533 assert_ne!(cell.0[16..], msg_orig.0[16..]);
534 let ctag = client.decrypt_inbound(ChanCmd::RELAY, &mut cell);
535 clean_cell_fields(&mut cell, format);
536 assert_eq!(cell.0[..], msg_orig.0[..]);
537 assert_eq!(ctag, Some(rtag));
538 }
539 }
540
541 fn test_fwd_three_hops_leaky<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
542 where
543 CS: CryptInit + ClientLayer<CF, CB>,
544 RS: CryptInit + RelayLayer<RF, RB>,
545 CF: OutboundClientLayer + Send + 'static,
546 CB: InboundClientLayer,
547 RF: OutboundRelayLayer,
548 RB: InboundRelayLayer,
549 {
550 let mut rng = testing_rng();
551 assert_eq!(CS::seed_len(), RS::seed_len());
552 let mut client = OutboundClientCrypt::new();
553 let mut relays = Vec::new();
554 for _ in 0..3 {
555 let mut seed = vec![0; CS::seed_len()];
556 rng.fill_bytes(&mut seed[..]);
557 let (client_layer, _, _) = CS::initialize(&seed).unwrap().split_client_layer();
558 let (relay_layer, _, _) = RS::initialize(&seed).unwrap().split_relay_layer();
559 client.add_layer(Box::new(client_layer));
560 relays.push(relay_layer);
561 }
562
563 'cell_loop: for _ in 0..32 {
564 let mut cell = RelayCellBody(Box::new([0_u8; 509]));
565 rng.fill_bytes(&mut cell.0[..]);
566 clean_cell_fields(&mut cell, format);
567 let msg_orig = cell.clone();
568 let cmd = *[ChanCmd::RELAY, ChanCmd::RELAY_EARLY]
569 .choose(&mut rng)
570 .unwrap();
571 let hop: u8 = rng.gen_range_checked(0_u8..=2).unwrap();
572
573 let ctag = client.encrypt(cmd, &mut cell, hop.into()).unwrap();
574
575 for r_idx in 0..=hop {
576 let rtag = relays[r_idx as usize].decrypt_outbound(cmd, &mut cell);
577 if let Some(rtag) = rtag {
578 clean_cell_fields(&mut cell, format);
579 assert_eq!(cell.0[..], msg_orig.0[..]);
580 assert_eq!(rtag, ctag);
581 continue 'cell_loop;
582 }
583 }
584 panic!("None of the relays thought that this cell was recognized!");
585 }
586 }
587
588 fn test_rev_three_hops_leaky<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
589 where
590 CS: CryptInit + ClientLayer<CF, CB>,
591 RS: CryptInit + RelayLayer<RF, RB>,
592 CF: OutboundClientLayer,
593 CB: InboundClientLayer + Send + 'static,
594 RF: OutboundRelayLayer,
595 RB: InboundRelayLayer,
596 {
597 let mut rng = testing_rng();
598 assert_eq!(CS::seed_len(), RS::seed_len());
599 let mut client = InboundClientCrypt::new();
600 let mut relays = Vec::new();
601 for _ in 0..3 {
602 let mut seed = vec![0; CS::seed_len()];
603 rng.fill_bytes(&mut seed[..]);
604 let (_, client_layer, _) = CS::initialize(&seed).unwrap().split_client_layer();
605 let (_, relay_layer, _) = RS::initialize(&seed).unwrap().split_relay_layer();
606 client.add_layer(Box::new(client_layer));
607 relays.push(relay_layer);
608 }
609
610 for _ in 0..32 {
611 let mut cell = RelayCellBody(Box::new([0_u8; 509]));
612 rng.fill_bytes(&mut cell.0[..]);
613 clean_cell_fields(&mut cell, format);
614 let msg_orig = cell.clone();
615 let cmd = *[ChanCmd::RELAY, ChanCmd::RELAY_EARLY]
616 .choose(&mut rng)
617 .unwrap();
618 let hop: u8 = rng.gen_range_checked(0_u8..=2).unwrap();
619
620 let rtag = relays[hop as usize].originate(cmd, &mut cell);
621 for r_idx in (0..hop.into()).rev() {
622 relays[r_idx as usize].encrypt_inbound(cmd, &mut cell);
623 }
624
625 let (observed_hop, ctag) = client.decrypt(cmd, &mut cell).unwrap();
626 assert_eq!(observed_hop, hop.into());
627 clean_cell_fields(&mut cell, format);
628 assert_eq!(cell.0[..], msg_orig.0[..]);
629 assert_eq!(ctag, rtag);
630 }
631 }
632
633 macro_rules! integration_tests { { $modname:ident($fmt:expr, $ctype:ty, $rtype:ty) } => {
634 mod $modname {
635 use super::*;
636 #[test]
637 fn test_fwd_one_hop() {
638 super::test_fwd_one_hop::<$ctype, $rtype, _, _, _, _>($fmt);
639 }
640 #[test]
641 fn test_rev_one_hop() {
642 super::test_rev_one_hop::<$ctype, $rtype, _, _, _, _>($fmt);
643 }
644 #[test]
645 fn test_fwd_three_hops_leaky() {
646 super::test_fwd_three_hops_leaky::<$ctype, $rtype, _, _, _, _>($fmt);
647 }
648 #[test]
649 fn test_rev_three_hops_leaky() {
650 super::test_rev_three_hops_leaky::<$ctype, $rtype, _, _, _, _>($fmt);
651 }
652 }
653 }}
654
655 integration_tests! { tor1(RelayCellFormat::V0, Tor1RelayCrypto, Tor1RelayCrypto) }
656 #[cfg(feature = "hs-common")]
657 integration_tests! { tor1_hs(RelayCellFormat::V0, Tor1Hsv3RelayCrypto, Tor1Hsv3RelayCrypto) }
658
659 #[cfg(feature = "counter-galois-onion")]
660 integration_tests! {
661 cgo_aes128(RelayCellFormat::V1,
662 cgo::CryptStatePair<aes::Aes128Dec, aes::Aes128Enc>,cgo::CryptStatePair<aes::Aes128Enc, aes::Aes128Enc> )
665 }
666 #[cfg(feature = "counter-galois-onion")]
667 integration_tests! {
668 cgo_aes256(RelayCellFormat::V1,
669 cgo::CryptStatePair<aes::Aes256Dec, aes::Aes256Enc>,cgo::CryptStatePair<aes::Aes256Enc, aes::Aes256Enc> )
672 }
673}