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.
56// 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.
1314use tor_cell::relaycell::RelayCellFormat;
15use tor_error::internal;
1617use 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};
2425use crate::Result;
2627#[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;
3132/// 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.
42HsV3,
43}
4445/// 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>
54Tor1(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")]
62HsV3(RelayCellFormat),
63}
6465#[cfg(feature = "hs-common")]
66impl From<RelayProtocol> for RelayCryptLayerProtocol {
67fn from(value: RelayProtocol) -> Self {
68match value {
69// TODO #1948
70RelayProtocol::HsV3 => RelayCryptLayerProtocol::HsV3(RelayCellFormat::V0),
71 }
72 }
73}
7475/// 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.
80Initiator,
81/// We are the party responding to the handshake.
82Responder,
83}
8485/// 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
89pub(crate) fwd: Box<dyn OutboundClientLayer + Send>,
90/// The inbound cryptogarphic layer to use for this hop
91pub(crate) back: Box<dyn InboundClientLayer + Send>,
92/// A circuit binding key for this hop.
93pub(crate) binding: Option<CircuitBinding>,
94}
9596impl 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.
102pub(crate) fn construct_client_layers(
103self,
104 role: HandshakeRole,
105 keygen: impl KeyGenerator,
106 ) -> Result<BoxedClientLayer> {
107use RelayCellFormat::*;
108use RelayCryptLayerProtocol::*;
109110match self {
111 Tor1(V0) => construct::<Tor1RelayCrypto, _, _, _, _>(keygen, role),
112 Tor1(_) => Err(internal!("protocol not implemented").into()),
113#[cfg(feature = "hs-common")]
114HsV3(V0) => construct::<Tor1Hsv3RelayCrypto, _, _, _, _>(keygen, role),
115#[cfg(feature = "hs-common")]
116HsV3(_) => Err(internal!("protocol not implemented").into()),
117 }
118 }
119120/// Return the cell format used by this protocol.
121pub(crate) fn relay_cell_format(&self) -> RelayCellFormat {
122match self {
123 RelayCryptLayerProtocol::Tor1(v) => *v,
124#[cfg(feature = "hs-common")]
125RelayCryptLayerProtocol::HsV3(v) => *v,
126 }
127 }
128}
129130/// 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> {
136fn originate_for(
137&mut self,
138 cmd: tor_cell::chancell::ChanCmd,
139 cell: &mut crate::crypto::cell::RelayCellBody,
140 ) -> tor_cell::relaycell::msg::SendmeTag {
141self.0.originate(cmd, cell)
142 }
143144fn encrypt_outbound(
145&mut self,
146 cmd: tor_cell::chancell::ChanCmd,
147 cell: &mut crate::crypto::cell::RelayCellBody,
148 ) {
149self.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> {
158fn 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> {
163self.0.decrypt_outbound(cmd, cell)
164 }
165}
166167/// 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
174L: 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{
180let layer = L::construct(keygen)?;
181match role {
182 HandshakeRole::Initiator => {
183let (fwd, back, binding) = layer.split_client_layer();
184Ok(BoxedClientLayer {
185 fwd: Box::new(fwd),
186 back: Box::new(back),
187 binding: Some(binding),
188 })
189 }
190 HandshakeRole::Responder => {
191let (fwd, back, binding) = layer.split_relay_layer();
192Ok(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.
196fwd: Box::new(ResponderOutboundLayer(back)),
197 back: Box::new(ResponderInboundLayer(fwd)),
198 binding: Some(binding),
199 })
200 }
201 }
202}