use tor_cell::relaycell::RelayCellFormat;
use tor_error::internal;
use crate::crypto::binding::CircuitBinding;
#[cfg(feature = "counter-galois-onion")]
use crate::crypto::cell::CgoRelayCrypto;
#[cfg(feature = "hs-common")]
use crate::crypto::cell::Tor1Hsv3RelayCrypto;
use crate::crypto::cell::{
ClientLayer, CryptInit, InboundClientLayer, InboundRelayLayer, OutboundClientLayer,
OutboundRelayLayer, RelayLayer, Tor1RelayCrypto,
};
use crate::Result;
pub use crate::crypto::handshake::KeyGenerator;
#[cfg(feature = "hs-common")]
pub use crate::crypto::handshake::hs_ntor;
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
#[cfg(feature = "hs-common")]
pub enum RelayProtocol {
HsV3,
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum RelayCryptLayerProtocol {
Tor1(RelayCellFormat),
#[cfg(feature = "hs-common")]
HsV3(RelayCellFormat),
#[cfg(feature = "counter-galois-onion")]
Cgo,
}
#[cfg(feature = "hs-common")]
impl From<RelayProtocol> for RelayCryptLayerProtocol {
fn from(value: RelayProtocol) -> Self {
match value {
RelayProtocol::HsV3 => RelayCryptLayerProtocol::HsV3(RelayCellFormat::V0),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum HandshakeRole {
Initiator,
Responder,
}
pub(crate) struct BoxedClientLayer {
pub(crate) fwd: Box<dyn OutboundClientLayer + Send>,
pub(crate) back: Box<dyn InboundClientLayer + Send>,
pub(crate) binding: Option<CircuitBinding>,
}
impl RelayCryptLayerProtocol {
pub(crate) fn construct_client_layers(
self,
role: HandshakeRole,
keygen: impl KeyGenerator,
) -> Result<BoxedClientLayer> {
use RelayCellFormat::*;
use RelayCryptLayerProtocol::*;
match self {
Tor1(V0) => construct::<Tor1RelayCrypto, _, _, _, _>(keygen, role),
Tor1(_) => Err(internal!("protocol not implemented").into()),
#[cfg(feature = "hs-common")]
HsV3(V0) => construct::<Tor1Hsv3RelayCrypto, _, _, _, _>(keygen, role),
#[cfg(feature = "hs-common")]
HsV3(_) => Err(internal!("protocol not implemented").into()),
#[cfg(feature = "counter-galois-onion")]
Cgo => construct::<CgoRelayCrypto, _, _, _, _>(keygen, role),
}
}
pub(crate) fn relay_cell_format(&self) -> RelayCellFormat {
match self {
RelayCryptLayerProtocol::Tor1(v) => *v,
#[cfg(feature = "hs-common")]
RelayCryptLayerProtocol::HsV3(v) => *v,
#[cfg(feature = "counter-galois-onion")]
RelayCryptLayerProtocol::Cgo => RelayCellFormat::V1,
}
}
}
struct ResponderOutboundLayer<L: InboundRelayLayer>(L);
impl<L: InboundRelayLayer> OutboundClientLayer for ResponderOutboundLayer<L> {
fn originate_for(
&mut self,
cmd: tor_cell::chancell::ChanCmd,
cell: &mut crate::crypto::cell::RelayCellBody,
) -> tor_cell::relaycell::msg::SendmeTag {
self.0.originate(cmd, cell)
}
fn encrypt_outbound(
&mut self,
cmd: tor_cell::chancell::ChanCmd,
cell: &mut crate::crypto::cell::RelayCellBody,
) {
self.0.encrypt_inbound(cmd, cell);
}
}
struct ResponderInboundLayer<L: OutboundRelayLayer>(L);
impl<L: OutboundRelayLayer> InboundClientLayer for ResponderInboundLayer<L> {
fn decrypt_inbound(
&mut self,
cmd: tor_cell::chancell::ChanCmd,
cell: &mut crate::crypto::cell::RelayCellBody,
) -> Option<tor_cell::relaycell::msg::SendmeTag> {
self.0.decrypt_outbound(cmd, cell)
}
}
fn construct<L, FC, BC, FR, BR>(
keygen: impl KeyGenerator,
role: HandshakeRole,
) -> Result<BoxedClientLayer>
where
L: CryptInit + ClientLayer<FC, BC> + RelayLayer<FR, BR>,
FC: OutboundClientLayer + Send + 'static,
BC: InboundClientLayer + Send + 'static,
FR: OutboundRelayLayer + Send + 'static,
BR: InboundRelayLayer + Send + 'static,
{
let layer = L::construct(keygen)?;
match role {
HandshakeRole::Initiator => {
let (fwd, back, binding) = layer.split_client_layer();
Ok(BoxedClientLayer {
fwd: Box::new(fwd),
back: Box::new(back),
binding: Some(binding),
})
}
HandshakeRole::Responder => {
let (fwd, back, binding) = layer.split_relay_layer();
Ok(BoxedClientLayer {
fwd: Box::new(ResponderOutboundLayer(back)),
back: Box::new(ResponderInboundLayer(fwd)),
binding: Some(binding),
})
}
}
}