Skip to main content

tor_proto/client/circuit/
handshake.rs

1//! Features for manual invocation of Tor's cryptographic circuit handshakes.
2//!
3//! These features are used to implement onion services, by giving the onion
4//! service code more direct control over the lower-level pieces of the protocol.
5
6// Here we re-export some key types from our cryptographic code, for use when we
7// implement our onion handshake.
8//
9// TODO: it might be neat, someday,  to clean this all up so that the types
10// and functions in hs_ntor are all methods on a set of related traits.  But
11// that can wait IMO until we have a second circuit creation mechanism for use
12// with onion services.
13
14use tor_cell::relaycell::RelayCellFormat;
15use tor_error::internal;
16
17use crate::crypto::binding::CircuitBinding;
18use crate::crypto::cell::CgoRelayCrypto;
19#[cfg(feature = "hs-common")]
20use crate::crypto::cell::Tor1Hsv3RelayCrypto;
21use crate::crypto::cell::{
22    ClientLayer, CryptInit, InboundClientLayer, InboundRelayLayer, OutboundClientLayer,
23    OutboundRelayLayer, RelayLayer, Tor1RelayCrypto,
24};
25
26use crate::Result;
27
28pub use crate::crypto::handshake::KeyGenerator;
29#[cfg(feature = "hs-common")]
30pub use crate::crypto::handshake::hs_ntor;
31
32/// The relay protocol to use when extending a circuit manually with
33/// [`Circuit::extend_virtual`](crate::client::circuit::ClientCirc::extend_virtual).
34//
35// NOTE: These correspond internally to implementations of
36// crate::crypto::cell::ClientLayer.
37#[derive(Copy, Clone, Debug)]
38#[non_exhaustive]
39#[cfg(feature = "hs-common")]
40pub enum RelayProtocol {
41    /// A variation of Tor's original protocol, using AES-256 and SHA-3.
42    HsV3,
43}
44
45/// Internal counterpart of RelayProtocol; includes variants that can't be
46/// negotiated from [`extend_virtual`](crate::client::circuit::ClientCirc::extend_virtual).
47#[derive(Copy, Clone, Debug)]
48pub(crate) enum RelayCryptLayerProtocol {
49    /// The original Tor cell encryption protocol, using AES-128 and SHA-1.
50    ///
51    /// References:
52    /// - <https://spec.torproject.org/tor-spec/relay-cells.html>
53    /// - <https://spec.torproject.org/tor-spec/routing-relay-cells.html>
54    Tor1(RelayCellFormat),
55    /// A variation of Tor's original cell encryption protocol, using AES-256
56    /// and SHA3-256.
57    ///
58    /// Reference:
59    /// - <https://spec.torproject.org/rend-spec/encrypting-user-data.html>
60    /// - <https://spec.torproject.org/rend-spec/introduction-protocol.html#INTRO-HANDSHAKE-REQS>
61    #[cfg(feature = "hs-common")]
62    HsV3(RelayCellFormat),
63    /// The counter galois onion cell encryption protocol.
64    Cgo,
65}
66
67#[cfg(feature = "hs-common")]
68impl From<RelayProtocol> for RelayCryptLayerProtocol {
69    fn from(value: RelayProtocol) -> Self {
70        match value {
71            // TODO #1948
72            RelayProtocol::HsV3 => RelayCryptLayerProtocol::HsV3(RelayCellFormat::V0),
73        }
74    }
75}
76
77/// What role we are playing in a handshake.
78#[derive(Copy, Clone, Debug, Eq, PartialEq)]
79#[non_exhaustive]
80pub enum HandshakeRole {
81    /// We are the party initiating the handshake.
82    Initiator,
83    /// We are the party responding to the handshake.
84    Responder,
85}
86
87/// A set of type-erased cryptographic layers to use for a single hop at a
88/// client.
89pub(crate) struct BoxedClientLayer {
90    /// The outbound cryptographic layer to use for this hop
91    pub(crate) fwd: Box<dyn OutboundClientLayer + Send>,
92    /// The inbound cryptogarphic layer to use for this hop
93    pub(crate) back: Box<dyn InboundClientLayer + Send>,
94    /// A circuit binding key for this hop.
95    pub(crate) binding: Option<CircuitBinding>,
96}
97
98impl RelayCryptLayerProtocol {
99    /// Construct the client cell-crypto layers that are needed for a given set of
100    /// circuit hop parameters.
101    ///
102    /// This returns layers for use in a client circuit,
103    /// whether as the initiator or responder of an onion service request.
104    pub(crate) fn construct_client_layers(
105        self,
106        role: HandshakeRole,
107        keygen: impl KeyGenerator,
108    ) -> Result<BoxedClientLayer> {
109        use RelayCellFormat::*;
110        use RelayCryptLayerProtocol::*;
111
112        match self {
113            Tor1(V0) => construct::<Tor1RelayCrypto, _, _, _, _>(keygen, role),
114            Tor1(_) => Err(internal!("protocol not implemented").into()),
115            #[cfg(feature = "hs-common")]
116            HsV3(V0) => construct::<Tor1Hsv3RelayCrypto, _, _, _, _>(keygen, role),
117            #[cfg(feature = "hs-common")]
118            HsV3(_) => Err(internal!("protocol not implemented").into()),
119            Cgo => construct::<CgoRelayCrypto, _, _, _, _>(keygen, role),
120        }
121    }
122
123    /// Return the cell format used by this protocol.
124    pub(crate) fn relay_cell_format(&self) -> RelayCellFormat {
125        match self {
126            RelayCryptLayerProtocol::Tor1(v) => *v,
127            #[cfg(feature = "hs-common")]
128            RelayCryptLayerProtocol::HsV3(v) => *v,
129            RelayCryptLayerProtocol::Cgo => RelayCellFormat::V1,
130        }
131    }
132}
133
134/// Wrapper to make a relay layer behave as a client layer.
135///
136/// We use this wrapper to implement onion services,
137/// which use relay layers to communicate with clients.
138struct ResponderOutboundLayer<L: InboundRelayLayer>(L);
139impl<L: InboundRelayLayer> OutboundClientLayer for ResponderOutboundLayer<L> {
140    fn originate_for(
141        &mut self,
142        cmd: tor_cell::chancell::ChanCmd,
143        cell: &mut crate::crypto::cell::RelayCellBody,
144    ) -> tor_cell::relaycell::msg::SendmeTag {
145        self.0.originate(cmd, cell)
146    }
147
148    fn encrypt_outbound(
149        &mut self,
150        cmd: tor_cell::chancell::ChanCmd,
151        cell: &mut crate::crypto::cell::RelayCellBody,
152    ) {
153        self.0.encrypt_inbound(cmd, cell);
154    }
155}
156/// Wrapper to make a relay layer behave as a client layer.
157///
158/// We use this wrapper to implement onion services,
159/// which use relay layers to communicate with clients.
160struct ResponderInboundLayer<L: OutboundRelayLayer>(L);
161impl<L: OutboundRelayLayer> InboundClientLayer for ResponderInboundLayer<L> {
162    fn decrypt_inbound(
163        &mut self,
164        cmd: tor_cell::chancell::ChanCmd,
165        cell: &mut crate::crypto::cell::RelayCellBody,
166    ) -> Option<tor_cell::relaycell::msg::SendmeTag> {
167        self.0.decrypt_outbound(cmd, cell)
168    }
169}
170
171/// Helper: Construct a BoxedClientLayer for a layer type L whose inbound and outbound
172/// cryptographic states are the same type.
173fn construct<L, FC, BC, FR, BR>(
174    keygen: impl KeyGenerator,
175    role: HandshakeRole,
176) -> Result<BoxedClientLayer>
177where
178    L: CryptInit + ClientLayer<FC, BC> + RelayLayer<FR, BR>,
179    FC: OutboundClientLayer + Send + 'static,
180    BC: InboundClientLayer + Send + 'static,
181    FR: OutboundRelayLayer + Send + 'static,
182    BR: InboundRelayLayer + Send + 'static,
183{
184    let layer = L::construct(keygen)?;
185    match role {
186        HandshakeRole::Initiator => {
187            let (fwd, back, binding) = layer.split_client_layer();
188            Ok(BoxedClientLayer {
189                fwd: Box::new(fwd),
190                back: Box::new(back),
191                binding: Some(binding),
192            })
193        }
194        HandshakeRole::Responder => {
195            let (fwd, back, binding) = layer.split_relay_layer();
196            Ok(BoxedClientLayer {
197                // We reverse the inbound and outbound layers before wrapping them,
198                // since from the responder's perspective, _they_ are the origin
199                // point of the circuit.
200                fwd: Box::new(ResponderOutboundLayer(back)),
201                back: Box::new(ResponderInboundLayer(fwd)),
202                binding: Some(binding),
203            })
204        }
205    }
206}