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

            
14
use tor_cell::relaycell::RelayCellFormat;
15
use tor_error::internal;
16

            
17
use crate::crypto::binding::CircuitBinding;
18
use crate::crypto::cell::CgoRelayCrypto;
19
#[cfg(feature = "hs-common")]
20
use crate::crypto::cell::Tor1Hsv3RelayCrypto;
21
use crate::crypto::cell::{
22
    ClientLayer, CryptInit, InboundClientLayer, InboundRelayLayer, OutboundClientLayer,
23
    OutboundRelayLayer, RelayLayer, Tor1RelayCrypto,
24
};
25

            
26
use crate::Result;
27

            
28
pub use crate::crypto::handshake::KeyGenerator;
29
#[cfg(feature = "hs-common")]
30
pub 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")]
40
pub 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)]
48
pub(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")]
68
impl 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]
80
pub 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.
89
pub(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

            
98
impl 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
86
    pub(crate) fn construct_client_layers(
105
86
        self,
106
86
        role: HandshakeRole,
107
86
        keygen: impl KeyGenerator,
108
86
    ) -> Result<BoxedClientLayer> {
109
        use RelayCellFormat::*;
110
        use RelayCryptLayerProtocol::*;
111

            
112
86
        match self {
113
86
            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
86
    }
122

            
123
    /// Return the cell format used by this protocol.
124
1182
    pub(crate) fn relay_cell_format(&self) -> RelayCellFormat {
125
1182
        match self {
126
1182
            RelayCryptLayerProtocol::Tor1(v) => *v,
127
            #[cfg(feature = "hs-common")]
128
            RelayCryptLayerProtocol::HsV3(v) => *v,
129
            RelayCryptLayerProtocol::Cgo => RelayCellFormat::V1,
130
        }
131
1182
    }
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.
138
struct ResponderOutboundLayer<L: InboundRelayLayer>(L);
139
impl<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.
160
struct ResponderInboundLayer<L: OutboundRelayLayer>(L);
161
impl<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.
173
86
fn construct<L, FC, BC, FR, BR>(
174
86
    keygen: impl KeyGenerator,
175
86
    role: HandshakeRole,
176
86
) -> Result<BoxedClientLayer>
177
86
where
178
86
    L: CryptInit + ClientLayer<FC, BC> + RelayLayer<FR, BR>,
179
86
    FC: OutboundClientLayer + Send + 'static,
180
86
    BC: InboundClientLayer + Send + 'static,
181
86
    FR: OutboundRelayLayer + Send + 'static,
182
86
    BR: InboundRelayLayer + Send + 'static,
183
{
184
86
    let layer = L::construct(keygen)?;
185
86
    match role {
186
        HandshakeRole::Initiator => {
187
86
            let (fwd, back, binding) = layer.split_client_layer();
188
86
            Ok(BoxedClientLayer {
189
86
                fwd: Box::new(fwd),
190
86
                back: Box::new(back),
191
86
                binding: Some(binding),
192
86
            })
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
86
}