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 = "counter-galois-onion")]
19use crate::crypto::cell::CgoRelayCrypto;
20#[cfg(feature = "hs-common")]
21use crate::crypto::cell::Tor1Hsv3RelayCrypto;
22use crate::crypto::cell::{
23 ClientLayer, CryptInit, InboundClientLayer, InboundRelayLayer, OutboundClientLayer,
24 OutboundRelayLayer, RelayLayer, Tor1RelayCrypto,
25};
2627use crate::Result;
2829#[cfg(feature = "hs-common")]
30#[cfg_attr(docsrs, doc(cfg(feature = "hs-common")))]
31pub use crate::crypto::handshake::hs_ntor;
32pub use crate::crypto::handshake::KeyGenerator;
3334/// The relay protocol to use when extending a circuit manually with
35/// [`Circuit::extend_virtual`](crate::tunnel::circuit::ClientCirc::extend_virtual).
36//
37// NOTE: These correspond internally to implementations of
38// crate::crypto::cell::ClientLayer.
39#[derive(Copy, Clone, Debug)]
40#[non_exhaustive]
41#[cfg(feature = "hs-common")]
42pub enum RelayProtocol {
43/// A variation of Tor's original protocol, using AES-256 and SHA-3.
44HsV3,
45}
4647/// Internal counterpart of RelayProtocol; includes variants that can't be
48/// negotiated from [`extend_virtual`](crate::tunnel::circuit::ClientCirc::extend_virtual).
49#[derive(Copy, Clone, Debug)]
50pub(crate) enum RelayCryptLayerProtocol {
51/// The original Tor cell encryption protocol, using AES-128 and SHA-1.
52 ///
53 /// References:
54 /// - <https://spec.torproject.org/tor-spec/relay-cells.html>
55 /// - <https://spec.torproject.org/tor-spec/routing-relay-cells.html>
56Tor1(RelayCellFormat),
57/// A variation of Tor's original cell encryption protocol, using AES-256
58 /// and SHA3-256.
59 ///
60 /// Reference:
61 /// - <https://spec.torproject.org/rend-spec/encrypting-user-data.html>
62 /// - <https://spec.torproject.org/rend-spec/introduction-protocol.html#INTRO-HANDSHAKE-REQS>
63#[cfg(feature = "hs-common")]
64HsV3(RelayCellFormat),
65/// The counter galois onion cell encryption protocol.
66#[cfg(feature = "counter-galois-onion")]
67Cgo,
68}
6970#[cfg(feature = "hs-common")]
71impl From<RelayProtocol> for RelayCryptLayerProtocol {
72fn from(value: RelayProtocol) -> Self {
73match value {
74// TODO #1948
75RelayProtocol::HsV3 => RelayCryptLayerProtocol::HsV3(RelayCellFormat::V0),
76 }
77 }
78}
7980/// What role we are playing in a handshake.
81#[derive(Copy, Clone, Debug, Eq, PartialEq)]
82#[non_exhaustive]
83pub enum HandshakeRole {
84/// We are the party initiating the handshake.
85Initiator,
86/// We are the party responding to the handshake.
87Responder,
88}
8990/// A set of type-erased cryptographic layers to use for a single hop at a
91/// client.
92pub(crate) struct BoxedClientLayer {
93/// The outbound cryptographic layer to use for this hop
94pub(crate) fwd: Box<dyn OutboundClientLayer + Send>,
95/// The inbound cryptogarphic layer to use for this hop
96pub(crate) back: Box<dyn InboundClientLayer + Send>,
97/// A circuit binding key for this hop.
98pub(crate) binding: Option<CircuitBinding>,
99}
100101impl RelayCryptLayerProtocol {
102/// Construct the client cell-crypto layers that are needed for a given set of
103 /// circuit hop parameters.
104 ///
105 /// This returns layers for use in a client circuit,
106 /// whether as the initiator or responder of an onion service request.
107pub(crate) fn construct_client_layers(
108self,
109 role: HandshakeRole,
110 keygen: impl KeyGenerator,
111 ) -> Result<BoxedClientLayer> {
112use RelayCellFormat::*;
113use RelayCryptLayerProtocol::*;
114115match self {
116 Tor1(V0) => construct::<Tor1RelayCrypto, _, _, _, _>(keygen, role),
117 Tor1(_) => Err(internal!("protocol not implemented").into()),
118#[cfg(feature = "hs-common")]
119HsV3(V0) => construct::<Tor1Hsv3RelayCrypto, _, _, _, _>(keygen, role),
120#[cfg(feature = "hs-common")]
121HsV3(_) => Err(internal!("protocol not implemented").into()),
122#[cfg(feature = "counter-galois-onion")]
123Cgo => construct::<CgoRelayCrypto, _, _, _, _>(keygen, role),
124 }
125 }
126127/// Return the cell format used by this protocol.
128pub(crate) fn relay_cell_format(&self) -> RelayCellFormat {
129match self {
130 RelayCryptLayerProtocol::Tor1(v) => *v,
131#[cfg(feature = "hs-common")]
132RelayCryptLayerProtocol::HsV3(v) => *v,
133#[cfg(feature = "counter-galois-onion")]
134RelayCryptLayerProtocol::Cgo => RelayCellFormat::V1,
135 }
136 }
137}
138139/// Wrapper to make a relay layer behave as a client layer.
140///
141/// We use this wrapper to implement onion services,
142/// which use relay layers to communicate with clients.
143struct ResponderOutboundLayer<L: InboundRelayLayer>(L);
144impl<L: InboundRelayLayer> OutboundClientLayer for ResponderOutboundLayer<L> {
145fn originate_for(
146&mut self,
147 cmd: tor_cell::chancell::ChanCmd,
148 cell: &mut crate::crypto::cell::RelayCellBody,
149 ) -> tor_cell::relaycell::msg::SendmeTag {
150self.0.originate(cmd, cell)
151 }
152153fn encrypt_outbound(
154&mut self,
155 cmd: tor_cell::chancell::ChanCmd,
156 cell: &mut crate::crypto::cell::RelayCellBody,
157 ) {
158self.0.encrypt_inbound(cmd, cell);
159 }
160}
161/// Wrapper to make a relay layer behave as a client layer.
162///
163/// We use this wrapper to implement onion services,
164/// which use relay layers to communicate with clients.
165struct ResponderInboundLayer<L: OutboundRelayLayer>(L);
166impl<L: OutboundRelayLayer> InboundClientLayer for ResponderInboundLayer<L> {
167fn decrypt_inbound(
168&mut self,
169 cmd: tor_cell::chancell::ChanCmd,
170 cell: &mut crate::crypto::cell::RelayCellBody,
171 ) -> Option<tor_cell::relaycell::msg::SendmeTag> {
172self.0.decrypt_outbound(cmd, cell)
173 }
174}
175176/// Helper: Construct a BoxedClientLayer for a layer type L whose inbound and outbound
177/// cryptographic states are the same type.
178fn construct<L, FC, BC, FR, BR>(
179 keygen: impl KeyGenerator,
180 role: HandshakeRole,
181) -> Result<BoxedClientLayer>
182where
183L: CryptInit + ClientLayer<FC, BC> + RelayLayer<FR, BR>,
184 FC: OutboundClientLayer + Send + 'static,
185 BC: InboundClientLayer + Send + 'static,
186 FR: OutboundRelayLayer + Send + 'static,
187 BR: InboundRelayLayer + Send + 'static,
188{
189let layer = L::construct(keygen)?;
190match role {
191 HandshakeRole::Initiator => {
192let (fwd, back, binding) = layer.split_client_layer();
193Ok(BoxedClientLayer {
194 fwd: Box::new(fwd),
195 back: Box::new(back),
196 binding: Some(binding),
197 })
198 }
199 HandshakeRole::Responder => {
200let (fwd, back, binding) = layer.split_relay_layer();
201Ok(BoxedClientLayer {
202// We reverse the inbound and outbound layers before wrapping them,
203 // since from the responder's perspective, _they_ are the origin
204 // point of the circuit.
205fwd: Box::new(ResponderOutboundLayer(back)),
206 back: Box::new(ResponderInboundLayer(fwd)),
207 binding: Some(binding),
208 })
209 }
210 }
211}