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
#[cfg(feature = "counter-galois-onion")]
19
use crate::crypto::cell::CgoRelayCrypto;
20
#[cfg(feature = "hs-common")]
21
use crate::crypto::cell::Tor1Hsv3RelayCrypto;
22
use crate::crypto::cell::{
23
    ClientLayer, CryptInit, InboundClientLayer, InboundRelayLayer, OutboundClientLayer,
24
    OutboundRelayLayer, RelayLayer, Tor1RelayCrypto,
25
};
26

            
27
use crate::Result;
28

            
29
pub use crate::crypto::handshake::KeyGenerator;
30
#[cfg(feature = "hs-common")]
31
pub use crate::crypto::handshake::hs_ntor;
32

            
33
/// The relay protocol to use when extending a circuit manually with
34
/// [`Circuit::extend_virtual`](crate::client::circuit::ClientCirc::extend_virtual).
35
//
36
// NOTE: These correspond internally to implementations of
37
// crate::crypto::cell::ClientLayer.
38
#[derive(Copy, Clone, Debug)]
39
#[non_exhaustive]
40
#[cfg(feature = "hs-common")]
41
pub enum RelayProtocol {
42
    /// A variation of Tor's original protocol, using AES-256 and SHA-3.
43
    HsV3,
44
}
45

            
46
/// Internal counterpart of RelayProtocol; includes variants that can't be
47
/// negotiated from [`extend_virtual`](crate::client::circuit::ClientCirc::extend_virtual).
48
#[derive(Copy, Clone, Debug)]
49
pub(crate) enum RelayCryptLayerProtocol {
50
    /// The original Tor cell encryption protocol, using AES-128 and SHA-1.
51
    ///
52
    /// References:
53
    /// - <https://spec.torproject.org/tor-spec/relay-cells.html>
54
    /// - <https://spec.torproject.org/tor-spec/routing-relay-cells.html>
55
    Tor1(RelayCellFormat),
56
    /// A variation of Tor's original cell encryption protocol, using AES-256
57
    /// and SHA3-256.
58
    ///
59
    /// Reference:
60
    /// - <https://spec.torproject.org/rend-spec/encrypting-user-data.html>
61
    /// - <https://spec.torproject.org/rend-spec/introduction-protocol.html#INTRO-HANDSHAKE-REQS>
62
    #[cfg(feature = "hs-common")]
63
    HsV3(RelayCellFormat),
64
    /// The counter galois onion cell encryption protocol.
65
    #[cfg(feature = "counter-galois-onion")]
66
    Cgo,
67
}
68

            
69
#[cfg(feature = "hs-common")]
70
impl From<RelayProtocol> for RelayCryptLayerProtocol {
71
    fn from(value: RelayProtocol) -> Self {
72
        match value {
73
            // TODO #1948
74
            RelayProtocol::HsV3 => RelayCryptLayerProtocol::HsV3(RelayCellFormat::V0),
75
        }
76
    }
77
}
78

            
79
/// What role we are playing in a handshake.
80
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
81
#[non_exhaustive]
82
pub enum HandshakeRole {
83
    /// We are the party initiating the handshake.
84
    Initiator,
85
    /// We are the party responding to the handshake.
86
    Responder,
87
}
88

            
89
/// A set of type-erased cryptographic layers to use for a single hop at a
90
/// client.
91
pub(crate) struct BoxedClientLayer {
92
    /// The outbound cryptographic layer to use for this hop
93
    pub(crate) fwd: Box<dyn OutboundClientLayer + Send>,
94
    /// The inbound cryptogarphic layer to use for this hop
95
    pub(crate) back: Box<dyn InboundClientLayer + Send>,
96
    /// A circuit binding key for this hop.
97
    pub(crate) binding: Option<CircuitBinding>,
98
}
99

            
100
impl RelayCryptLayerProtocol {
101
    /// Construct the client cell-crypto layers that are needed for a given set of
102
    /// circuit hop parameters.
103
    ///
104
    /// This returns layers for use in a client circuit,
105
    /// whether as the initiator or responder of an onion service request.
106
72
    pub(crate) fn construct_client_layers(
107
72
        self,
108
72
        role: HandshakeRole,
109
72
        keygen: impl KeyGenerator,
110
72
    ) -> Result<BoxedClientLayer> {
111
        use RelayCellFormat::*;
112
        use RelayCryptLayerProtocol::*;
113

            
114
72
        match self {
115
72
            Tor1(V0) => construct::<Tor1RelayCrypto, _, _, _, _>(keygen, role),
116
            Tor1(_) => Err(internal!("protocol not implemented").into()),
117
            #[cfg(feature = "hs-common")]
118
            HsV3(V0) => construct::<Tor1Hsv3RelayCrypto, _, _, _, _>(keygen, role),
119
            #[cfg(feature = "hs-common")]
120
            HsV3(_) => Err(internal!("protocol not implemented").into()),
121
            #[cfg(feature = "counter-galois-onion")]
122
            Cgo => construct::<CgoRelayCrypto, _, _, _, _>(keygen, role),
123
        }
124
72
    }
125

            
126
    /// Return the cell format used by this protocol.
127
1020
    pub(crate) fn relay_cell_format(&self) -> RelayCellFormat {
128
1020
        match self {
129
1020
            RelayCryptLayerProtocol::Tor1(v) => *v,
130
            #[cfg(feature = "hs-common")]
131
            RelayCryptLayerProtocol::HsV3(v) => *v,
132
            #[cfg(feature = "counter-galois-onion")]
133
            RelayCryptLayerProtocol::Cgo => RelayCellFormat::V1,
134
        }
135
1020
    }
136
}
137

            
138
/// Wrapper to make a relay layer behave as a client layer.
139
///
140
/// We use this wrapper to implement onion services,
141
/// which use relay layers to communicate with clients.
142
struct ResponderOutboundLayer<L: InboundRelayLayer>(L);
143
impl<L: InboundRelayLayer> OutboundClientLayer for ResponderOutboundLayer<L> {
144
    fn originate_for(
145
        &mut self,
146
        cmd: tor_cell::chancell::ChanCmd,
147
        cell: &mut crate::crypto::cell::RelayCellBody,
148
    ) -> tor_cell::relaycell::msg::SendmeTag {
149
        self.0.originate(cmd, cell)
150
    }
151

            
152
    fn encrypt_outbound(
153
        &mut self,
154
        cmd: tor_cell::chancell::ChanCmd,
155
        cell: &mut crate::crypto::cell::RelayCellBody,
156
    ) {
157
        self.0.encrypt_inbound(cmd, cell);
158
    }
159
}
160
/// Wrapper to make a relay layer behave as a client layer.
161
///
162
/// We use this wrapper to implement onion services,
163
/// which use relay layers to communicate with clients.
164
struct ResponderInboundLayer<L: OutboundRelayLayer>(L);
165
impl<L: OutboundRelayLayer> InboundClientLayer for ResponderInboundLayer<L> {
166
    fn decrypt_inbound(
167
        &mut self,
168
        cmd: tor_cell::chancell::ChanCmd,
169
        cell: &mut crate::crypto::cell::RelayCellBody,
170
    ) -> Option<tor_cell::relaycell::msg::SendmeTag> {
171
        self.0.decrypt_outbound(cmd, cell)
172
    }
173
}
174

            
175
/// Helper: Construct a BoxedClientLayer for a layer type L whose inbound and outbound
176
/// cryptographic states are the same type.
177
72
fn construct<L, FC, BC, FR, BR>(
178
72
    keygen: impl KeyGenerator,
179
72
    role: HandshakeRole,
180
72
) -> Result<BoxedClientLayer>
181
72
where
182
72
    L: CryptInit + ClientLayer<FC, BC> + RelayLayer<FR, BR>,
183
72
    FC: OutboundClientLayer + Send + 'static,
184
72
    BC: InboundClientLayer + Send + 'static,
185
72
    FR: OutboundRelayLayer + Send + 'static,
186
72
    BR: InboundRelayLayer + Send + 'static,
187
{
188
72
    let layer = L::construct(keygen)?;
189
72
    match role {
190
        HandshakeRole::Initiator => {
191
72
            let (fwd, back, binding) = layer.split_client_layer();
192
72
            Ok(BoxedClientLayer {
193
72
                fwd: Box::new(fwd),
194
72
                back: Box::new(back),
195
72
                binding: Some(binding),
196
72
            })
197
        }
198
        HandshakeRole::Responder => {
199
            let (fwd, back, binding) = layer.split_relay_layer();
200
            Ok(BoxedClientLayer {
201
                // We reverse the inbound and outbound layers before wrapping them,
202
                // since from the responder's perspective, _they_ are the origin
203
                // point of the circuit.
204
                fwd: Box::new(ResponderOutboundLayer(back)),
205
                back: Box::new(ResponderInboundLayer(fwd)),
206
                binding: Some(binding),
207
            })
208
        }
209
    }
210
72
}