1use crate::{Error, Result, client::circuit::CircuitBinding, crypto::binding::CIRC_BINDING_LEN};
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(
98 kf.try_into().expect("Incorrect size, despite validation!"),
99 &Default::default(),
100 ),
101 digest: D::new().chain_update(df),
102 last_sendme_tag: [0_u8; SENDME_TAG_LEN].into(),
103 };
104 let back = CryptState {
105 cipher: SC::new(
106 kb.try_into().expect("Incorrect size, despite validation!"),
107 &Default::default(),
108 ),
109 digest: D::new().chain_update(db),
110 last_sendme_tag: [0_u8; SENDME_TAG_LEN].into(),
111 };
112 let binding = CircuitBinding::try_from(binding_key)?;
113
114 Ok(CryptStatePair { fwd, back, binding })
115 }
116}
117
118impl<SC, D> ClientLayer<ClientOutbound<SC, D>, ClientInbound<SC, D>> for CryptStatePair<SC, D>
119where
120 SC: StreamCipher,
121 D: Digest + Clone,
122{
123 fn split_client_layer(self) -> (ClientOutbound<SC, D>, ClientInbound<SC, D>, CircuitBinding) {
124 (self.fwd.into(), self.back.into(), self.binding)
125 }
126}
127
128#[cfg_attr(feature = "bench", visibility::make(pub))]
130#[derive(derive_more::From)]
131pub(crate) struct RelayInbound<SC: StreamCipher, D: Digest + Clone>(CryptState<SC, D>);
132impl<SC: StreamCipher, D: Digest + Clone> InboundRelayLayer for RelayInbound<SC, D> {
133 fn originate(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag {
134 cell.set_digest::<_>(&mut self.0.digest, &mut self.0.last_sendme_tag);
135 self.encrypt_inbound(cmd, cell);
136 self.0.last_sendme_tag
137 }
138 fn encrypt_inbound(&mut self, _cmd: ChanCmd, cell: &mut RelayCellBody) {
139 self.0.cipher.apply_keystream(cell.as_mut());
141 }
142}
143
144#[cfg_attr(feature = "bench", visibility::make(pub))]
146#[derive(derive_more::From)]
147pub(crate) struct RelayOutbound<SC: StreamCipher, D: Digest + Clone>(CryptState<SC, D>);
148impl<SC: StreamCipher, D: Digest + Clone> OutboundRelayLayer for RelayOutbound<SC, D> {
149 fn decrypt_outbound(&mut self, _cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag> {
150 self.0.cipher.apply_keystream(cell.as_mut());
152 if cell.is_recognized::<_>(&mut self.0.digest, &mut self.0.last_sendme_tag) {
153 Some(self.0.last_sendme_tag)
154 } else {
155 None
156 }
157 }
158}
159impl<SC: StreamCipher, D: Digest + Clone> RelayLayer<RelayOutbound<SC, D>, RelayInbound<SC, D>>
160 for CryptStatePair<SC, D>
161{
162 fn split_relay_layer(self) -> (RelayOutbound<SC, D>, RelayInbound<SC, D>, CircuitBinding) {
163 let CryptStatePair { fwd, back, binding } = self;
164 (fwd.into(), back.into(), binding)
165 }
166}
167
168#[cfg_attr(feature = "bench", visibility::make(pub))]
170#[derive(derive_more::From)]
171pub(crate) struct ClientOutbound<SC: StreamCipher, D: Digest + Clone>(CryptState<SC, D>);
172
173impl<SC: StreamCipher, D: Digest + Clone> OutboundClientLayer for ClientOutbound<SC, D> {
174 fn originate_for(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag {
175 cell.set_digest::<_>(&mut self.0.digest, &mut self.0.last_sendme_tag);
176 self.encrypt_outbound(cmd, cell);
177 self.0.last_sendme_tag
178 }
179 fn encrypt_outbound(&mut self, _cmd: ChanCmd, cell: &mut RelayCellBody) {
180 self.0.cipher.apply_keystream(&mut cell.0[..]);
183 }
184}
185
186#[cfg_attr(feature = "bench", visibility::make(pub))]
188#[derive(derive_more::From)]
189pub(crate) struct ClientInbound<SC: StreamCipher, D: Digest + Clone>(CryptState<SC, D>);
190impl<SC: StreamCipher, D: Digest + Clone> InboundClientLayer for ClientInbound<SC, D> {
191 fn decrypt_inbound(&mut self, _cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag> {
192 self.0.cipher.apply_keystream(&mut cell.0[..]);
195 if cell.is_recognized::<_>(&mut self.0.digest, &mut self.0.last_sendme_tag) {
196 Some(self.0.last_sendme_tag)
197 } else {
198 None
199 }
200 }
201}
202
203pub(super) const RECOGNIZED_RANGE: std::ops::Range<usize> = 1..3;
205pub(super) const DIGEST_RANGE: std::ops::Range<usize> = 5..9;
207pub(super) const EMPTY_DIGEST: &[u8] = &[0, 0, 0, 0];
209
210impl RelayCellBody {
226 fn recognized(&self) -> &[u8] {
228 &self.0[RECOGNIZED_RANGE]
229 }
230 fn recognized_mut(&mut self) -> &mut [u8] {
232 &mut self.0[RECOGNIZED_RANGE]
233 }
234 fn digest(&self) -> &[u8] {
236 &self.0[DIGEST_RANGE]
237 }
238 fn digest_mut(&mut self) -> &mut [u8] {
240 &mut self.0[DIGEST_RANGE]
241 }
242 #[cfg_attr(feature = "bench", visibility::make(pub))]
244 fn set_digest<D: Digest + Clone>(&mut self, d: &mut D, sendme_tag: &mut SendmeTag) {
245 self.recognized_mut().fill(0); self.digest_mut().fill(0); d.update(&self.0[..]);
249 let computed_digest = d.clone().finalize();
251 *sendme_tag = SendmeTag::try_from(&computed_digest[..SENDME_TAG_LEN])
253 .expect("Somehow produced a SENDME tag of invalid length!");
254 let used_digest_prefix = &computed_digest[0..DIGEST_RANGE.len()];
255 self.digest_mut().copy_from_slice(used_digest_prefix);
256 }
257 #[cfg_attr(feature = "bench", visibility::make(pub))]
269 fn is_recognized<D: Digest + Clone>(&self, d: &mut D, rcvd: &mut SendmeTag) -> bool {
270 use crate::util::ct;
271
272 if !ct::is_zero(self.recognized()) {
274 return false;
275 }
276
277 let mut dtmp = d.clone();
280 dtmp.update(&self.0[..DIGEST_RANGE.start]);
282 dtmp.update(EMPTY_DIGEST);
284 dtmp.update(&self.0[DIGEST_RANGE.end..]);
286 let dtmp_clone = dtmp.clone();
289 let result = dtmp.finalize();
290
291 if ct::bytes_eq(self.digest(), &result[0..DIGEST_RANGE.len()]) {
292 *d = dtmp_clone;
294 *rcvd = SendmeTag::try_from(&result[..SENDME_TAG_LEN])
295 .expect("Somehow generated a sendme tag of invalid length!");
296 return true;
297 }
298
299 false
300 }
301}
302
303#[cfg(feature = "bench")]
305pub mod bench_utils {
306 pub use super::ClientInbound;
307 pub use super::ClientOutbound;
308 pub use super::CryptStatePair;
309 pub use super::RelayInbound;
310 pub use super::RelayOutbound;
311
312 pub const TOR1_THROUGHPUT: u64 = 498;
314}
315
316#[cfg(test)]
317mod test {
318 #![allow(clippy::bool_assert_comparison)]
320 #![allow(clippy::clone_on_copy)]
321 #![allow(clippy::dbg_macro)]
322 #![allow(clippy::mixed_attributes_style)]
323 #![allow(clippy::print_stderr)]
324 #![allow(clippy::print_stdout)]
325 #![allow(clippy::single_char_pattern)]
326 #![allow(clippy::unwrap_used)]
327 #![allow(clippy::unchecked_time_subtraction)]
328 #![allow(clippy::useless_vec)]
329 #![allow(clippy::needless_pass_by_value)]
330 #![allow(clippy::string_slice)] use crate::crypto::cell::{
334 InboundClientCrypt, OutboundClientCrypt, Tor1RelayCrypto, test::add_layers,
335 };
336
337 use super::*;
338
339 #[test]
342 fn testvec() {
343 use digest::XofReader;
344 use digest::{ExtendableOutput, Update};
345
346 const K1: &[u8; 92] =
348 b" 'My public key is in this signed x509 object', said Tom assertively. (N-PREG-VIRYL)";
349 const K2: &[u8; 92] =
350 b"'Let's chart the pedal phlanges in the tomb', said Tom cryptographically. (PELCG-GBR-TENCU)";
351 const K3: &[u8; 92] =
352 b" 'Segmentation fault bugs don't _just happen_', said Tom seethingly. (P-GUVAT-YL)";
353
354 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.";
355 let cmd = ChanCmd::RELAY;
356
357 let data: &[(usize, &str)] = &include!("../../../testdata/cell_crypt.rs");
359
360 let mut cc_out = OutboundClientCrypt::new();
361 let mut cc_in = InboundClientCrypt::new();
362 let pair = Tor1RelayCrypto::initialize(&K1[..]).unwrap();
363 add_layers(&mut cc_out, &mut cc_in, pair);
364 let pair = Tor1RelayCrypto::initialize(&K2[..]).unwrap();
365 add_layers(&mut cc_out, &mut cc_in, pair);
366 let pair = Tor1RelayCrypto::initialize(&K3[..]).unwrap();
367 add_layers(&mut cc_out, &mut cc_in, pair);
368
369 let mut xof = tor_llcrypto::d::Shake256::default();
370 xof.update(&SEED[..]);
371 let mut stream = xof.finalize_xof();
372
373 let mut j = 0;
374 for cellno in 0..51 {
375 let mut body = Box::new([0_u8; 509]);
376 body[0] = 2; body[4] = 1; body[9] = 1; body[10] = 242;
380 stream.read(&mut body[11..]);
381
382 let mut cell = body.into();
383 let _ = cc_out.encrypt(cmd, &mut cell, 2.into());
384
385 if cellno == data[j].0 {
386 let expected = hex::decode(data[j].1).unwrap();
387 assert_eq!(cell.as_ref(), &expected[..]);
388 j += 1;
389 }
390 }
391 }
392}