tor_proto/crypto/handshake/
ntor.rs

1//! Implements the ntor handshake, as used in modern Tor.
2
3use std::borrow::Borrow;
4
5use super::{AuxDataReply, KeyGenerator, RelayHandshakeError, RelayHandshakeResult};
6use crate::util::ct;
7use crate::{Error, Result};
8use tor_bytes::{EncodeResult, Reader, SecretBuf, Writer};
9use tor_error::into_internal;
10use tor_llcrypto::d;
11use tor_llcrypto::pk::curve25519::*;
12use tor_llcrypto::pk::rsa::RsaIdentity;
13use tor_llcrypto::util::ct::ct_lookup;
14
15use digest::Mac;
16use rand_core::{CryptoRng, RngCore};
17
18/// Client side of the Ntor handshake.
19pub(crate) struct NtorClient;
20
21impl super::ClientHandshake for NtorClient {
22    type KeyType = NtorPublicKey;
23    type StateType = NtorHandshakeState;
24    type KeyGen = NtorHkdfKeyGenerator;
25    type ClientAuxData = ();
26    type ServerAuxData = ();
27
28    fn client1<R: RngCore + CryptoRng, M: Borrow<()>>(
29        rng: &mut R,
30        key: &Self::KeyType,
31        _client_aux_data: &M,
32    ) -> Result<(Self::StateType, Vec<u8>)> {
33        client_handshake_ntor_v1(rng, key)
34    }
35
36    fn client2<T: AsRef<[u8]>>(state: Self::StateType, msg: T) -> Result<((), Self::KeyGen)> {
37        let keygen = client_handshake2_ntor_v1(msg, &state)?;
38        Ok(((), keygen))
39    }
40}
41
42/// Server side of the ntor handshake.
43#[allow(dead_code)] // TODO #1467
44pub(crate) struct NtorServer;
45
46impl super::ServerHandshake for NtorServer {
47    type KeyType = NtorSecretKey;
48    type KeyGen = NtorHkdfKeyGenerator;
49    type ClientAuxData = ();
50    type ServerAuxData = ();
51
52    fn server<R: RngCore + CryptoRng, REPLY: AuxDataReply<Self>, T: AsRef<[u8]>>(
53        rng: &mut R,
54        reply_fn: &mut REPLY,
55        key: &[Self::KeyType],
56        msg: T,
57    ) -> RelayHandshakeResult<(Self::KeyGen, Vec<u8>)> {
58        let _reply_msg = reply_fn
59            .reply(&())
60            .ok_or(RelayHandshakeError::BadClientHandshake)?;
61
62        server_handshake_ntor_v1(rng, msg, key)
63    }
64}
65
66/// A set of public keys used by a client to initiate an ntor handshake.
67#[derive(Clone, Debug)]
68pub(crate) struct NtorPublicKey {
69    /// Public RSA identity fingerprint for the relay; used in authentication
70    /// calculation.
71    pub(crate) id: RsaIdentity,
72    /// Public curve25519 ntor key for the relay.
73    pub(crate) pk: PublicKey,
74}
75
76/// A secret key used by a relay to answer an ntor request
77#[allow(dead_code)] // TODO #1467
78pub(crate) struct NtorSecretKey {
79    /// Public key components; must match those held by the client.
80    pk: NtorPublicKey,
81    /// Secret curve25519 ntor key for the relay; must correspond to
82    /// the public key in pk.pk.
83    sk: StaticSecret,
84}
85
86use subtle::{Choice, ConstantTimeEq};
87impl NtorSecretKey {
88    /// Construct a new NtorSecretKey from its components.
89    #[allow(unused)]
90    pub(crate) fn new(sk: StaticSecret, pk: PublicKey, id: RsaIdentity) -> Self {
91        NtorSecretKey {
92            pk: NtorPublicKey { id, pk },
93            sk,
94        }
95    }
96    /// Return true if the curve25519 public key in `self` matches `pk`.
97    ///
98    /// Used for looking up keys in an array.
99    #[allow(dead_code)] // TODO #1467
100    fn matches_pk(&self, pk: &PublicKey) -> Choice {
101        self.pk.pk.as_bytes().ct_eq(pk.as_bytes())
102    }
103}
104
105/// Client state for an ntor handshake.
106pub(crate) struct NtorHandshakeState {
107    /// The relay's public key.  We need to remember this since it is
108    /// used to finish the handshake.
109    relay_public: NtorPublicKey,
110    /// The temporary curve25519 secret (x) that we've generated for
111    /// this handshake.
112    // We'd like to EphemeralSecret here, but we can't since we need
113    // to use it twice.
114    my_sk: StaticSecret,
115    /// The public key `X` corresponding to my_sk.
116    my_public: PublicKey,
117}
118
119/// KeyGenerator for use with ntor circuit handshake.
120pub(crate) struct NtorHkdfKeyGenerator {
121    /// Secret key information derived from the handshake, used as input
122    /// to HKDF
123    seed: SecretBuf,
124}
125
126impl NtorHkdfKeyGenerator {
127    /// Create a new key generator to expand a given seed
128    pub(crate) fn new(seed: SecretBuf) -> Self {
129        NtorHkdfKeyGenerator { seed }
130    }
131}
132
133impl KeyGenerator for NtorHkdfKeyGenerator {
134    fn expand(self, keylen: usize) -> Result<SecretBuf> {
135        let ntor1_key = &b"ntor-curve25519-sha256-1:key_extract"[..];
136        let ntor1_expand = &b"ntor-curve25519-sha256-1:key_expand"[..];
137        use crate::crypto::ll::kdf::{Kdf, Ntor1Kdf};
138        Ntor1Kdf::new(ntor1_key, ntor1_expand).derive(&self.seed[..], keylen)
139    }
140}
141
142/// Alias for an HMAC output, used to validate correctness of a handshake.
143type Authcode = digest::CtOutput<hmac::Hmac<d::Sha256>>;
144
145/// Perform a client handshake, generating an onionskin and a state object
146fn client_handshake_ntor_v1<R>(
147    rng: &mut R,
148    relay_public: &NtorPublicKey,
149) -> Result<(NtorHandshakeState, Vec<u8>)>
150where
151    R: RngCore + CryptoRng,
152{
153    let my_sk = StaticSecret::random_from_rng(rng);
154    let my_public = PublicKey::from(&my_sk);
155
156    client_handshake_ntor_v1_no_keygen(my_public, my_sk, relay_public)
157}
158
159/// Helper: client handshake _without_ generating  new keys.
160fn client_handshake_ntor_v1_no_keygen(
161    my_public: PublicKey,
162    my_sk: StaticSecret,
163    relay_public: &NtorPublicKey,
164) -> Result<(NtorHandshakeState, Vec<u8>)> {
165    let mut v: Vec<u8> = Vec::new();
166
167    v.write(&relay_public.id)
168        .and_then(|_| v.write(&relay_public.pk))
169        .and_then(|_| v.write(&my_public))
170        .map_err(|e| Error::from_bytes_enc(e, "Can't encode client handshake."))?;
171
172    assert_eq!(v.len(), 20 + 32 + 32);
173
174    let state = NtorHandshakeState {
175        relay_public: relay_public.clone(),
176        my_public,
177        my_sk,
178    };
179
180    Ok((state, v))
181}
182
183/// Complete a client handshake, returning a key generator on success.
184fn client_handshake2_ntor_v1<T>(msg: T, state: &NtorHandshakeState) -> Result<NtorHkdfKeyGenerator>
185where
186    T: AsRef<[u8]>,
187{
188    let mut cur = Reader::from_slice(msg.as_ref());
189    let their_pk: PublicKey = cur
190        .extract()
191        .map_err(|e| Error::from_bytes_err(e, "v3 ntor handshake"))?;
192    let auth: Authcode = cur
193        .extract()
194        .map_err(|e| Error::from_bytes_err(e, "v3 ntor handshake"))?;
195
196    let xy = state.my_sk.diffie_hellman(&their_pk);
197    let xb = state.my_sk.diffie_hellman(&state.relay_public.pk);
198
199    let (keygen, authcode) =
200        ntor_derive(&xy, &xb, &state.relay_public, &state.my_public, &their_pk)
201            .map_err(into_internal!("Error deriving keys"))?;
202
203    let okay = authcode.ct_eq(&auth)
204        & ct::bool_to_choice(xy.was_contributory())
205        & ct::bool_to_choice(xb.was_contributory());
206
207    if okay.into() {
208        Ok(keygen)
209    } else {
210        Err(Error::BadCircHandshakeAuth)
211    }
212}
213
214/// helper: compute a key generator and an authentication code from a set
215/// of ntor parameters.
216///
217/// These parameter names are as described in tor-spec.txt
218fn ntor_derive(
219    xy: &SharedSecret,
220    xb: &SharedSecret,
221    server_pk: &NtorPublicKey,
222    x: &PublicKey,
223    y: &PublicKey,
224) -> EncodeResult<(NtorHkdfKeyGenerator, Authcode)> {
225    let ntor1_protoid = &b"ntor-curve25519-sha256-1"[..];
226    let ntor1_mac = &b"ntor-curve25519-sha256-1:mac"[..];
227    let ntor1_verify = &b"ntor-curve25519-sha256-1:verify"[..];
228    let server_string = &b"Server"[..];
229
230    let mut secret_input = SecretBuf::new();
231    secret_input.write(xy)?; // EXP(X,y)
232    secret_input.write(xb)?; // EXP(X,b)
233    secret_input.write(&server_pk.id)?; // ID
234    secret_input.write(&server_pk.pk)?; // B
235    secret_input.write(x)?; // X
236    secret_input.write(y)?; // Y
237    secret_input.write(ntor1_protoid)?; // PROTOID
238
239    use hmac::Hmac;
240    use tor_llcrypto::d::Sha256;
241    let verify = {
242        let mut m =
243            Hmac::<Sha256>::new_from_slice(ntor1_verify).expect("Hmac allows keys of any size");
244        m.update(&secret_input[..]);
245        m.finalize()
246    };
247    let mut auth_input = Vec::new();
248    auth_input.write_and_consume(verify)?; // verify
249    auth_input.write(&server_pk.id)?; // ID
250    auth_input.write(&server_pk.pk)?; // B
251    auth_input.write(y)?; // Y
252    auth_input.write(x)?; // X
253    auth_input.write(ntor1_protoid)?; // PROTOID
254    auth_input.write(server_string)?; // "Server"
255
256    let auth_mac = {
257        let mut m =
258            Hmac::<Sha256>::new_from_slice(ntor1_mac).expect("Hmac allows keys of any size");
259        m.update(&auth_input[..]);
260        m.finalize()
261    };
262
263    let keygen = NtorHkdfKeyGenerator::new(secret_input);
264    Ok((keygen, auth_mac))
265}
266
267/// Perform a server-side ntor handshake.
268///
269/// On success returns a key generator and a server onionskin.
270#[allow(dead_code)] // TODO #1467
271fn server_handshake_ntor_v1<R, T>(
272    rng: &mut R,
273    msg: T,
274    keys: &[NtorSecretKey],
275) -> RelayHandshakeResult<(NtorHkdfKeyGenerator, Vec<u8>)>
276where
277    R: RngCore + CryptoRng,
278    T: AsRef<[u8]>,
279{
280    // TODO(nickm): we generate this key whether or not we are
281    // actually going to find our nodeid or keyid. Perhaps we should
282    // delay that till later?  It shouldn't matter for most cases,
283    // though.
284    let ephem = EphemeralSecret::random_from_rng(rng);
285    let ephem_pub = PublicKey::from(&ephem);
286
287    server_handshake_ntor_v1_no_keygen(ephem_pub, ephem, msg, keys)
288}
289
290/// Helper: perform a server handshake without generating any new keys.
291fn server_handshake_ntor_v1_no_keygen<T>(
292    ephem_pub: PublicKey,
293    ephem: EphemeralSecret,
294    msg: T,
295    keys: &[NtorSecretKey],
296) -> RelayHandshakeResult<(NtorHkdfKeyGenerator, Vec<u8>)>
297where
298    T: AsRef<[u8]>,
299{
300    let mut cur = Reader::from_slice(msg.as_ref());
301
302    let my_id: RsaIdentity = cur.extract()?;
303    let my_key: PublicKey = cur.extract()?;
304    let their_pk: PublicKey = cur.extract()?;
305
306    let keypair = ct_lookup(keys, |key| key.matches_pk(&my_key));
307    let keypair = match keypair {
308        Some(k) => k,
309        None => return Err(RelayHandshakeError::MissingKey),
310    };
311
312    if my_id != keypair.pk.id {
313        return Err(RelayHandshakeError::MissingKey);
314    }
315
316    let xy = ephem.diffie_hellman(&their_pk);
317    let xb = keypair.sk.diffie_hellman(&their_pk);
318
319    let okay =
320        ct::bool_to_choice(xy.was_contributory()) & ct::bool_to_choice(xb.was_contributory());
321
322    let (keygen, authcode) = ntor_derive(&xy, &xb, &keypair.pk, &their_pk, &ephem_pub)
323        .map_err(into_internal!("Error deriving keys"))?;
324
325    let mut reply: Vec<u8> = Vec::new();
326    reply
327        .write(&ephem_pub)
328        .and_then(|_| reply.write_and_consume(authcode))
329        .map_err(into_internal!(
330            "Generated relay handshake we couldn't encode"
331        ))?;
332
333    if okay.into() {
334        Ok((keygen, reply))
335    } else {
336        Err(RelayHandshakeError::BadClientHandshake)
337    }
338}
339
340#[cfg(test)]
341mod tests {
342    #![allow(clippy::unwrap_used)]
343    use super::*;
344    use crate::crypto::testing::FakePRNG;
345    use tor_basic_utils::test_rng::testing_rng;
346
347    #[test]
348    fn simple() -> Result<()> {
349        use crate::crypto::handshake::{ClientHandshake, ServerHandshake};
350        let mut rng = testing_rng();
351        let relay_secret = StaticSecret::random_from_rng(&mut rng);
352        let relay_public = PublicKey::from(&relay_secret);
353        let relay_identity = RsaIdentity::from_bytes(&[12; 20]).unwrap();
354        let relay_ntpk = NtorPublicKey {
355            id: relay_identity,
356            pk: relay_public,
357        };
358        let (state, cmsg) = NtorClient::client1(&mut rng, &relay_ntpk, &())?;
359
360        let relay_ntsk = NtorSecretKey {
361            pk: relay_ntpk,
362            sk: relay_secret,
363        };
364        let relay_ntsks = [relay_ntsk];
365
366        let (skeygen, smsg) =
367            NtorServer::server(&mut rng, &mut |_: &()| Some(()), &relay_ntsks, &cmsg).unwrap();
368
369        let (_extensions, ckeygen) = NtorClient::client2(state, smsg)?;
370
371        let skeys = skeygen.expand(55)?;
372        let ckeys = ckeygen.expand(55)?;
373
374        assert_eq!(skeys, ckeys);
375
376        Ok(())
377    }
378
379    fn make_fake_ephem_key(bytes: &[u8]) -> EphemeralSecret {
380        assert_eq!(bytes.len(), 32);
381        let rng = FakePRNG::new(bytes);
382        EphemeralSecret::random_from_rng(rng)
383    }
384
385    #[test]
386    fn testvec() -> Result<()> {
387        use hex_literal::hex;
388
389        let b_sk = hex!("4820544f4c4420594f5520444f474954204b454550532048415050454e494e47");
390        let b_pk = hex!("ccbc8541904d18af08753eae967874749e6149f873de937f57f8fd903a21c471");
391        let x_sk = hex!("706f6461792069207075742e2e2e2e2e2e2e2e4a454c4c59206f6e2074686973");
392        let x_pk = hex!("e65dfdbef8b2635837fe2cebc086a8096eae3213e6830dc407516083d412b078");
393        let y_sk = hex!("70686520737175697272656c2e2e2e2e2e2e2e2e686173206869732067616d65");
394        let y_pk = hex!("390480a14362761d6aec1fea840f6e9e928fb2adb7b25c670be1045e35133a37");
395        let id = hex!("69546f6c64596f7541626f75745374616972732e");
396        let client_handshake = hex!("69546f6c64596f7541626f75745374616972732eccbc8541904d18af08753eae967874749e6149f873de937f57f8fd903a21c471e65dfdbef8b2635837fe2cebc086a8096eae3213e6830dc407516083d412b078");
397        let server_handshake = hex!("390480a14362761d6aec1fea840f6e9e928fb2adb7b25c670be1045e35133a371cbdf68b89923e1f85e8e18ee6e805ea333fe4849c790ffd2670bd80fec95cc8");
398        let keys = hex!("0c62dee7f48893370d0ef896758d35729867beef1a5121df80e00f79ed349af39b51cae125719182f19d932a667dae1afbf2e336e6910e7822223e763afad0a13342157969dc6b79");
399
400        let relay_pk = NtorPublicKey {
401            id: RsaIdentity::from_bytes(&id).unwrap(),
402            pk: b_pk.into(),
403        };
404        let relay_sk = NtorSecretKey {
405            pk: relay_pk.clone(),
406            sk: b_sk.into(),
407        };
408
409        let (state, create_msg) =
410            client_handshake_ntor_v1_no_keygen(x_pk.into(), x_sk.into(), &relay_pk).unwrap();
411        assert_eq!(&create_msg[..], &client_handshake[..]);
412
413        let ephem = make_fake_ephem_key(&y_sk[..]);
414        let ephem_pub = y_pk.into();
415        let (s_keygen, created_msg) =
416            server_handshake_ntor_v1_no_keygen(ephem_pub, ephem, &create_msg[..], &[relay_sk])
417                .unwrap();
418        assert_eq!(&created_msg[..], &server_handshake[..]);
419
420        let c_keygen = client_handshake2_ntor_v1(created_msg, &state)?;
421
422        let c_keys = c_keygen.expand(keys.len())?;
423        let s_keys = s_keygen.expand(keys.len())?;
424        assert_eq!(&c_keys[..], &keys[..]);
425        assert_eq!(&s_keys[..], &keys[..]);
426
427        Ok(())
428    }
429
430    #[test]
431    fn failing_handshakes() {
432        use crate::crypto::handshake::{ClientHandshake, ServerHandshake};
433        let mut rng = testing_rng();
434
435        // Set up keys.
436        let relay_secret = StaticSecret::random_from_rng(&mut rng);
437        let relay_public = PublicKey::from(&relay_secret);
438        let wrong_public = PublicKey::from([16_u8; 32]);
439        let relay_identity = RsaIdentity::from_bytes(&[12; 20]).unwrap();
440        let wrong_identity = RsaIdentity::from_bytes(&[13; 20]).unwrap();
441        let relay_ntpk = NtorPublicKey {
442            id: relay_identity,
443            pk: relay_public,
444        };
445        let relay_ntsk = NtorSecretKey {
446            pk: relay_ntpk.clone(),
447            sk: relay_secret,
448        };
449        let relay_ntsks = &[relay_ntsk];
450        let wrong_ntpk1 = NtorPublicKey {
451            id: wrong_identity,
452            pk: relay_public,
453        };
454        let wrong_ntpk2 = NtorPublicKey {
455            id: relay_identity,
456            pk: wrong_public,
457        };
458
459        // If the client uses the wrong keys, the relay should reject the
460        // handshake.
461        let (_, handshake1) = NtorClient::client1(&mut rng, &wrong_ntpk1, &()).unwrap();
462        let (_, handshake2) = NtorClient::client1(&mut rng, &wrong_ntpk2, &()).unwrap();
463        let (st3, handshake3) = NtorClient::client1(&mut rng, &relay_ntpk, &()).unwrap();
464
465        let ans1 = NtorServer::server(&mut rng, &mut |_: &()| Some(()), relay_ntsks, &handshake1);
466        let ans2 = NtorServer::server(&mut rng, &mut |_: &()| Some(()), relay_ntsks, &handshake2);
467
468        assert!(ans1.is_err());
469        assert!(ans2.is_err());
470
471        // If the relay's message is tampered with, the client will
472        // reject the handshake.
473        let (_, mut smsg) =
474            NtorServer::server(&mut rng, &mut |_: &()| Some(()), relay_ntsks, &handshake3).unwrap();
475        smsg[60] ^= 7;
476        let ans3 = NtorClient::client2(st3, smsg);
477        assert!(ans3.is_err());
478    }
479}