use crate::{Error, Result, client::circuit::CircuitBinding, crypto::binding::CIRC_BINDING_LEN};
use cipher::{KeyIvInit, StreamCipher};
use digest::Digest;
use tor_cell::{chancell::ChanCmd, relaycell::msg::SendmeTag};
use tor_error::internal;
use typenum::Unsigned;
use super::{
ClientLayer, CryptInit, InboundClientLayer, InboundRelayLayer, OutboundClientLayer,
OutboundRelayLayer, RelayCellBody, RelayLayer,
};
const SENDME_TAG_LEN: usize = 20;
struct CryptState<SC: StreamCipher, D: Digest + Clone> {
cipher: SC,
digest: D,
last_sendme_tag: SendmeTag,
}
#[cfg_attr(feature = "bench", visibility::make(pub))]
pub(crate) struct CryptStatePair<SC: StreamCipher, D: Digest + Clone> {
fwd: CryptState<SC, D>,
back: CryptState<SC, D>,
binding: CircuitBinding,
}
impl<SC: StreamCipher + KeyIvInit, D: Digest + Clone> CryptInit for CryptStatePair<SC, D> {
fn seed_len() -> usize {
SC::KeySize::to_usize() * 2 + D::OutputSize::to_usize() * 2 + CIRC_BINDING_LEN
}
fn initialize(mut seed: &[u8]) -> Result<Self> {
if seed.len() != Self::seed_len() {
return Err(Error::from(internal!(
"seed length {} was invalid",
seed.len()
)));
}
let mut take_seed = |n: usize| -> &[u8] {
let res = &seed[..n];
seed = &seed[n..];
res
};
let dlen = D::OutputSize::to_usize();
let keylen = SC::KeySize::to_usize();
let df = take_seed(dlen);
let db = take_seed(dlen);
let kf = take_seed(keylen);
let kb = take_seed(keylen);
let binding_key = take_seed(CIRC_BINDING_LEN);
let fwd = CryptState {
cipher: SC::new(kf.into(), &Default::default()),
digest: D::new().chain_update(df),
last_sendme_tag: [0_u8; SENDME_TAG_LEN].into(),
};
let back = CryptState {
cipher: SC::new(kb.into(), &Default::default()),
digest: D::new().chain_update(db),
last_sendme_tag: [0_u8; SENDME_TAG_LEN].into(),
};
let binding = CircuitBinding::try_from(binding_key)?;
Ok(CryptStatePair { fwd, back, binding })
}
}
impl<SC, D> ClientLayer<ClientOutbound<SC, D>, ClientInbound<SC, D>> for CryptStatePair<SC, D>
where
SC: StreamCipher,
D: Digest + Clone,
{
fn split_client_layer(self) -> (ClientOutbound<SC, D>, ClientInbound<SC, D>, CircuitBinding) {
(self.fwd.into(), self.back.into(), self.binding)
}
}
#[cfg_attr(feature = "bench", visibility::make(pub))]
#[derive(derive_more::From)]
pub(crate) struct RelayInbound<SC: StreamCipher, D: Digest + Clone>(CryptState<SC, D>);
impl<SC: StreamCipher, D: Digest + Clone> InboundRelayLayer for RelayInbound<SC, D> {
fn originate(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag {
cell.set_digest::<_>(&mut self.0.digest, &mut self.0.last_sendme_tag);
self.encrypt_inbound(cmd, cell);
self.0.last_sendme_tag
}
fn encrypt_inbound(&mut self, _cmd: ChanCmd, cell: &mut RelayCellBody) {
self.0.cipher.apply_keystream(cell.as_mut());
}
}
#[cfg_attr(feature = "bench", visibility::make(pub))]
#[derive(derive_more::From)]
pub(crate) struct RelayOutbound<SC: StreamCipher, D: Digest + Clone>(CryptState<SC, D>);
impl<SC: StreamCipher, D: Digest + Clone> OutboundRelayLayer for RelayOutbound<SC, D> {
fn decrypt_outbound(&mut self, _cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag> {
self.0.cipher.apply_keystream(cell.as_mut());
if cell.is_recognized::<_>(&mut self.0.digest, &mut self.0.last_sendme_tag) {
Some(self.0.last_sendme_tag)
} else {
None
}
}
}
impl<SC: StreamCipher, D: Digest + Clone> RelayLayer<RelayOutbound<SC, D>, RelayInbound<SC, D>>
for CryptStatePair<SC, D>
{
fn split_relay_layer(self) -> (RelayOutbound<SC, D>, RelayInbound<SC, D>, CircuitBinding) {
let CryptStatePair { fwd, back, binding } = self;
(fwd.into(), back.into(), binding)
}
}
#[cfg_attr(feature = "bench", visibility::make(pub))]
#[derive(derive_more::From)]
pub(crate) struct ClientOutbound<SC: StreamCipher, D: Digest + Clone>(CryptState<SC, D>);
impl<SC: StreamCipher, D: Digest + Clone> OutboundClientLayer for ClientOutbound<SC, D> {
fn originate_for(&mut self, cmd: ChanCmd, cell: &mut RelayCellBody) -> SendmeTag {
cell.set_digest::<_>(&mut self.0.digest, &mut self.0.last_sendme_tag);
self.encrypt_outbound(cmd, cell);
self.0.last_sendme_tag
}
fn encrypt_outbound(&mut self, _cmd: ChanCmd, cell: &mut RelayCellBody) {
self.0.cipher.apply_keystream(&mut cell.0[..]);
}
}
#[cfg_attr(feature = "bench", visibility::make(pub))]
#[derive(derive_more::From)]
pub(crate) struct ClientInbound<SC: StreamCipher, D: Digest + Clone>(CryptState<SC, D>);
impl<SC: StreamCipher, D: Digest + Clone> InboundClientLayer for ClientInbound<SC, D> {
fn decrypt_inbound(&mut self, _cmd: ChanCmd, cell: &mut RelayCellBody) -> Option<SendmeTag> {
self.0.cipher.apply_keystream(&mut cell.0[..]);
if cell.is_recognized::<_>(&mut self.0.digest, &mut self.0.last_sendme_tag) {
Some(self.0.last_sendme_tag)
} else {
None
}
}
}
pub(super) const RECOGNIZED_RANGE: std::ops::Range<usize> = 1..3;
pub(super) const DIGEST_RANGE: std::ops::Range<usize> = 5..9;
pub(super) const EMPTY_DIGEST: &[u8] = &[0, 0, 0, 0];
impl RelayCellBody {
fn recognized(&self) -> &[u8] {
&self.0[RECOGNIZED_RANGE]
}
fn recognized_mut(&mut self) -> &mut [u8] {
&mut self.0[RECOGNIZED_RANGE]
}
fn digest(&self) -> &[u8] {
&self.0[DIGEST_RANGE]
}
fn digest_mut(&mut self) -> &mut [u8] {
&mut self.0[DIGEST_RANGE]
}
#[cfg_attr(feature = "bench", visibility::make(pub))]
fn set_digest<D: Digest + Clone>(&mut self, d: &mut D, sendme_tag: &mut SendmeTag) {
self.recognized_mut().fill(0); self.digest_mut().fill(0);
d.update(&self.0[..]);
let computed_digest = d.clone().finalize();
*sendme_tag = SendmeTag::try_from(&computed_digest[..SENDME_TAG_LEN])
.expect("Somehow produced a SENDME tag of invalid length!");
let used_digest_prefix = &computed_digest[0..DIGEST_RANGE.len()];
self.digest_mut().copy_from_slice(used_digest_prefix);
}
#[cfg_attr(feature = "bench", visibility::make(pub))]
fn is_recognized<D: Digest + Clone>(&self, d: &mut D, rcvd: &mut SendmeTag) -> bool {
use crate::util::ct;
if !ct::is_zero(self.recognized()) {
return false;
}
let mut dtmp = d.clone();
dtmp.update(&self.0[..DIGEST_RANGE.start]);
dtmp.update(EMPTY_DIGEST);
dtmp.update(&self.0[DIGEST_RANGE.end..]);
let dtmp_clone = dtmp.clone();
let result = dtmp.finalize();
if ct::bytes_eq(self.digest(), &result[0..DIGEST_RANGE.len()]) {
*d = dtmp_clone;
*rcvd = SendmeTag::try_from(&result[..SENDME_TAG_LEN])
.expect("Somehow generated a sendme tag of invalid length!");
return true;
}
false
}
}
#[cfg(feature = "bench")]
pub mod bench_utils {
pub use super::ClientInbound;
pub use super::ClientOutbound;
pub use super::CryptStatePair;
pub use super::RelayInbound;
pub use super::RelayOutbound;
pub const TOR1_THROUGHPUT: u64 = 498;
}
#[cfg(test)]
mod test {
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_time_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
use crate::crypto::cell::{
InboundClientCrypt, OutboundClientCrypt, Tor1RelayCrypto, test::add_layers,
};
use super::*;
#[test]
fn testvec() {
use digest::XofReader;
use digest::{ExtendableOutput, Update};
const K1: &[u8; 92] =
b" 'My public key is in this signed x509 object', said Tom assertively. (N-PREG-VIRYL)";
const K2: &[u8; 92] =
b"'Let's chart the pedal phlanges in the tomb', said Tom cryptographically. (PELCG-GBR-TENCU)";
const K3: &[u8; 92] =
b" 'Segmentation fault bugs don't _just happen_', said Tom seethingly. (P-GUVAT-YL)";
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.";
let cmd = ChanCmd::RELAY;
let data: &[(usize, &str)] = &include!("../../../testdata/cell_crypt.rs");
let mut cc_out = OutboundClientCrypt::new();
let mut cc_in = InboundClientCrypt::new();
let pair = Tor1RelayCrypto::initialize(&K1[..]).unwrap();
add_layers(&mut cc_out, &mut cc_in, pair);
let pair = Tor1RelayCrypto::initialize(&K2[..]).unwrap();
add_layers(&mut cc_out, &mut cc_in, pair);
let pair = Tor1RelayCrypto::initialize(&K3[..]).unwrap();
add_layers(&mut cc_out, &mut cc_in, pair);
let mut xof = tor_llcrypto::d::Shake256::default();
xof.update(&SEED[..]);
let mut stream = xof.finalize_xof();
let mut j = 0;
for cellno in 0..51 {
let mut body = Box::new([0_u8; 509]);
body[0] = 2; body[4] = 1; body[9] = 1; body[10] = 242;
stream.read(&mut body[11..]);
let mut cell = body.into();
let _ = cc_out.encrypt(cmd, &mut cell, 2.into());
if cellno == data[j].0 {
let expected = hex::decode(data[j].1).unwrap();
assert_eq!(cell.as_ref(), &expected[..]);
j += 1;
}
}
}
}