Skip to main content

tor_proto/crypto/
handshake.rs

1//! Circuit extension handshake for Tor.
2//!
3//! Tor circuit handshakes all implement a one-way-authenticated key
4//! exchange, where a client that knows a public "onion key" for a
5//! relay sends a "client onionskin" to extend to a relay, and receives a
6//! "relay onionskin" in response.  When the handshake is successful,
7//! both the client and relay share a set of session keys, and the
8//! client knows that nobody _else_ shares those keys unless they
9//! relay's private onion key.
10//!
11//! Currently, this module implements only the "ntor" handshake used
12//! for circuits on today's Tor.
13pub(crate) mod fast;
14#[cfg(feature = "hs-common")]
15pub mod hs_ntor;
16pub(crate) mod ntor;
17pub(crate) mod ntor_v3;
18
19use std::borrow::Borrow;
20
21use crate::Result;
22//use zeroize::Zeroizing;
23use rand_core::{CryptoRng, RngCore};
24use tor_bytes::SecretBuf;
25use tor_cell::chancell::msg::DestroyReason;
26use tor_error::{ErrorKind, HasKind};
27
28/// A ClientHandshake is used to generate a client onionskin and
29/// handle a relay onionskin.
30pub(crate) trait ClientHandshake {
31    /// The type for the onion key.
32    type KeyType;
33    /// The type for the state that the client holds while waiting for a reply.
34    type StateType;
35    /// A type that is returned and used to generate session keys.x
36    type KeyGen;
37    /// Type of extra data sent from client (without forward secrecy).
38    type ClientAuxData: ?Sized;
39    /// Type of extra data returned by server (without forward secrecy).
40    type ServerAuxData;
41    /// Generate a new client onionskin for a relay with a given onion key,
42    /// including `client_aux_data` to be sent without forward secrecy.
43    ///
44    /// On success, return a state object that will be used to
45    /// complete the handshake, along with the message to send.
46    fn client1<R: RngCore + CryptoRng, M: Borrow<Self::ClientAuxData>>(
47        rng: &mut R,
48        key: &Self::KeyType,
49        client_aux_data: &M,
50    ) -> Result<(Self::StateType, Vec<u8>)>;
51    /// Handle an onionskin from a relay, and produce aux data returned
52    /// from the server, and a key generator.
53    ///
54    /// The state object must match the one that was used to make the
55    /// client onionskin that the server is replying to.
56    fn client2<T: AsRef<[u8]>>(
57        state: Self::StateType,
58        msg: T,
59    ) -> Result<(Self::ServerAuxData, Self::KeyGen)>;
60}
61
62/// Trait for an object that handles incoming auxiliary data and
63/// returns the server's auxiliary data to be included in the reply.
64///
65/// This is implemented for `FnMut(&H::ClientAuxData) -> Option<H::ServerAuxData>` automatically.
66pub(crate) trait AuxDataReply<H>
67where
68    H: ServerHandshake + ?Sized,
69{
70    /// Given a list of extensions received from a client, decide
71    /// what extensions to send in reply.
72    ///
73    /// Return None if the handshake should fail.
74    fn reply(&mut self, msg: &H::ClientAuxData) -> Option<H::ServerAuxData>;
75}
76
77impl<F, H> AuxDataReply<H> for F
78where
79    H: ServerHandshake + ?Sized,
80    F: FnMut(&H::ClientAuxData) -> Option<H::ServerAuxData>,
81{
82    fn reply(&mut self, msg: &H::ClientAuxData) -> Option<H::ServerAuxData> {
83        self(msg)
84    }
85}
86
87/// A ServerHandshake is used to handle a client onionskin and generate a
88/// server onionskin.
89pub(crate) trait ServerHandshake {
90    /// The type for the onion key.  This is a private key type.
91    type KeyType;
92    /// The returned key generator type.
93    type KeyGen;
94    /// Type of extra data sent from client (without forward secrecy).
95    type ClientAuxData: ?Sized;
96    /// Type of extra data returned by server (without forward secrecy).
97    type ServerAuxData;
98
99    /// Perform the server handshake.  Take as input a strong PRNG in `rng`, a
100    /// function for processing requested extensions, a slice of all our private
101    /// onion keys, and the client's message.
102    ///
103    /// On success, return a key generator and a server handshake message
104    /// to send in reply.
105    #[allow(dead_code)] // TODO #1383 ????
106    fn server<R: RngCore + CryptoRng, REPLY: AuxDataReply<Self>, T: AsRef<[u8]>>(
107        rng: &mut R,
108        reply_fn: &mut REPLY,
109        key: &[Self::KeyType],
110        msg: T,
111    ) -> RelayHandshakeResult<(Self::KeyGen, Vec<u8>)>;
112}
113
114/// A KeyGenerator is returned by a handshake, and used to generate
115/// session keys for the protocol.
116///
117/// Typically, it wraps a KDF function, and some seed key material.
118///
119/// It can only be used once.
120#[allow(unreachable_pub)] // This is only exported depending on enabled features.
121pub trait KeyGenerator {
122    /// Consume the key
123    fn expand(self, keylen: usize) -> Result<SecretBuf>;
124}
125
126/// Generates keys based on the KDF-TOR function.
127///
128/// This is deprecated and shouldn't be used for new keys.
129pub(crate) struct TapKeyGenerator {
130    /// Seed for the TAP KDF.
131    seed: SecretBuf,
132}
133
134impl TapKeyGenerator {
135    /// Create a key generator based on a provided seed
136    pub(crate) fn new(seed: SecretBuf) -> Self {
137        TapKeyGenerator { seed }
138    }
139}
140
141impl KeyGenerator for TapKeyGenerator {
142    fn expand(self, keylen: usize) -> Result<SecretBuf> {
143        use crate::crypto::ll::kdf::{Kdf, LegacyKdf};
144        LegacyKdf::new(1).derive(&self.seed[..], keylen)
145    }
146}
147
148/// Generates keys based on SHAKE-256.
149#[cfg_attr(feature = "bench", visibility::make(pub))]
150pub(crate) struct ShakeKeyGenerator {
151    /// Seed for the key generator
152    seed: SecretBuf,
153}
154
155impl ShakeKeyGenerator {
156    /// Create a key generator based on a provided seed
157    #[allow(dead_code)] // We'll construct these for v3 onion services
158    #[cfg_attr(feature = "bench", visibility::make(pub))]
159    pub(crate) fn new(seed: SecretBuf) -> Self {
160        ShakeKeyGenerator { seed }
161    }
162}
163
164impl KeyGenerator for ShakeKeyGenerator {
165    fn expand(self, keylen: usize) -> Result<SecretBuf> {
166        use crate::crypto::ll::kdf::{Kdf, ShakeKdf};
167        ShakeKdf::new().derive(&self.seed[..], keylen)
168    }
169}
170
171/// An error produced by a Relay's attempt to handle a client's onion handshake.
172#[derive(Clone, Debug, thiserror::Error)]
173pub(crate) enum RelayHandshakeError {
174    /// An error in parsing a handshake message.
175    #[error("Problem decoding onion handshake")]
176    Fmt(#[from] tor_bytes::Error),
177    /// The client asked for a key we didn't have.
178    #[error("Client asked for a key or ID that we don't have")]
179    MissingKey,
180    /// The client did something wrong with their handshake or cryptography.
181    #[error("Bad handshake from client")]
182    BadClientHandshake,
183    /// An internal error.
184    #[error("Internal error")]
185    Internal(#[from] tor_error::Bug),
186}
187
188impl RelayHandshakeError {
189    /// The reason to use in a DESTROY message for this failure.
190    pub(crate) fn destroy_reason(&self) -> DestroyReason {
191        match self {
192            Self::Fmt(_) => DestroyReason::PROTOCOL,
193            // TODO(relay): Is this right?
194            Self::MissingKey => DestroyReason::OR_IDENTITY,
195            Self::BadClientHandshake => DestroyReason::PROTOCOL,
196            Self::Internal(_) => DestroyReason::INTERNAL,
197        }
198    }
199}
200
201impl HasKind for RelayHandshakeError {
202    fn kind(&self) -> ErrorKind {
203        match self {
204            Self::Fmt(_) => ErrorKind::RemoteProtocolViolation,
205            Self::MissingKey => ErrorKind::RemoteProtocolViolation,
206            Self::BadClientHandshake => ErrorKind::RemoteProtocolViolation,
207            Self::Internal(_) => ErrorKind::Internal,
208        }
209    }
210}
211
212/// Type alias for results from a relay's attempt to handle a client's onion
213/// handshake.
214pub(crate) type RelayHandshakeResult<T> = std::result::Result<T, RelayHandshakeError>;