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, Ord, PartialOrd)]
160#[derive_deftly(HasMemoryCost)]
161pub struct HopNum(u8);
162
163impl HopNum {
164 pub fn display(&self) -> HopNumDisplay {
172 HopNumDisplay(*self)
173 }
174
175 pub(crate) fn is_first_hop(&self) -> bool {
177 self.0 == 0
178 }
179}
180
181#[derive(Copy, Clone, Eq, PartialEq, Debug)]
187pub struct HopNumDisplay(HopNum);
188
189impl std::fmt::Display for HopNumDisplay {
190 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
191 let hop_num: u8 = self.0.into();
192
193 write!(f, "#{}", hop_num + 1)
194 }
195}
196
197impl From<HopNum> for u8 {
198 fn from(hop: HopNum) -> u8 {
199 hop.0
200 }
201}
202
203impl From<u8> for HopNum {
204 fn from(v: u8) -> HopNum {
205 HopNum(v)
206 }
207}
208
209impl From<HopNum> for usize {
210 fn from(hop: HopNum) -> usize {
211 hop.0 as usize
212 }
213}
214
215#[cfg_attr(feature = "bench", visibility::make(pub), derive(Default))]
218pub(crate) struct OutboundClientCrypt {
219 layers: Vec<Box<dyn OutboundClientLayer + Send>>,
222}
223
224#[cfg_attr(feature = "bench", visibility::make(pub), derive(Default))]
227pub(crate) struct InboundClientCrypt {
228 layers: Vec<Box<dyn InboundClientLayer + Send>>,
231}
232
233impl OutboundClientCrypt {
234 #[cfg_attr(feature = "bench", visibility::make(pub))]
236 pub(crate) fn new() -> Self {
237 OutboundClientCrypt { layers: Vec::new() }
238 }
239 #[cfg_attr(feature = "bench", visibility::make(pub))]
247 pub(crate) fn encrypt(
248 &mut self,
249 cmd: ChanCmd,
250 cell: &mut RelayCellBody,
251 hop: HopNum,
252 ) -> Result<SendmeTag> {
253 let hop: usize = hop.into();
254 if hop >= self.layers.len() {
255 return Err(Error::NoSuchHop);
256 }
257
258 let mut layers = self.layers.iter_mut().take(hop + 1).rev();
259 let first_layer = layers.next().ok_or(Error::NoSuchHop)?;
260 let tag = first_layer.originate_for(cmd, cell);
261 for layer in layers {
262 layer.encrypt_outbound(cmd, cell);
263 }
264 Ok(tag)
265 }
266
267 pub(crate) fn add_layer(&mut self, layer: Box<dyn OutboundClientLayer + Send>) {
269 assert!(self.layers.len() < u8::MAX as usize);
270 self.layers.push(layer);
271 }
272
273 pub(crate) fn n_layers(&self) -> usize {
275 self.layers.len()
276 }
277}
278
279impl InboundClientCrypt {
280 #[cfg_attr(feature = "bench", visibility::make(pub))]
282 pub(crate) fn new() -> Self {
283 InboundClientCrypt { layers: Vec::new() }
284 }
285 #[cfg_attr(feature = "bench", visibility::make(pub))]
290 pub(crate) fn decrypt(
291 &mut self,
292 cmd: ChanCmd,
293 cell: &mut RelayCellBody,
294 ) -> Result<(HopNum, SendmeTag)> {
295 for (hopnum, layer) in self.layers.iter_mut().enumerate() {
296 if let Some(tag) = layer.decrypt_inbound(cmd, cell) {
297 let hopnum = HopNum(u8::try_from(hopnum).expect("Somehow > 255 hops"));
298 return Ok((hopnum, tag));
299 }
300 }
301 Err(Error::BadCellAuth)
302 }
303 pub(crate) fn add_layer(&mut self, layer: Box<dyn InboundClientLayer + Send>) {
305 assert!(self.layers.len() < u8::MAX as usize);
306 self.layers.push(layer);
307 }
308
309 #[allow(dead_code)]
313 pub(crate) fn n_layers(&self) -> usize {
314 self.layers.len()
315 }
316}
317
318pub(crate) type Tor1RelayCrypto =
320 tor1::CryptStatePair<tor_llcrypto::cipher::aes::Aes128Ctr, tor_llcrypto::d::Sha1>;
321
322#[cfg(feature = "hs-common")]
326pub(crate) type Tor1Hsv3RelayCrypto =
327 tor1::CryptStatePair<tor_llcrypto::cipher::aes::Aes256Ctr, tor_llcrypto::d::Sha3_256>;
328
329#[cfg(feature = "counter-galois-onion")]
335pub(crate) type CgoRelayCrypto = cgo::CryptStatePair<aes::Aes128, aes::Aes128Enc>;
336
337#[cfg(test)]
338mod test {
339 #![allow(clippy::bool_assert_comparison)]
341 #![allow(clippy::clone_on_copy)]
342 #![allow(clippy::dbg_macro)]
343 #![allow(clippy::mixed_attributes_style)]
344 #![allow(clippy::print_stderr)]
345 #![allow(clippy::print_stdout)]
346 #![allow(clippy::single_char_pattern)]
347 #![allow(clippy::unwrap_used)]
348 #![allow(clippy::unchecked_time_subtraction)]
349 #![allow(clippy::useless_vec)]
350 #![allow(clippy::needless_pass_by_value)]
351 #![allow(clippy::string_slice)] use super::*;
355 use rand::{Rng, seq::IndexedRandom as _};
356 use tor_basic_utils::{RngExt as _, test_rng::testing_rng};
357 use tor_bytes::SecretBuf;
358 use tor_cell::relaycell::RelayCellFormat;
359
360 pub(crate) fn add_layers(
361 cc_out: &mut OutboundClientCrypt,
362 cc_in: &mut InboundClientCrypt,
363 pair: Tor1RelayCrypto,
364 ) {
365 let (outbound, inbound, _) = pair.split_client_layer();
366 cc_out.add_layer(Box::new(outbound));
367 cc_in.add_layer(Box::new(inbound));
368 }
369
370 #[test]
371 fn roundtrip() {
372 use crate::crypto::handshake::ShakeKeyGenerator as KGen;
374 fn s(seed: &[u8]) -> SecretBuf {
375 seed.to_vec().into()
376 }
377
378 let seed1 = s(b"hidden we are free");
379 let seed2 = s(b"free to speak, to free ourselves");
380 let seed3 = s(b"free to hide no more");
381
382 let mut cc_out = OutboundClientCrypt::new();
383 let mut cc_in = InboundClientCrypt::new();
384 let pair = Tor1RelayCrypto::construct(KGen::new(seed1.clone())).unwrap();
385 add_layers(&mut cc_out, &mut cc_in, pair);
386 let pair = Tor1RelayCrypto::construct(KGen::new(seed2.clone())).unwrap();
387 add_layers(&mut cc_out, &mut cc_in, pair);
388 let pair = Tor1RelayCrypto::construct(KGen::new(seed3.clone())).unwrap();
389 add_layers(&mut cc_out, &mut cc_in, pair);
390
391 assert_eq!(cc_in.n_layers(), 3);
392 assert_eq!(cc_out.n_layers(), 3);
393
394 let (mut r1f, mut r1b, _) = Tor1RelayCrypto::construct(KGen::new(seed1))
395 .unwrap()
396 .split_relay_layer();
397 let (mut r2f, mut r2b, _) = Tor1RelayCrypto::construct(KGen::new(seed2))
398 .unwrap()
399 .split_relay_layer();
400 let (mut r3f, mut r3b, _) = Tor1RelayCrypto::construct(KGen::new(seed3))
401 .unwrap()
402 .split_relay_layer();
403 let cmd = ChanCmd::RELAY;
404
405 let mut rng = testing_rng();
406 for _ in 1..300 {
407 let mut cell = Box::new([0_u8; 509]);
409 let mut cell_orig = [0_u8; 509];
410 rng.fill_bytes(&mut cell_orig);
411 cell.copy_from_slice(&cell_orig);
412 let mut cell = cell.into();
413 let _tag = cc_out.encrypt(cmd, &mut cell, 2.into()).unwrap();
414 assert_ne!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
415 assert!(r1f.decrypt_outbound(cmd, &mut cell).is_none());
416 assert!(r2f.decrypt_outbound(cmd, &mut cell).is_none());
417 assert!(r3f.decrypt_outbound(cmd, &mut cell).is_some());
418
419 assert_eq!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
420
421 let mut cell = Box::new([0_u8; 509]);
423 let mut cell_orig = [0_u8; 509];
424 rng.fill_bytes(&mut cell_orig);
425 cell.copy_from_slice(&cell_orig);
426 let mut cell = cell.into();
427
428 r3b.originate(cmd, &mut cell);
429 r2b.encrypt_inbound(cmd, &mut cell);
430 r1b.encrypt_inbound(cmd, &mut cell);
431 let (layer, _tag) = cc_in.decrypt(cmd, &mut cell).unwrap();
432 assert_eq!(layer, 2.into());
433 assert_eq!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
434
435 }
437
438 {
440 let mut cell = Box::new([0_u8; 509]).into();
441 let err = cc_out.encrypt(cmd, &mut cell, 10.into());
442 assert!(matches!(err, Err(Error::NoSuchHop)));
443 }
444
445 {
447 let mut cell = Box::new([0_u8; 509]).into();
448 let err = cc_in.decrypt(cmd, &mut cell);
449 assert!(matches!(err, Err(Error::BadCellAuth)));
450 }
451 }
452
453 #[test]
454 fn hop_num_display() {
455 for i in 0..10 {
456 let hop_num = HopNum::from(i);
457 let expect = format!("#{}", i + 1);
458
459 assert_eq!(expect, hop_num.display().to_string());
460 }
461 }
462
463 fn clean_cell_fields(cell: &mut RelayCellBody, format: RelayCellFormat) {
468 use super::tor1;
469 match format {
470 RelayCellFormat::V0 => {
471 cell.0[tor1::RECOGNIZED_RANGE].fill(0);
472 cell.0[tor1::DIGEST_RANGE].fill(0);
473 }
474 RelayCellFormat::V1 => {
475 cell.0[0..16].fill(0);
476 }
477 _ => {
478 panic!("Unrecognized format!");
479 }
480 }
481 }
482
483 fn test_fwd_one_hop<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
485 where
486 CS: CryptInit + ClientLayer<CF, CB>,
487 RS: CryptInit + RelayLayer<RF, RB>,
488 CF: OutboundClientLayer,
489 CB: InboundClientLayer,
490 RF: OutboundRelayLayer,
491 RB: InboundRelayLayer,
492 {
493 let mut rng = testing_rng();
494 assert_eq!(CS::seed_len(), RS::seed_len());
495 let mut seed = vec![0; CS::seed_len()];
496 rng.fill_bytes(&mut seed[..]);
497 let (mut client, _, _) = CS::initialize(&seed).unwrap().split_client_layer();
498 let (mut relay, _, _) = RS::initialize(&seed).unwrap().split_relay_layer();
499
500 for _ in 0..5 {
501 let mut cell = RelayCellBody(Box::new([0_u8; 509]));
502 rng.fill_bytes(&mut cell.0[..]);
503 clean_cell_fields(&mut cell, format);
504 let msg_orig = cell.clone();
505
506 let ctag = client.originate_for(ChanCmd::RELAY, &mut cell);
507 assert_ne!(cell.0[16..], msg_orig.0[16..]);
508 let rtag = relay.decrypt_outbound(ChanCmd::RELAY, &mut cell);
509 clean_cell_fields(&mut cell, format);
510 assert_eq!(cell.0[..], msg_orig.0[..]);
511 assert_eq!(rtag, Some(ctag));
512 }
513 }
514
515 fn test_rev_one_hop<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
517 where
518 CS: CryptInit + ClientLayer<CF, CB>,
519 RS: CryptInit + RelayLayer<RF, RB>,
520 CF: OutboundClientLayer,
521 CB: InboundClientLayer,
522 RF: OutboundRelayLayer,
523 RB: InboundRelayLayer,
524 {
525 let mut rng = testing_rng();
526 assert_eq!(CS::seed_len(), RS::seed_len());
527 let mut seed = vec![0; CS::seed_len()];
528 rng.fill_bytes(&mut seed[..]);
529 let (_, mut client, _) = CS::initialize(&seed).unwrap().split_client_layer();
530 let (_, mut relay, _) = RS::initialize(&seed).unwrap().split_relay_layer();
531
532 for _ in 0..5 {
533 let mut cell = RelayCellBody(Box::new([0_u8; 509]));
534 rng.fill_bytes(&mut cell.0[..]);
535 clean_cell_fields(&mut cell, format);
536 let msg_orig = cell.clone();
537
538 let rtag = relay.originate(ChanCmd::RELAY, &mut cell);
539 assert_ne!(cell.0[16..], msg_orig.0[16..]);
540 let ctag = client.decrypt_inbound(ChanCmd::RELAY, &mut cell);
541 clean_cell_fields(&mut cell, format);
542 assert_eq!(cell.0[..], msg_orig.0[..]);
543 assert_eq!(ctag, Some(rtag));
544 }
545 }
546
547 fn test_fwd_three_hops_leaky<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
548 where
549 CS: CryptInit + ClientLayer<CF, CB>,
550 RS: CryptInit + RelayLayer<RF, RB>,
551 CF: OutboundClientLayer + Send + 'static,
552 CB: InboundClientLayer,
553 RF: OutboundRelayLayer,
554 RB: InboundRelayLayer,
555 {
556 let mut rng = testing_rng();
557 assert_eq!(CS::seed_len(), RS::seed_len());
558 let mut client = OutboundClientCrypt::new();
559 let mut relays = Vec::new();
560 for _ in 0..3 {
561 let mut seed = vec![0; CS::seed_len()];
562 rng.fill_bytes(&mut seed[..]);
563 let (client_layer, _, _) = CS::initialize(&seed).unwrap().split_client_layer();
564 let (relay_layer, _, _) = RS::initialize(&seed).unwrap().split_relay_layer();
565 client.add_layer(Box::new(client_layer));
566 relays.push(relay_layer);
567 }
568
569 'cell_loop: for _ in 0..32 {
570 let mut cell = RelayCellBody(Box::new([0_u8; 509]));
571 rng.fill_bytes(&mut cell.0[..]);
572 clean_cell_fields(&mut cell, format);
573 let msg_orig = cell.clone();
574 let cmd = *[ChanCmd::RELAY, ChanCmd::RELAY_EARLY]
575 .choose(&mut rng)
576 .unwrap();
577 let hop: u8 = rng.gen_range_checked(0_u8..=2).unwrap();
578
579 let ctag = client.encrypt(cmd, &mut cell, hop.into()).unwrap();
580
581 for r_idx in 0..=hop {
582 let rtag = relays[r_idx as usize].decrypt_outbound(cmd, &mut cell);
583 if let Some(rtag) = rtag {
584 clean_cell_fields(&mut cell, format);
585 assert_eq!(cell.0[..], msg_orig.0[..]);
586 assert_eq!(rtag, ctag);
587 continue 'cell_loop;
588 }
589 }
590 panic!("None of the relays thought that this cell was recognized!");
591 }
592 }
593
594 fn test_rev_three_hops_leaky<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
595 where
596 CS: CryptInit + ClientLayer<CF, CB>,
597 RS: CryptInit + RelayLayer<RF, RB>,
598 CF: OutboundClientLayer,
599 CB: InboundClientLayer + Send + 'static,
600 RF: OutboundRelayLayer,
601 RB: InboundRelayLayer,
602 {
603 let mut rng = testing_rng();
604 assert_eq!(CS::seed_len(), RS::seed_len());
605 let mut client = InboundClientCrypt::new();
606 let mut relays = Vec::new();
607 for _ in 0..3 {
608 let mut seed = vec![0; CS::seed_len()];
609 rng.fill_bytes(&mut seed[..]);
610 let (_, client_layer, _) = CS::initialize(&seed).unwrap().split_client_layer();
611 let (_, relay_layer, _) = RS::initialize(&seed).unwrap().split_relay_layer();
612 client.add_layer(Box::new(client_layer));
613 relays.push(relay_layer);
614 }
615
616 for _ in 0..32 {
617 let mut cell = RelayCellBody(Box::new([0_u8; 509]));
618 rng.fill_bytes(&mut cell.0[..]);
619 clean_cell_fields(&mut cell, format);
620 let msg_orig = cell.clone();
621 let cmd = *[ChanCmd::RELAY, ChanCmd::RELAY_EARLY]
622 .choose(&mut rng)
623 .unwrap();
624 let hop: u8 = rng.gen_range_checked(0_u8..=2).unwrap();
625
626 let rtag = relays[hop as usize].originate(cmd, &mut cell);
627 for r_idx in (0..hop.into()).rev() {
628 relays[r_idx as usize].encrypt_inbound(cmd, &mut cell);
629 }
630
631 let (observed_hop, ctag) = client.decrypt(cmd, &mut cell).unwrap();
632 assert_eq!(observed_hop, hop.into());
633 clean_cell_fields(&mut cell, format);
634 assert_eq!(cell.0[..], msg_orig.0[..]);
635 assert_eq!(ctag, rtag);
636 }
637 }
638
639 macro_rules! integration_tests { { $modname:ident($fmt:expr, $ctype:ty, $rtype:ty) } => {
640 mod $modname {
641 use super::*;
642 #[test]
643 fn test_fwd_one_hop() {
644 super::test_fwd_one_hop::<$ctype, $rtype, _, _, _, _>($fmt);
645 }
646 #[test]
647 fn test_rev_one_hop() {
648 super::test_rev_one_hop::<$ctype, $rtype, _, _, _, _>($fmt);
649 }
650 #[test]
651 fn test_fwd_three_hops_leaky() {
652 super::test_fwd_three_hops_leaky::<$ctype, $rtype, _, _, _, _>($fmt);
653 }
654 #[test]
655 fn test_rev_three_hops_leaky() {
656 super::test_rev_three_hops_leaky::<$ctype, $rtype, _, _, _, _>($fmt);
657 }
658 }
659 }}
660
661 integration_tests! { tor1(RelayCellFormat::V0, Tor1RelayCrypto, Tor1RelayCrypto) }
662 #[cfg(feature = "hs-common")]
663 integration_tests! { tor1_hs(RelayCellFormat::V0, Tor1Hsv3RelayCrypto, Tor1Hsv3RelayCrypto) }
664
665 #[cfg(feature = "counter-galois-onion")]
666 integration_tests! {
667 cgo_aes128(RelayCellFormat::V1,
668 cgo::CryptStatePair<aes::Aes128Dec, aes::Aes128Enc>,cgo::CryptStatePair<aes::Aes128Enc, aes::Aes128Enc> )
671 }
672 #[cfg(feature = "counter-galois-onion")]
673 integration_tests! {
674 cgo_aes256(RelayCellFormat::V1,
675 cgo::CryptStatePair<aes::Aes256Dec, aes::Aes256Enc>,cgo::CryptStatePair<aes::Aes256Enc, aes::Aes256Enc> )
678 }
679}