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
53#[cfg_attr(feature = "bench", visibility::make(pub))]
57pub(crate) struct CryptStatePair<SC: StreamCipher, D: Digest + Clone> {
58 fwd: CryptState<SC, D>,
60 back: CryptState<SC, D>,
62 binding: CircuitBinding,
64}
65
66impl<SC: StreamCipher + KeyIvInit, D: Digest + Clone> CryptInit for CryptStatePair<SC, D> {
67 fn seed_len() -> usize {
68 SC::KeySize::to_usize() * 2 + D::OutputSize::to_usize() * 2 + CIRC_BINDING_LEN
69 }
70 fn initialize(mut seed: &[u8]) -> Result<Self> {
71 if seed.len() != Self::seed_len() {
74 return Err(Error::from(internal!(
75 "seed length {} was invalid",
76 seed.len()
77 )));
78 }
79
80 let mut take_seed = |n: usize| -> &[u8] {
82 let res = &seed[..n];
83 seed = &seed[n..];
84 res
85 };
86
87 let dlen = D::OutputSize::to_usize();
88 let keylen = SC::KeySize::to_usize();
89
90 let df = take_seed(dlen);
91 let db = take_seed(dlen);
92 let kf = take_seed(keylen);
93 let kb = take_seed(keylen);
94 let binding_key = take_seed(CIRC_BINDING_LEN);
95
96 let fwd = CryptState {
97 cipher: SC::new(kf.into(), &Default::default()),
98 digest: D::new().chain_update(df),
99 last_sendme_tag: [0_u8; SENDME_TAG_LEN].into(),
100 };
101 let back = CryptState {
102 cipher: SC::new(kb.into(), &Default::default()),
103 digest: D::new().chain_update(db),
104 last_sendme_tag: [0_u8; SENDME_TAG_LEN].into(),
105 };
106 let binding = CircuitBinding::try_from(binding_key)?;
107
108 Ok(CryptStatePair { fwd, back, binding })
109 }
110}
111
112impl<SC, D> ClientLayer<ClientOutbound<SC, D>, ClientInbound<SC, D>> for CryptStatePair<SC, D>
113where
114 SC: StreamCipher,
115 D: Digest + Clone,
116{
117 fn split_client_layer(self) -> (ClientOutbound<SC, D>, ClientInbound<SC, D>, CircuitBinding) {
118 (self.fwd.into(), self.back.into(), self.binding)
119 }
120}
121
122#[cfg_attr(feature = "bench", visibility::make(pub))]
124#[derive(derive_more::From)]
125pub(crate) struct RelayInbound<SC: StreamCipher, D: Digest + Clone>(CryptState<SC, D>);
126impl<SC: StreamCipher, D: Digest + Clone> InboundRelayLayer for RelayInbound<SC, D> {
127 fn originate(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag {
128 cell.set_digest::<_>(&mut self.0.digest, &mut self.0.last_sendme_tag);
129 self.encrypt_inbound(cmd, cell);
130 self.0.last_sendme_tag
131 }
132 fn encrypt_inbound(&mut self, _cmd: ChanCmd, cell: &mut RelayCellBody) {
133 self.0.cipher.apply_keystream(cell.as_mut());
135 }
136}
137
138#[cfg_attr(feature = "bench", visibility::make(pub))]
140#[derive(derive_more::From)]
141pub(crate) struct RelayOutbound<SC: StreamCipher, D: Digest + Clone>(CryptState<SC, D>);
142impl<SC: StreamCipher, D: Digest + Clone> OutboundRelayLayer for RelayOutbound<SC, D> {
143 fn decrypt_outbound(&mut self, _cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag> {
144 self.0.cipher.apply_keystream(cell.as_mut());
146 if cell.is_recognized::<_>(&mut self.0.digest, &mut self.0.last_sendme_tag) {
147 Some(self.0.last_sendme_tag)
148 } else {
149 None
150 }
151 }
152}
153impl<SC: StreamCipher, D: Digest + Clone> RelayLayer<RelayOutbound<SC, D>, RelayInbound<SC, D>>
154 for CryptStatePair<SC, D>
155{
156 fn split_relay_layer(self) -> (RelayOutbound<SC, D>, RelayInbound<SC, D>, CircuitBinding) {
157 let CryptStatePair { fwd, back, binding } = self;
158 (fwd.into(), back.into(), binding)
159 }
160}
161
162#[cfg_attr(feature = "bench", visibility::make(pub))]
164#[derive(derive_more::From)]
165pub(crate) struct ClientOutbound<SC: StreamCipher, D: Digest + Clone>(CryptState<SC, D>);
166
167impl<SC: StreamCipher, D: Digest + Clone> OutboundClientLayer for ClientOutbound<SC, D> {
168 fn originate_for(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag {
169 cell.set_digest::<_>(&mut self.0.digest, &mut self.0.last_sendme_tag);
170 self.encrypt_outbound(cmd, cell);
171 self.0.last_sendme_tag
172 }
173 fn encrypt_outbound(&mut self, _cmd: ChanCmd, cell: &mut RelayCellBody) {
174 self.0.cipher.apply_keystream(&mut cell.0[..]);
177 }
178}
179
180#[cfg_attr(feature = "bench", visibility::make(pub))]
182#[derive(derive_more::From)]
183pub(crate) struct ClientInbound<SC: StreamCipher, D: Digest + Clone>(CryptState<SC, D>);
184impl<SC: StreamCipher, D: Digest + Clone> InboundClientLayer for ClientInbound<SC, D> {
185 fn decrypt_inbound(&mut self, _cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag> {
186 self.0.cipher.apply_keystream(&mut cell.0[..]);
189 if cell.is_recognized::<_>(&mut self.0.digest, &mut self.0.last_sendme_tag) {
190 Some(self.0.last_sendme_tag)
191 } else {
192 None
193 }
194 }
195}
196
197pub(super) const RECOGNIZED_RANGE: std::ops::Range<usize> = 1..3;
199pub(super) const DIGEST_RANGE: std::ops::Range<usize> = 5..9;
201pub(super) const EMPTY_DIGEST: &[u8] = &[0, 0, 0, 0];
203
204impl RelayCellBody {
220 fn recognized(&self) -> &[u8] {
222 &self.0[RECOGNIZED_RANGE]
223 }
224 fn recognized_mut(&mut self) -> &mut [u8] {
226 &mut self.0[RECOGNIZED_RANGE]
227 }
228 fn digest(&self) -> &[u8] {
230 &self.0[DIGEST_RANGE]
231 }
232 fn digest_mut(&mut self) -> &mut [u8] {
234 &mut self.0[DIGEST_RANGE]
235 }
236 #[cfg_attr(feature = "bench", visibility::make(pub))]
238 fn set_digest<D: Digest + Clone>(&mut self, d: &mut D, sendme_tag: &mut SendmeTag) {
239 self.recognized_mut().fill(0); self.digest_mut().fill(0); d.update(&self.0[..]);
243 let computed_digest = d.clone().finalize();
245 *sendme_tag = SendmeTag::try_from(&computed_digest[..SENDME_TAG_LEN])
247 .expect("Somehow produced a SENDME tag of invalid length!");
248 let used_digest_prefix = &computed_digest[0..DIGEST_RANGE.len()];
249 self.digest_mut().copy_from_slice(used_digest_prefix);
250 }
251 #[cfg_attr(feature = "bench", visibility::make(pub))]
263 fn is_recognized<D: Digest + Clone>(&self, d: &mut D, rcvd: &mut SendmeTag) -> bool {
264 use crate::util::ct;
265
266 if !ct::is_zero(self.recognized()) {
268 return false;
269 }
270
271 let mut dtmp = d.clone();
274 dtmp.update(&self.0[..DIGEST_RANGE.start]);
276 dtmp.update(EMPTY_DIGEST);
278 dtmp.update(&self.0[DIGEST_RANGE.end..]);
280 let dtmp_clone = dtmp.clone();
283 let result = dtmp.finalize();
284
285 if ct::bytes_eq(self.digest(), &result[0..DIGEST_RANGE.len()]) {
286 *d = dtmp_clone;
288 *rcvd = SendmeTag::try_from(&result[..SENDME_TAG_LEN])
289 .expect("Somehow generated a sendme tag of invalid length!");
290 return true;
291 }
292
293 false
294 }
295}
296
297#[cfg(feature = "bench")]
299pub mod bench_utils {
300 pub use super::ClientInbound;
301 pub use super::ClientOutbound;
302 pub use super::CryptStatePair;
303 pub use super::RelayInbound;
304 pub use super::RelayOutbound;
305
306 pub const TOR1_THROUGHPUT: u64 = 498;
308}
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 crate::crypto::cell::{
327 test::add_layers, InboundClientCrypt, OutboundClientCrypt, Tor1RelayCrypto,
328 };
329
330 use super::*;
331
332 #[test]
335 fn testvec() {
336 use digest::XofReader;
337 use digest::{ExtendableOutput, Update};
338
339 const K1: &[u8; 92] =
341 b" 'My public key is in this signed x509 object', said Tom assertively. (N-PREG-VIRYL)";
342 const K2: &[u8; 92] =
343 b"'Let's chart the pedal phlanges in the tomb', said Tom cryptographically. (PELCG-GBR-TENCU)";
344 const K3: &[u8; 92] =
345 b" 'Segmentation fault bugs don't _just happen_', said Tom seethingly. (P-GUVAT-YL)";
346
347 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.";
348 let cmd = ChanCmd::RELAY;
349
350 let data: &[(usize, &str)] = &include!("../../../testdata/cell_crypt.rs");
352
353 let mut cc_out = OutboundClientCrypt::new();
354 let mut cc_in = InboundClientCrypt::new();
355 let pair = Tor1RelayCrypto::initialize(&K1[..]).unwrap();
356 add_layers(&mut cc_out, &mut cc_in, pair);
357 let pair = Tor1RelayCrypto::initialize(&K2[..]).unwrap();
358 add_layers(&mut cc_out, &mut cc_in, pair);
359 let pair = Tor1RelayCrypto::initialize(&K3[..]).unwrap();
360 add_layers(&mut cc_out, &mut cc_in, pair);
361
362 let mut xof = tor_llcrypto::d::Shake256::default();
363 xof.update(&SEED[..]);
364 let mut stream = xof.finalize_xof();
365
366 let mut j = 0;
367 for cellno in 0..51 {
368 let mut body = Box::new([0_u8; 509]);
369 body[0] = 2; body[4] = 1; body[9] = 1; body[10] = 242;
373 stream.read(&mut body[11..]);
374
375 let mut cell = body.into();
376 let _ = cc_out.encrypt(cmd, &mut cell, 2.into());
377
378 if cellno == data[j].0 {
379 let expected = hex::decode(data[j].1).unwrap();
380 assert_eq!(cell.as_ref(), &expected[..]);
381 j += 1;
382 }
383 }
384 }
385}