tor_proto/tunnel/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;
18#[cfg(feature = "hs-common")]
19use crate::crypto::cell::Tor1Hsv3RelayCrypto;
20use crate::crypto::cell::{
21    ClientLayer, CryptInit, InboundClientLayer, InboundRelayLayer, OutboundClientLayer,
22    OutboundRelayLayer, RelayLayer, Tor1RelayCrypto,
23};
24
25use crate::Result;
26
27#[cfg(feature = "hs-common")]
28#[cfg_attr(docsrs, doc(cfg(feature = "hs-common")))]
29pub use crate::crypto::handshake::hs_ntor;
30pub use crate::crypto::handshake::KeyGenerator;
31
32/// The relay protocol to use when extending a circuit manually with
33/// [`Circuit::extend_virtual`](crate::tunnel::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::tunnel::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}
64
65#[cfg(feature = "hs-common")]
66impl From<RelayProtocol> for RelayCryptLayerProtocol {
67    fn from(value: RelayProtocol) -> Self {
68        match value {
69            // TODO #1948
70            RelayProtocol::HsV3 => RelayCryptLayerProtocol::HsV3(RelayCellFormat::V0),
71        }
72    }
73}
74
75/// What role we are playing in a handshake.
76#[derive(Copy, Clone, Debug, Eq, PartialEq)]
77#[non_exhaustive]
78pub enum HandshakeRole {
79    /// We are the party initiating the handshake.
80    Initiator,
81    /// We are the party responding to the handshake.
82    Responder,
83}
84
85/// A set of type-erased cryptographic layers to use for a single hop at a
86/// client.
87pub(crate) struct BoxedClientLayer {
88    /// The outbound cryptographic layer to use for this hop
89    pub(crate) fwd: Box<dyn OutboundClientLayer + Send>,
90    /// The inbound cryptogarphic layer to use for this hop
91    pub(crate) back: Box<dyn InboundClientLayer + Send>,
92    /// A circuit binding key for this hop.
93    pub(crate) binding: Option<CircuitBinding>,
94}
95
96impl RelayCryptLayerProtocol {
97    /// Construct the client cell-crypto layers that are needed for a given set of
98    /// circuit hop parameters.
99    ///
100    /// This returns layers for use in a client circuit,
101    /// whether as the initiator or responder of an onion service request.
102    pub(crate) fn construct_client_layers(
103        self,
104        role: HandshakeRole,
105        keygen: impl KeyGenerator,
106    ) -> Result<BoxedClientLayer> {
107        use RelayCellFormat::*;
108        use RelayCryptLayerProtocol::*;
109
110        match self {
111            Tor1(V0) => construct::<Tor1RelayCrypto, _, _, _, _>(keygen, role),
112            Tor1(_) => Err(internal!("protocol not implemented").into()),
113            #[cfg(feature = "hs-common")]
114            HsV3(V0) => construct::<Tor1Hsv3RelayCrypto, _, _, _, _>(keygen, role),
115            #[cfg(feature = "hs-common")]
116            HsV3(_) => Err(internal!("protocol not implemented").into()),
117        }
118    }
119
120    /// Return the cell format used by this protocol.
121    pub(crate) fn relay_cell_format(&self) -> RelayCellFormat {
122        match self {
123            RelayCryptLayerProtocol::Tor1(v) => *v,
124            #[cfg(feature = "hs-common")]
125            RelayCryptLayerProtocol::HsV3(v) => *v,
126        }
127    }
128}
129
130/// Wrapper to make a relay layer behave as a client layer.
131///
132/// We use this wrapper to implement onion services,
133/// which use relay layers to communicate with clients.
134struct ResponderOutboundLayer<L: InboundRelayLayer>(L);
135impl<L: InboundRelayLayer> OutboundClientLayer for ResponderOutboundLayer<L> {
136    fn originate_for(
137        &mut self,
138        cmd: tor_cell::chancell::ChanCmd,
139        cell: &mut crate::crypto::cell::RelayCellBody,
140    ) -> tor_cell::relaycell::msg::SendmeTag {
141        self.0.originate(cmd, cell)
142    }
143
144    fn encrypt_outbound(
145        &mut self,
146        cmd: tor_cell::chancell::ChanCmd,
147        cell: &mut crate::crypto::cell::RelayCellBody,
148    ) {
149        self.0.encrypt_inbound(cmd, cell);
150    }
151}
152/// Wrapper to make a relay layer behave as a client layer.
153///
154/// We use this wrapper to implement onion services,
155/// which use relay layers to communicate with clients.
156struct ResponderInboundLayer<L: OutboundRelayLayer>(L);
157impl<L: OutboundRelayLayer> InboundClientLayer for ResponderInboundLayer<L> {
158    fn decrypt_inbound(
159        &mut self,
160        cmd: tor_cell::chancell::ChanCmd,
161        cell: &mut crate::crypto::cell::RelayCellBody,
162    ) -> Option<tor_cell::relaycell::msg::SendmeTag> {
163        self.0.decrypt_outbound(cmd, cell)
164    }
165}
166
167/// Helper: Construct a BoxedClientLayer for a layer type L whose inbound and outbound
168/// cryptographic states are the same type.
169fn construct<L, FC, BC, FR, BR>(
170    keygen: impl KeyGenerator,
171    role: HandshakeRole,
172) -> Result<BoxedClientLayer>
173where
174    L: CryptInit + ClientLayer<FC, BC> + RelayLayer<FR, BR>,
175    FC: OutboundClientLayer + Send + 'static,
176    BC: InboundClientLayer + Send + 'static,
177    FR: OutboundRelayLayer + Send + 'static,
178    BR: InboundRelayLayer + Send + 'static,
179{
180    let layer = L::construct(keygen)?;
181    match role {
182        HandshakeRole::Initiator => {
183            let (fwd, back, binding) = layer.split_client_layer();
184            Ok(BoxedClientLayer {
185                fwd: Box::new(fwd),
186                back: Box::new(back),
187                binding: Some(binding),
188            })
189        }
190        HandshakeRole::Responder => {
191            let (fwd, back, binding) = layer.split_relay_layer();
192            Ok(BoxedClientLayer {
193                // We reverse the inbound and outbound layers before wrapping them,
194                // since from the responder's perspective, _they_ are the origin
195                // point of the circuit.
196                fwd: Box::new(ResponderOutboundLayer(back)),
197                back: Box::new(ResponderInboundLayer(fwd)),
198                binding: Some(binding),
199            })
200        }
201    }
202}