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#[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
64pub(crate) trait CryptInit: Sized {
67 fn seed_len() -> usize;
69 fn initialize(seed: &[u8]) -> Result<Self>;
71 fn construct<K: super::handshake::KeyGenerator>(keygen: K) -> Result<Self> {
73 let seed = keygen.expand(Self::seed_len())?;
74 Self::initialize(&seed[..])
75 }
76}
77
78pub(crate) trait ClientLayer<F, B>
83where
84 F: OutboundClientLayer,
85 B: InboundClientLayer,
86{
87 fn split_client_layer(self) -> (F, B, CircuitBinding);
90}
91
92#[allow(dead_code)] pub(crate) trait RelayLayer<F, B>
97where
98 F: OutboundRelayLayer,
99 B: InboundRelayLayer,
100{
101 fn split_relay_layer(self) -> (F, B, CircuitBinding);
104}
105
106#[allow(dead_code)] pub(crate) trait InboundRelayLayer {
109 fn originate(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag;
114 fn encrypt_inbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody);
116}
117
118#[allow(dead_code)]
120pub(crate) trait OutboundRelayLayer {
121 fn decrypt_outbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag>;
125}
126
127pub(crate) trait OutboundClientLayer {
130 fn originate_for(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag;
135 fn encrypt_outbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody);
137}
138
139pub(crate) trait InboundClientLayer {
142 fn decrypt_inbound(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag>;
146}
147
148#[derive(Copy, Clone, Eq, PartialEq, Debug, Deftly)]
152#[derive_deftly(HasMemoryCost)]
153pub struct HopNum(u8);
154
155impl HopNum {
156 pub fn display(&self) -> HopNumDisplay {
164 HopNumDisplay(*self)
165 }
166}
167
168#[derive(Copy, Clone, Eq, PartialEq, Debug)]
174pub struct HopNumDisplay(HopNum);
175
176impl std::fmt::Display for HopNumDisplay {
177 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
178 let hop_num: u8 = self.0.into();
179
180 write!(f, "#{}", hop_num + 1)
181 }
182}
183
184impl From<HopNum> for u8 {
185 fn from(hop: HopNum) -> u8 {
186 hop.0
187 }
188}
189
190impl From<u8> for HopNum {
191 fn from(v: u8) -> HopNum {
192 HopNum(v)
193 }
194}
195
196impl From<HopNum> for usize {
197 fn from(hop: HopNum) -> usize {
198 hop.0 as usize
199 }
200}
201
202pub(crate) struct OutboundClientCrypt {
205 layers: Vec<Box<dyn OutboundClientLayer + Send>>,
208}
209
210pub(crate) struct InboundClientCrypt {
213 layers: Vec<Box<dyn InboundClientLayer + Send>>,
216}
217
218impl OutboundClientCrypt {
219 pub(crate) fn new() -> Self {
221 OutboundClientCrypt { layers: Vec::new() }
222 }
223 pub(crate) fn encrypt(
231 &mut self,
232 cmd: ChanCmd,
233 cell: &mut RelayCellBody,
234 hop: HopNum,
235 ) -> Result<SendmeTag> {
236 let hop: usize = hop.into();
237 if hop >= self.layers.len() {
238 return Err(Error::NoSuchHop);
239 }
240
241 let mut layers = self.layers.iter_mut().take(hop + 1).rev();
242 let first_layer = layers.next().ok_or(Error::NoSuchHop)?;
243 let tag = first_layer.originate_for(cmd, cell);
244 for layer in layers {
245 layer.encrypt_outbound(cmd, cell);
246 }
247 Ok(tag)
248 }
249
250 pub(crate) fn add_layer(&mut self, layer: Box<dyn OutboundClientLayer + Send>) {
252 assert!(self.layers.len() < u8::MAX as usize);
253 self.layers.push(layer);
254 }
255
256 pub(crate) fn n_layers(&self) -> usize {
258 self.layers.len()
259 }
260}
261
262impl InboundClientCrypt {
263 pub(crate) fn new() -> Self {
265 InboundClientCrypt { layers: Vec::new() }
266 }
267 pub(crate) fn decrypt(
272 &mut self,
273 cmd: ChanCmd,
274 cell: &mut RelayCellBody,
275 ) -> Result<(HopNum, SendmeTag)> {
276 for (hopnum, layer) in self.layers.iter_mut().enumerate() {
277 if let Some(tag) = layer.decrypt_inbound(cmd, cell) {
278 let hopnum = HopNum(u8::try_from(hopnum).expect("Somehow > 255 hops"));
279 return Ok((hopnum, tag));
280 }
281 }
282 Err(Error::BadCellAuth)
283 }
284 pub(crate) fn add_layer(&mut self, layer: Box<dyn InboundClientLayer + Send>) {
286 assert!(self.layers.len() < u8::MAX as usize);
287 self.layers.push(layer);
288 }
289
290 #[allow(dead_code)]
294 pub(crate) fn n_layers(&self) -> usize {
295 self.layers.len()
296 }
297}
298
299pub(crate) type Tor1RelayCrypto =
301 tor1::CryptStatePair<tor_llcrypto::cipher::aes::Aes128Ctr, tor_llcrypto::d::Sha1>;
302
303#[cfg(feature = "hs-common")]
307pub(crate) type Tor1Hsv3RelayCrypto =
308 tor1::CryptStatePair<tor_llcrypto::cipher::aes::Aes256Ctr, tor_llcrypto::d::Sha3_256>;
309
310#[cfg(test)]
311mod test {
312 #![allow(clippy::bool_assert_comparison)]
314 #![allow(clippy::clone_on_copy)]
315 #![allow(clippy::dbg_macro)]
316 #![allow(clippy::mixed_attributes_style)]
317 #![allow(clippy::print_stderr)]
318 #![allow(clippy::print_stdout)]
319 #![allow(clippy::single_char_pattern)]
320 #![allow(clippy::unwrap_used)]
321 #![allow(clippy::unchecked_duration_subtraction)]
322 #![allow(clippy::useless_vec)]
323 #![allow(clippy::needless_pass_by_value)]
324 use super::*;
327 use rand::{seq::IndexedRandom as _, RngCore};
328 use tor_basic_utils::{test_rng::testing_rng, RngExt as _};
329 use tor_bytes::SecretBuf;
330 use tor_cell::relaycell::RelayCellFormat;
331
332 pub(crate) fn add_layers(
333 cc_out: &mut OutboundClientCrypt,
334 cc_in: &mut InboundClientCrypt,
335 pair: Tor1RelayCrypto,
336 ) {
337 let (outbound, inbound, _) = pair.split_client_layer();
338 cc_out.add_layer(Box::new(outbound));
339 cc_in.add_layer(Box::new(inbound));
340 }
341
342 #[test]
343 fn roundtrip() {
344 use crate::crypto::handshake::ShakeKeyGenerator as KGen;
346 fn s(seed: &[u8]) -> SecretBuf {
347 seed.to_vec().into()
348 }
349
350 let seed1 = s(b"hidden we are free");
351 let seed2 = s(b"free to speak, to free ourselves");
352 let seed3 = s(b"free to hide no more");
353
354 let mut cc_out = OutboundClientCrypt::new();
355 let mut cc_in = InboundClientCrypt::new();
356 let pair = Tor1RelayCrypto::construct(KGen::new(seed1.clone())).unwrap();
357 add_layers(&mut cc_out, &mut cc_in, pair);
358 let pair = Tor1RelayCrypto::construct(KGen::new(seed2.clone())).unwrap();
359 add_layers(&mut cc_out, &mut cc_in, pair);
360 let pair = Tor1RelayCrypto::construct(KGen::new(seed3.clone())).unwrap();
361 add_layers(&mut cc_out, &mut cc_in, pair);
362
363 assert_eq!(cc_in.n_layers(), 3);
364 assert_eq!(cc_out.n_layers(), 3);
365
366 let (mut r1f, mut r1b, _) = Tor1RelayCrypto::construct(KGen::new(seed1))
367 .unwrap()
368 .split_relay_layer();
369 let (mut r2f, mut r2b, _) = Tor1RelayCrypto::construct(KGen::new(seed2))
370 .unwrap()
371 .split_relay_layer();
372 let (mut r3f, mut r3b, _) = Tor1RelayCrypto::construct(KGen::new(seed3))
373 .unwrap()
374 .split_relay_layer();
375 let cmd = ChanCmd::RELAY;
376
377 let mut rng = testing_rng();
378 for _ in 1..300 {
379 let mut cell = Box::new([0_u8; 509]);
381 let mut cell_orig = [0_u8; 509];
382 rng.fill_bytes(&mut cell_orig);
383 cell.copy_from_slice(&cell_orig);
384 let mut cell = cell.into();
385 let _tag = cc_out.encrypt(cmd, &mut cell, 2.into()).unwrap();
386 assert_ne!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
387 assert!(r1f.decrypt_outbound(cmd, &mut cell).is_none());
388 assert!(r2f.decrypt_outbound(cmd, &mut cell).is_none());
389 assert!(r3f.decrypt_outbound(cmd, &mut cell).is_some());
390
391 assert_eq!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
392
393 let mut cell = Box::new([0_u8; 509]);
395 let mut cell_orig = [0_u8; 509];
396 rng.fill_bytes(&mut cell_orig);
397 cell.copy_from_slice(&cell_orig);
398 let mut cell = cell.into();
399
400 r3b.originate(cmd, &mut cell);
401 r2b.encrypt_inbound(cmd, &mut cell);
402 r1b.encrypt_inbound(cmd, &mut cell);
403 let (layer, _tag) = cc_in.decrypt(cmd, &mut cell).unwrap();
404 assert_eq!(layer, 2.into());
405 assert_eq!(&cell.as_ref()[9..], &cell_orig.as_ref()[9..]);
406
407 }
409
410 {
412 let mut cell = Box::new([0_u8; 509]).into();
413 let err = cc_out.encrypt(cmd, &mut cell, 10.into());
414 assert!(matches!(err, Err(Error::NoSuchHop)));
415 }
416
417 {
419 let mut cell = Box::new([0_u8; 509]).into();
420 let err = cc_in.decrypt(cmd, &mut cell);
421 assert!(matches!(err, Err(Error::BadCellAuth)));
422 }
423 }
424
425 #[test]
426 fn hop_num_display() {
427 for i in 0..10 {
428 let hop_num = HopNum::from(i);
429 let expect = format!("#{}", i + 1);
430
431 assert_eq!(expect, hop_num.display().to_string());
432 }
433 }
434
435 fn clean_cell_fields(cell: &mut RelayCellBody, format: RelayCellFormat) {
440 use super::tor1;
441 match format {
442 RelayCellFormat::V0 => {
443 cell.0[tor1::RECOGNIZED_RANGE].fill(0);
444 cell.0[tor1::DIGEST_RANGE].fill(0);
445 }
446 RelayCellFormat::V1 => {
447 cell.0[0..16].fill(0);
448 }
449 _ => {
450 panic!("Unrecognized format!");
451 }
452 }
453 }
454
455 fn test_fwd_one_hop<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
457 where
458 CS: CryptInit + ClientLayer<CF, CB>,
459 RS: CryptInit + RelayLayer<RF, RB>,
460 CF: OutboundClientLayer,
461 CB: InboundClientLayer,
462 RF: OutboundRelayLayer,
463 RB: InboundRelayLayer,
464 {
465 let mut rng = testing_rng();
466 assert_eq!(CS::seed_len(), RS::seed_len());
467 let mut seed = vec![0; CS::seed_len()];
468 rng.fill_bytes(&mut seed[..]);
469 let (mut client, _, _) = CS::initialize(&seed).unwrap().split_client_layer();
470 let (mut relay, _, _) = RS::initialize(&seed).unwrap().split_relay_layer();
471
472 for _ in 0..5 {
473 let mut cell = RelayCellBody(Box::new([0_u8; 509]));
474 rng.fill_bytes(&mut cell.0[..]);
475 clean_cell_fields(&mut cell, format);
476 let msg_orig = cell.clone();
477
478 let ctag = client.originate_for(ChanCmd::RELAY, &mut cell);
479 assert_ne!(cell.0[16..], msg_orig.0[16..]);
480 let rtag = relay.decrypt_outbound(ChanCmd::RELAY, &mut cell);
481 clean_cell_fields(&mut cell, format);
482 assert_eq!(cell.0[..], msg_orig.0[..]);
483 assert_eq!(rtag, Some(ctag));
484 }
485 }
486
487 fn test_rev_one_hop<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
489 where
490 CS: CryptInit + ClientLayer<CF, CB>,
491 RS: CryptInit + RelayLayer<RF, RB>,
492 CF: OutboundClientLayer,
493 CB: InboundClientLayer,
494 RF: OutboundRelayLayer,
495 RB: InboundRelayLayer,
496 {
497 let mut rng = testing_rng();
498 assert_eq!(CS::seed_len(), RS::seed_len());
499 let mut seed = vec![0; CS::seed_len()];
500 rng.fill_bytes(&mut seed[..]);
501 let (_, mut client, _) = CS::initialize(&seed).unwrap().split_client_layer();
502 let (_, mut relay, _) = RS::initialize(&seed).unwrap().split_relay_layer();
503
504 for _ in 0..5 {
505 let mut cell = RelayCellBody(Box::new([0_u8; 509]));
506 rng.fill_bytes(&mut cell.0[..]);
507 clean_cell_fields(&mut cell, format);
508 let msg_orig = cell.clone();
509
510 let rtag = relay.originate(ChanCmd::RELAY, &mut cell);
511 assert_ne!(cell.0[16..], msg_orig.0[16..]);
512 let ctag = client.decrypt_inbound(ChanCmd::RELAY, &mut cell);
513 clean_cell_fields(&mut cell, format);
514 assert_eq!(cell.0[..], msg_orig.0[..]);
515 assert_eq!(ctag, Some(rtag));
516 }
517 }
518
519 fn test_fwd_three_hops_leaky<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
520 where
521 CS: CryptInit + ClientLayer<CF, CB>,
522 RS: CryptInit + RelayLayer<RF, RB>,
523 CF: OutboundClientLayer + Send + 'static,
524 CB: InboundClientLayer,
525 RF: OutboundRelayLayer,
526 RB: InboundRelayLayer,
527 {
528 let mut rng = testing_rng();
529 assert_eq!(CS::seed_len(), RS::seed_len());
530 let mut client = OutboundClientCrypt::new();
531 let mut relays = Vec::new();
532 for _ in 0..3 {
533 let mut seed = vec![0; CS::seed_len()];
534 rng.fill_bytes(&mut seed[..]);
535 let (client_layer, _, _) = CS::initialize(&seed).unwrap().split_client_layer();
536 let (relay_layer, _, _) = RS::initialize(&seed).unwrap().split_relay_layer();
537 client.add_layer(Box::new(client_layer));
538 relays.push(relay_layer);
539 }
540
541 'cell_loop: for _ in 0..32 {
542 let mut cell = RelayCellBody(Box::new([0_u8; 509]));
543 rng.fill_bytes(&mut cell.0[..]);
544 clean_cell_fields(&mut cell, format);
545 let msg_orig = cell.clone();
546 let cmd = *[ChanCmd::RELAY, ChanCmd::RELAY_EARLY]
547 .choose(&mut rng)
548 .unwrap();
549 let hop: u8 = rng.gen_range_checked(0_u8..=2).unwrap();
550
551 let ctag = client.encrypt(cmd, &mut cell, hop.into()).unwrap();
552
553 for r_idx in 0..=hop {
554 let rtag = relays[r_idx as usize].decrypt_outbound(cmd, &mut cell);
555 if let Some(rtag) = rtag {
556 clean_cell_fields(&mut cell, format);
557 assert_eq!(cell.0[..], msg_orig.0[..]);
558 assert_eq!(rtag, ctag);
559 continue 'cell_loop;
560 }
561 }
562 panic!("None of the relays thought that this cell was recognized!");
563 }
564 }
565
566 fn test_rev_three_hops_leaky<CS, RS, CF, CB, RF, RB>(format: RelayCellFormat)
567 where
568 CS: CryptInit + ClientLayer<CF, CB>,
569 RS: CryptInit + RelayLayer<RF, RB>,
570 CF: OutboundClientLayer,
571 CB: InboundClientLayer + Send + 'static,
572 RF: OutboundRelayLayer,
573 RB: InboundRelayLayer,
574 {
575 let mut rng = testing_rng();
576 assert_eq!(CS::seed_len(), RS::seed_len());
577 let mut client = InboundClientCrypt::new();
578 let mut relays = Vec::new();
579 for _ in 0..3 {
580 let mut seed = vec![0; CS::seed_len()];
581 rng.fill_bytes(&mut seed[..]);
582 let (_, client_layer, _) = CS::initialize(&seed).unwrap().split_client_layer();
583 let (_, relay_layer, _) = RS::initialize(&seed).unwrap().split_relay_layer();
584 client.add_layer(Box::new(client_layer));
585 relays.push(relay_layer);
586 }
587
588 for _ in 0..32 {
589 let mut cell = RelayCellBody(Box::new([0_u8; 509]));
590 rng.fill_bytes(&mut cell.0[..]);
591 clean_cell_fields(&mut cell, format);
592 let msg_orig = cell.clone();
593 let cmd = *[ChanCmd::RELAY, ChanCmd::RELAY_EARLY]
594 .choose(&mut rng)
595 .unwrap();
596 let hop: u8 = rng.gen_range_checked(0_u8..=2).unwrap();
597
598 let rtag = relays[hop as usize].originate(cmd, &mut cell);
599 for r_idx in (0..hop.into()).rev() {
600 relays[r_idx as usize].encrypt_inbound(cmd, &mut cell);
601 }
602
603 let (observed_hop, ctag) = client.decrypt(cmd, &mut cell).unwrap();
604 assert_eq!(observed_hop, hop.into());
605 clean_cell_fields(&mut cell, format);
606 assert_eq!(cell.0[..], msg_orig.0[..]);
607 assert_eq!(ctag, rtag);
608 }
609 }
610
611 macro_rules! integration_tests { { $modname:ident($fmt:expr, $ctype:ty, $rtype:ty) } => {
612 mod $modname {
613 use super::*;
614 #[test]
615 fn test_fwd_one_hop() {
616 super::test_fwd_one_hop::<$ctype, $rtype, _, _, _, _>($fmt);
617 }
618 #[test]
619 fn test_rev_one_hop() {
620 super::test_rev_one_hop::<$ctype, $rtype, _, _, _, _>($fmt);
621 }
622 #[test]
623 fn test_fwd_three_hops_leaky() {
624 super::test_fwd_three_hops_leaky::<$ctype, $rtype, _, _, _, _>($fmt);
625 }
626 #[test]
627 fn test_rev_three_hops_leaky() {
628 super::test_rev_three_hops_leaky::<$ctype, $rtype, _, _, _, _>($fmt);
629 }
630 }
631 }}
632
633 integration_tests! { tor1(RelayCellFormat::V0, Tor1RelayCrypto, Tor1RelayCrypto) }
634 #[cfg(feature = "hs-common")]
635 integration_tests! { tor1_hs(RelayCellFormat::V0, Tor1Hsv3RelayCrypto, Tor1Hsv3RelayCrypto) }
636
637 #[cfg(feature = "counter-galois-onion")]
638 integration_tests! {
639 cgo_aes128(RelayCellFormat::V1,
640 cgo::CryptStatePair<aes::Aes128Dec, aes::Aes128Enc>,cgo::CryptStatePair<aes::Aes128Enc, aes::Aes128Enc> )
643 }
644 #[cfg(feature = "counter-galois-onion")]
645 integration_tests! {
646 cgo_aes256(RelayCellFormat::V1,
647 cgo::CryptStatePair<aes::Aes256Dec, aes::Aes256Enc>,cgo::CryptStatePair<aes::Aes256Enc, aes::Aes256Enc> )
650 }
651}