1use crate::{circuit::CircuitBinding, crypto::binding::CIRC_BINDING_LEN, Error, Result};
12
13use cipher::{KeyIvInit, StreamCipher};
14use digest::Digest;
15use tor_cell::{chancell::ChanCmd, relaycell::msg::SendmeTag};
16use tor_error::internal;
17use typenum::Unsigned;
18
19use super::{
20 ClientLayer, CryptInit, InboundClientLayer, InboundRelayLayer, OutboundClientLayer,
21 OutboundRelayLayer, RelayCellBody, RelayLayer,
22};
23
24const SENDME_TAG_LEN: usize = 20;
26
27struct CryptState<SC: StreamCipher, D: Digest + Clone> {
41 cipher: SC,
45 digest: D,
49 last_sendme_tag: SendmeTag,
51}
52
53pub(crate) struct CryptStatePair<SC: StreamCipher, D: Digest + Clone> {
57 fwd: CryptState<SC, D>,
59 back: CryptState<SC, D>,
61 binding: CircuitBinding,
63}
64
65impl<SC: StreamCipher + KeyIvInit, D: Digest + Clone> CryptInit for CryptStatePair<SC, D> {
66 fn seed_len() -> usize {
67 SC::KeySize::to_usize() * 2 + D::OutputSize::to_usize() * 2 + CIRC_BINDING_LEN
68 }
69 fn initialize(mut seed: &[u8]) -> Result<Self> {
70 if seed.len() != Self::seed_len() {
73 return Err(Error::from(internal!(
74 "seed length {} was invalid",
75 seed.len()
76 )));
77 }
78
79 let mut take_seed = |n: usize| -> &[u8] {
81 let res = &seed[..n];
82 seed = &seed[n..];
83 res
84 };
85
86 let dlen = D::OutputSize::to_usize();
87 let keylen = SC::KeySize::to_usize();
88
89 let df = take_seed(dlen);
90 let db = take_seed(dlen);
91 let kf = take_seed(keylen);
92 let kb = take_seed(keylen);
93 let binding_key = take_seed(CIRC_BINDING_LEN);
94
95 let fwd = CryptState {
96 cipher: SC::new(kf.into(), &Default::default()),
97 digest: D::new().chain_update(df),
98 last_sendme_tag: [0_u8; SENDME_TAG_LEN].into(),
99 };
100 let back = CryptState {
101 cipher: SC::new(kb.into(), &Default::default()),
102 digest: D::new().chain_update(db),
103 last_sendme_tag: [0_u8; SENDME_TAG_LEN].into(),
104 };
105 let binding = CircuitBinding::try_from(binding_key)?;
106
107 Ok(CryptStatePair { fwd, back, binding })
108 }
109}
110
111impl<SC, D> ClientLayer<ClientOutbound<SC, D>, ClientInbound<SC, D>> for CryptStatePair<SC, D>
112where
113 SC: StreamCipher,
114 D: Digest + Clone,
115{
116 fn split_client_layer(self) -> (ClientOutbound<SC, D>, ClientInbound<SC, D>, CircuitBinding) {
117 (self.fwd.into(), self.back.into(), self.binding)
118 }
119}
120
121#[derive(derive_more::From)]
123pub(crate) struct RelayInbound<SC: StreamCipher, D: Digest + Clone>(CryptState<SC, D>);
124impl<SC: StreamCipher, D: Digest + Clone> InboundRelayLayer for RelayInbound<SC, D> {
125 fn originate(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag {
126 cell.set_digest::<_>(&mut self.0.digest, &mut self.0.last_sendme_tag);
127 self.encrypt_inbound(cmd, cell);
128 self.0.last_sendme_tag
129 }
130 fn encrypt_inbound(&mut self, _cmd: ChanCmd, cell: &mut RelayCellBody) {
131 self.0.cipher.apply_keystream(cell.as_mut());
133 }
134}
135
136#[derive(derive_more::From)]
138pub(crate) struct RelayOutbound<SC: StreamCipher, D: Digest + Clone>(CryptState<SC, D>);
139impl<SC: StreamCipher, D: Digest + Clone> OutboundRelayLayer for RelayOutbound<SC, D> {
140 fn decrypt_outbound(&mut self, _cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag> {
141 self.0.cipher.apply_keystream(cell.as_mut());
143 if cell.is_recognized::<_>(&mut self.0.digest, &mut self.0.last_sendme_tag) {
144 Some(self.0.last_sendme_tag)
145 } else {
146 None
147 }
148 }
149}
150impl<SC: StreamCipher, D: Digest + Clone> RelayLayer<RelayOutbound<SC, D>, RelayInbound<SC, D>>
151 for CryptStatePair<SC, D>
152{
153 fn split_relay_layer(self) -> (RelayOutbound<SC, D>, RelayInbound<SC, D>, CircuitBinding) {
154 let CryptStatePair { fwd, back, binding } = self;
155 (fwd.into(), back.into(), binding)
156 }
157}
158
159#[derive(derive_more::From)]
161pub(crate) struct ClientOutbound<SC: StreamCipher, D: Digest + Clone>(CryptState<SC, D>);
162
163impl<SC: StreamCipher, D: Digest + Clone> OutboundClientLayer for ClientOutbound<SC, D> {
164 fn originate_for(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag {
165 cell.set_digest::<_>(&mut self.0.digest, &mut self.0.last_sendme_tag);
166 self.encrypt_outbound(cmd, cell);
167 self.0.last_sendme_tag
168 }
169 fn encrypt_outbound(&mut self, _cmd: ChanCmd, cell: &mut RelayCellBody) {
170 self.0.cipher.apply_keystream(&mut cell.0[..]);
173 }
174}
175
176#[derive(derive_more::From)]
178pub(crate) struct ClientInbound<SC: StreamCipher, D: Digest + Clone>(CryptState<SC, D>);
179impl<SC: StreamCipher, D: Digest + Clone> InboundClientLayer for ClientInbound<SC, D> {
180 fn decrypt_inbound(&mut self, _cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag> {
181 self.0.cipher.apply_keystream(&mut cell.0[..]);
184 if cell.is_recognized::<_>(&mut self.0.digest, &mut self.0.last_sendme_tag) {
185 Some(self.0.last_sendme_tag)
186 } else {
187 None
188 }
189 }
190}
191
192pub(super) const RECOGNIZED_RANGE: std::ops::Range<usize> = 1..3;
194pub(super) const DIGEST_RANGE: std::ops::Range<usize> = 5..9;
196pub(super) const EMPTY_DIGEST: &[u8] = &[0, 0, 0, 0];
198
199impl RelayCellBody {
215 fn recognized(&self) -> &[u8] {
217 &self.0[RECOGNIZED_RANGE]
218 }
219 fn recognized_mut(&mut self) -> &mut [u8] {
221 &mut self.0[RECOGNIZED_RANGE]
222 }
223 fn digest(&self) -> &[u8] {
225 &self.0[DIGEST_RANGE]
226 }
227 fn digest_mut(&mut self) -> &mut [u8] {
229 &mut self.0[DIGEST_RANGE]
230 }
231 fn set_digest<D: Digest + Clone>(&mut self, d: &mut D, sendme_tag: &mut SendmeTag) {
233 self.recognized_mut().fill(0); self.digest_mut().fill(0); d.update(&self.0[..]);
237 let computed_digest = d.clone().finalize();
239 *sendme_tag = SendmeTag::try_from(&computed_digest[..SENDME_TAG_LEN])
241 .expect("Somehow produced a SENDME tag of invalid length!");
242 let used_digest_prefix = &computed_digest[0..DIGEST_RANGE.len()];
243 self.digest_mut().copy_from_slice(used_digest_prefix);
244 }
245 fn is_recognized<D: Digest + Clone>(&self, d: &mut D, rcvd: &mut SendmeTag) -> bool {
257 use crate::util::ct;
258
259 if !ct::is_zero(self.recognized()) {
261 return false;
262 }
263
264 let mut dtmp = d.clone();
267 dtmp.update(&self.0[..DIGEST_RANGE.start]);
269 dtmp.update(EMPTY_DIGEST);
271 dtmp.update(&self.0[DIGEST_RANGE.end..]);
273 let dtmp_clone = dtmp.clone();
276 let result = dtmp.finalize();
277
278 if ct::bytes_eq(self.digest(), &result[0..DIGEST_RANGE.len()]) {
279 *d = dtmp_clone;
281 *rcvd = SendmeTag::try_from(&result[..SENDME_TAG_LEN])
282 .expect("Somehow generated a sendme tag of invalid length!");
283 return true;
284 }
285
286 false
287 }
288}
289
290#[cfg(feature = "bench")]
292pub mod bench_utils {
293 use super::*;
294
295 use crate::crypto::handshake::ShakeKeyGenerator as KGen;
296 use crate::{
297 bench_utils::{InboundClientLayerWrapper, OutboundClientLayerWrapper, RelayCryptState},
298 crypto::cell::bench_utils::RelayBody,
299 Result,
300 };
301 use tor_bytes::SecretBuf;
302
303 pub const TOR1_THROUGHPUT: u64 = 498;
305
306 pub struct Tor1ClientCryptState<SC: StreamCipher, D: Digest + Clone> {
308 fwd: ClientOutbound<SC, D>,
310 back: ClientInbound<SC, D>,
312 }
313
314 impl<SC: StreamCipher + KeyIvInit, D: Digest + Clone> Tor1ClientCryptState<SC, D> {
315 pub fn construct(seed: SecretBuf) -> Result<Self> {
317 let (outbound, inbound, _) =
318 CryptStatePair::construct(KGen::new(seed))?.split_client_layer();
319 Ok(Self {
320 back: inbound,
321 fwd: outbound,
322 })
323 }
324 }
325
326 impl<SC, D> From<Tor1ClientCryptState<SC, D>> for OutboundClientLayerWrapper
327 where
328 SC: StreamCipher + Send + 'static,
329 D: Digest + Clone + Send + 'static,
330 {
331 fn from(state: Tor1ClientCryptState<SC, D>) -> Self {
332 Self(Box::new(state.fwd))
333 }
334 }
335
336 impl<SC, D> From<Tor1ClientCryptState<SC, D>> for InboundClientLayerWrapper
337 where
338 SC: StreamCipher + Send + 'static,
339 D: Digest + Clone + Send + 'static,
340 {
341 fn from(value: Tor1ClientCryptState<SC, D>) -> Self {
342 Self(Box::new(value.back))
343 }
344 }
345
346 pub struct Tor1RelayCryptState<SC: StreamCipher, D: Digest + Clone> {
348 fwd: RelayOutbound<SC, D>,
350 back: RelayInbound<SC, D>,
352 }
353
354 impl<SC: StreamCipher + KeyIvInit, D: Digest + Clone> Tor1RelayCryptState<SC, D> {
355 pub fn construct(seed: SecretBuf) -> Result<Self> {
357 let (outbound, inbound, _) =
358 CryptStatePair::construct(KGen::new(seed))?.split_relay_layer();
359 Ok(Self {
360 back: inbound,
361 fwd: outbound,
362 })
363 }
364 }
365
366 impl<SC: StreamCipher + KeyIvInit, D: Digest + Clone> RelayCryptState
367 for Tor1RelayCryptState<SC, D>
368 {
369 fn originate(&mut self, cell: &mut RelayBody) {
370 let cell = &mut cell.0;
371 self.back.originate(ChanCmd::RELAY, cell);
372 }
373
374 fn encrypt(&mut self, cell: &mut RelayBody) {
375 let cell = &mut cell.0;
376 self.back.encrypt_inbound(ChanCmd::RELAY, cell);
377 }
378
379 fn decrypt(&mut self, cell: &mut RelayBody) {
380 let cell = &mut cell.0;
381 self.fwd.decrypt_outbound(ChanCmd::RELAY, cell);
382 }
383 }
384
385 pub fn set_digest<D: Digest + Clone>(
387 relay_body: &mut RelayBody,
388 d: &mut D,
389 used_digest: &mut SendmeTag,
390 ) {
391 relay_body.0.set_digest::<D>(d, used_digest);
392 }
393
394 pub fn is_recognized<D: Digest + Clone>(
396 relay_body: &RelayBody,
397 d: &mut D,
398 rcvd: &mut SendmeTag,
399 ) -> bool {
400 relay_body.0.is_recognized::<D>(d, rcvd)
401 }
402}
403
404#[cfg(test)]
405mod test {
406 #![allow(clippy::bool_assert_comparison)]
408 #![allow(clippy::clone_on_copy)]
409 #![allow(clippy::dbg_macro)]
410 #![allow(clippy::mixed_attributes_style)]
411 #![allow(clippy::print_stderr)]
412 #![allow(clippy::print_stdout)]
413 #![allow(clippy::single_char_pattern)]
414 #![allow(clippy::unwrap_used)]
415 #![allow(clippy::unchecked_duration_subtraction)]
416 #![allow(clippy::useless_vec)]
417 #![allow(clippy::needless_pass_by_value)]
418 use crate::crypto::cell::{
421 test::add_layers, InboundClientCrypt, OutboundClientCrypt, Tor1RelayCrypto,
422 };
423
424 use super::*;
425
426 #[test]
429 fn testvec() {
430 use digest::XofReader;
431 use digest::{ExtendableOutput, Update};
432
433 const K1: &[u8; 92] =
435 b" 'My public key is in this signed x509 object', said Tom assertively. (N-PREG-VIRYL)";
436 const K2: &[u8; 92] =
437 b"'Let's chart the pedal phlanges in the tomb', said Tom cryptographically. (PELCG-GBR-TENCU)";
438 const K3: &[u8; 92] =
439 b" 'Segmentation fault bugs don't _just happen_', said Tom seethingly. (P-GUVAT-YL)";
440
441 const SEED: &[u8;108] = b"'You mean to tell me that there's a version of Sha-3 with no limit on the output length?', said Tom shakily.";
442 let cmd = ChanCmd::RELAY;
443
444 let data: &[(usize, &str)] = &include!("../../../testdata/cell_crypt.rs");
446
447 let mut cc_out = OutboundClientCrypt::new();
448 let mut cc_in = InboundClientCrypt::new();
449 let pair = Tor1RelayCrypto::initialize(&K1[..]).unwrap();
450 add_layers(&mut cc_out, &mut cc_in, pair);
451 let pair = Tor1RelayCrypto::initialize(&K2[..]).unwrap();
452 add_layers(&mut cc_out, &mut cc_in, pair);
453 let pair = Tor1RelayCrypto::initialize(&K3[..]).unwrap();
454 add_layers(&mut cc_out, &mut cc_in, pair);
455
456 let mut xof = tor_llcrypto::d::Shake256::default();
457 xof.update(&SEED[..]);
458 let mut stream = xof.finalize_xof();
459
460 let mut j = 0;
461 for cellno in 0..51 {
462 let mut body = Box::new([0_u8; 509]);
463 body[0] = 2; body[4] = 1; body[9] = 1; body[10] = 242;
467 stream.read(&mut body[11..]);
468
469 let mut cell = body.into();
470 let _ = cc_out.encrypt(cmd, &mut cell, 2.into());
471
472 if cellno == data[j].0 {
473 let expected = hex::decode(data[j].1).unwrap();
474 assert_eq!(cell.as_ref(), &expected[..]);
475 j += 1;
476 }
477 }
478 }
479}