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