Line data Source code
1 : /* Copyright (c) 2012-2021, The Tor Project, Inc. */
2 : /* See LICENSE for licensing information */
3 :
4 : /**
5 : * \file onion_ntor.c
6 : *
7 : * \brief Implementation for the ntor handshake.
8 : *
9 : * The ntor circuit-extension handshake was developed as a replacement
10 : * for the old TAP handshake. It uses Elliptic-curve Diffie-Hellman and
11 : * a hash function in order to perform a one-way authenticated key
12 : * exchange. The ntor handshake is meant to replace the old "TAP"
13 : * handshake.
14 : *
15 : * We instantiate ntor with curve25519, HMAC-SHA256, and HKDF.
16 : *
17 : * This handshake, like the other circuit-extension handshakes, is
18 : * invoked from onion.c.
19 : */
20 :
21 : #include "orconfig.h"
22 :
23 : #define ONION_NTOR_PRIVATE
24 :
25 : #include "lib/crypt_ops/crypto_cipher.h"
26 : #include "lib/crypt_ops/crypto_digest.h"
27 : #include "lib/crypt_ops/crypto_hkdf.h"
28 : #include "lib/crypt_ops/crypto_util.h"
29 : #include "lib/ctime/di_ops.h"
30 : #include "lib/log/log.h"
31 : #include "lib/log/util_bug.h"
32 : #include "core/crypto/onion_ntor.h"
33 :
34 : #include <string.h>
35 :
36 : /** Free storage held in an ntor handshake state. */
37 : void
38 1 : ntor_handshake_state_free_(ntor_handshake_state_t *state)
39 : {
40 1 : if (!state)
41 : return;
42 1 : memwipe(state, 0, sizeof(*state));
43 1 : tor_free(state);
44 : }
45 :
46 : /** Convenience function to represent HMAC_SHA256 as our instantiation of
47 : * ntor's "tweaked hash'. Hash the <b>inp_len</b> bytes at <b>inp</b> into
48 : * a DIGEST256_LEN-byte digest at <b>out</b>, with the hash changing
49 : * depending on the value of <b>tweak</b>. */
50 : static void
51 6 : h_tweak(uint8_t *out,
52 : const uint8_t *inp, size_t inp_len,
53 : const char *tweak)
54 : {
55 6 : size_t tweak_len = strlen(tweak);
56 6 : crypto_hmac_sha256((char*)out, tweak, tweak_len, (const char*)inp, inp_len);
57 6 : }
58 :
59 : /** Wrapper around a set of tweak-values for use with the ntor handshake. */
60 : typedef struct tweakset_t {
61 : const char *t_mac;
62 : const char *t_key;
63 : const char *t_verify;
64 : const char *m_expand;
65 : } tweakset_t;
66 :
67 : /** The tweaks to be used with our handshake. */
68 : static const tweakset_t proto1_tweaks = {
69 : #define PROTOID "ntor-curve25519-sha256-1"
70 : #define PROTOID_LEN 24
71 : PROTOID ":mac",
72 : PROTOID ":key_extract",
73 : PROTOID ":verify",
74 : PROTOID ":key_expand"
75 : };
76 :
77 : /** Convenience macro: copy <b>len</b> bytes from <b>inp</b> to <b>ptr</b>,
78 : * and advance <b>ptr</b> by the number of bytes copied. */
79 : #define APPEND(ptr, inp, len) \
80 : STMT_BEGIN { \
81 : memcpy(ptr, (inp), (len)); \
82 : ptr += len; \
83 : } STMT_END
84 :
85 : /**
86 : * Compute the first client-side step of the ntor handshake for communicating
87 : * with a server whose DIGEST_LEN-byte server identity is <b>router_id</b>,
88 : * and whose onion key is <b>router_key</b>. Store the NTOR_ONIONSKIN_LEN-byte
89 : * message in <b>onion_skin_out</b>, and store the handshake state in
90 : * *<b>handshake_state_out</b>. Return 0 on success, -1 on failure.
91 : */
92 : int
93 1 : onion_skin_ntor_create(const uint8_t *router_id,
94 : const curve25519_public_key_t *router_key,
95 : ntor_handshake_state_t **handshake_state_out,
96 : uint8_t *onion_skin_out)
97 : {
98 1 : ntor_handshake_state_t *state;
99 1 : uint8_t *op;
100 :
101 1 : state = tor_malloc_zero(sizeof(ntor_handshake_state_t));
102 :
103 1 : memcpy(state->router_id, router_id, DIGEST_LEN);
104 1 : memcpy(&state->pubkey_B, router_key, sizeof(curve25519_public_key_t));
105 1 : if (curve25519_secret_key_generate(&state->seckey_x, 0) < 0) {
106 : /* LCOV_EXCL_START
107 : * Secret key generation should be unable to fail when the key isn't
108 : * marked as "extra-strong" */
109 : tor_assert_nonfatal_unreached();
110 : tor_free(state);
111 : return -1;
112 : /* LCOV_EXCL_STOP */
113 : }
114 1 : curve25519_public_key_generate(&state->pubkey_X, &state->seckey_x);
115 :
116 1 : op = onion_skin_out;
117 1 : APPEND(op, router_id, DIGEST_LEN);
118 1 : APPEND(op, router_key->public_key, CURVE25519_PUBKEY_LEN);
119 1 : APPEND(op, state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
120 1 : tor_assert(op == onion_skin_out + NTOR_ONIONSKIN_LEN);
121 :
122 1 : *handshake_state_out = state;
123 :
124 1 : return 0;
125 : }
126 :
127 : #define SERVER_STR "Server"
128 : #define SERVER_STR_LEN 6
129 :
130 : #define SECRET_INPUT_LEN (CURVE25519_PUBKEY_LEN * 3 + \
131 : CURVE25519_OUTPUT_LEN * 2 + \
132 : DIGEST_LEN + PROTOID_LEN)
133 : #define AUTH_INPUT_LEN (DIGEST256_LEN + DIGEST_LEN + \
134 : CURVE25519_PUBKEY_LEN*3 + \
135 : PROTOID_LEN + SERVER_STR_LEN)
136 :
137 : /**
138 : * Perform the server side of an ntor handshake. Given an
139 : * NTOR_ONIONSKIN_LEN-byte message in <b>onion_skin</b>, our own identity
140 : * fingerprint as <b>my_node_id</b>, and an associative array mapping public
141 : * onion keys to curve25519_keypair_t in <b>private_keys</b>, attempt to
142 : * perform the handshake. Use <b>junk_keys</b> if present if the handshake
143 : * indicates an unrecognized public key. Write an NTOR_REPLY_LEN-byte
144 : * message to send back to the client into <b>handshake_reply_out</b>, and
145 : * generate <b>key_out_len</b> bytes of key material in <b>key_out</b>. Return
146 : * 0 on success, -1 on failure.
147 : */
148 : int
149 1 : onion_skin_ntor_server_handshake(const uint8_t *onion_skin,
150 : const di_digest256_map_t *private_keys,
151 : const curve25519_keypair_t *junk_keys,
152 : const uint8_t *my_node_id,
153 : uint8_t *handshake_reply_out,
154 : uint8_t *key_out,
155 : size_t key_out_len)
156 : {
157 1 : const tweakset_t *T = &proto1_tweaks;
158 : /* Sensitive stack-allocated material. Kept in an anonymous struct to make
159 : * it easy to wipe. */
160 1 : struct {
161 : uint8_t secret_input[SECRET_INPUT_LEN];
162 : uint8_t auth_input[AUTH_INPUT_LEN];
163 : curve25519_public_key_t pubkey_X;
164 : curve25519_secret_key_t seckey_y;
165 : curve25519_public_key_t pubkey_Y;
166 : uint8_t verify[DIGEST256_LEN];
167 : } s;
168 1 : uint8_t *si = s.secret_input, *ai = s.auth_input;
169 1 : const curve25519_keypair_t *keypair_bB;
170 1 : int bad;
171 :
172 : /* Decode the onion skin */
173 : /* XXXX Does this possible early-return business threaten our security? */
174 1 : if (tor_memneq(onion_skin, my_node_id, DIGEST_LEN))
175 : return -1;
176 : /* Note that on key-not-found, we go through with this operation anyway,
177 : * using "junk_keys". This will result in failed authentication, but won't
178 : * leak whether we recognized the key. */
179 1 : keypair_bB = dimap_search(private_keys, onion_skin + DIGEST_LEN,
180 : (void*)junk_keys);
181 1 : if (!keypair_bB)
182 : return -1;
183 :
184 1 : memcpy(s.pubkey_X.public_key, onion_skin+DIGEST_LEN+DIGEST256_LEN,
185 : CURVE25519_PUBKEY_LEN);
186 :
187 : /* Make y, Y */
188 1 : curve25519_secret_key_generate(&s.seckey_y, 0);
189 1 : curve25519_public_key_generate(&s.pubkey_Y, &s.seckey_y);
190 :
191 : /* NOTE: If we ever use a group other than curve25519, or a different
192 : * representation for its points, we may need to perform different or
193 : * additional checks on X here and on Y in the client handshake, or lose our
194 : * security properties. What checks we need would depend on the properties
195 : * of the group and its representation.
196 : *
197 : * In short: if you use anything other than curve25519, this aspect of the
198 : * code will need to be reconsidered carefully. */
199 :
200 : /* build secret_input */
201 1 : curve25519_handshake(si, &s.seckey_y, &s.pubkey_X);
202 1 : bad = safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN);
203 1 : si += CURVE25519_OUTPUT_LEN;
204 1 : curve25519_handshake(si, &keypair_bB->seckey, &s.pubkey_X);
205 1 : bad |= safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN);
206 1 : si += CURVE25519_OUTPUT_LEN;
207 :
208 1 : APPEND(si, my_node_id, DIGEST_LEN);
209 1 : APPEND(si, keypair_bB->pubkey.public_key, CURVE25519_PUBKEY_LEN);
210 1 : APPEND(si, s.pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
211 1 : APPEND(si, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
212 1 : APPEND(si, PROTOID, PROTOID_LEN);
213 1 : tor_assert(si == s.secret_input + sizeof(s.secret_input));
214 :
215 : /* Compute hashes of secret_input */
216 1 : h_tweak(s.verify, s.secret_input, sizeof(s.secret_input), T->t_verify);
217 :
218 : /* Compute auth_input */
219 1 : APPEND(ai, s.verify, DIGEST256_LEN);
220 1 : APPEND(ai, my_node_id, DIGEST_LEN);
221 1 : APPEND(ai, keypair_bB->pubkey.public_key, CURVE25519_PUBKEY_LEN);
222 1 : APPEND(ai, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
223 1 : APPEND(ai, s.pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
224 1 : APPEND(ai, PROTOID, PROTOID_LEN);
225 1 : APPEND(ai, SERVER_STR, SERVER_STR_LEN);
226 1 : tor_assert(ai == s.auth_input + sizeof(s.auth_input));
227 :
228 : /* Build the reply */
229 1 : memcpy(handshake_reply_out, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
230 1 : h_tweak(handshake_reply_out+CURVE25519_PUBKEY_LEN,
231 : s.auth_input, sizeof(s.auth_input),
232 : T->t_mac);
233 :
234 : /* Generate the key material */
235 1 : crypto_expand_key_material_rfc5869_sha256(
236 : s.secret_input, sizeof(s.secret_input),
237 : (const uint8_t*)T->t_key, strlen(T->t_key),
238 : (const uint8_t*)T->m_expand, strlen(T->m_expand),
239 : key_out, key_out_len);
240 :
241 : /* Wipe all of our local state */
242 1 : memwipe(&s, 0, sizeof(s));
243 :
244 1 : return bad ? -1 : 0;
245 : }
246 :
247 : /**
248 : * Perform the final client side of the ntor handshake, using the state in
249 : * <b>handshake_state</b> and the server's NTOR_REPLY_LEN-byte reply in
250 : * <b>handshake_reply</b>. Generate <b>key_out_len</b> bytes of key material
251 : * in <b>key_out</b>. Return 0 on success, -1 on failure.
252 : */
253 : int
254 2 : onion_skin_ntor_client_handshake(
255 : const ntor_handshake_state_t *handshake_state,
256 : const uint8_t *handshake_reply,
257 : uint8_t *key_out,
258 : size_t key_out_len,
259 : const char **msg_out)
260 : {
261 2 : const tweakset_t *T = &proto1_tweaks;
262 : /* Sensitive stack-allocated material. Kept in an anonymous struct to make
263 : * it easy to wipe. */
264 2 : struct {
265 : curve25519_public_key_t pubkey_Y;
266 : uint8_t secret_input[SECRET_INPUT_LEN];
267 : uint8_t verify[DIGEST256_LEN];
268 : uint8_t auth_input[AUTH_INPUT_LEN];
269 : uint8_t auth[DIGEST256_LEN];
270 : } s;
271 2 : uint8_t *ai = s.auth_input, *si = s.secret_input;
272 2 : const uint8_t *auth_candidate;
273 2 : int bad;
274 :
275 : /* Decode input */
276 2 : memcpy(s.pubkey_Y.public_key, handshake_reply, CURVE25519_PUBKEY_LEN);
277 2 : auth_candidate = handshake_reply + CURVE25519_PUBKEY_LEN;
278 :
279 : /* See note in server_handshake above about checking points. The
280 : * circumstances under which we'd need to check Y for membership are
281 : * different than those under which we'd be checking X. */
282 :
283 : /* Compute secret_input */
284 2 : curve25519_handshake(si, &handshake_state->seckey_x, &s.pubkey_Y);
285 2 : bad = safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN);
286 2 : si += CURVE25519_OUTPUT_LEN;
287 2 : curve25519_handshake(si, &handshake_state->seckey_x,
288 : &handshake_state->pubkey_B);
289 2 : bad |= (safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN) << 1);
290 2 : si += CURVE25519_OUTPUT_LEN;
291 2 : APPEND(si, handshake_state->router_id, DIGEST_LEN);
292 2 : APPEND(si, handshake_state->pubkey_B.public_key, CURVE25519_PUBKEY_LEN);
293 2 : APPEND(si, handshake_state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
294 2 : APPEND(si, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
295 2 : APPEND(si, PROTOID, PROTOID_LEN);
296 2 : tor_assert(si == s.secret_input + sizeof(s.secret_input));
297 :
298 : /* Compute verify from secret_input */
299 2 : h_tweak(s.verify, s.secret_input, sizeof(s.secret_input), T->t_verify);
300 :
301 : /* Compute auth_input */
302 2 : APPEND(ai, s.verify, DIGEST256_LEN);
303 2 : APPEND(ai, handshake_state->router_id, DIGEST_LEN);
304 2 : APPEND(ai, handshake_state->pubkey_B.public_key, CURVE25519_PUBKEY_LEN);
305 2 : APPEND(ai, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
306 2 : APPEND(ai, handshake_state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
307 2 : APPEND(ai, PROTOID, PROTOID_LEN);
308 2 : APPEND(ai, SERVER_STR, SERVER_STR_LEN);
309 2 : tor_assert(ai == s.auth_input + sizeof(s.auth_input));
310 :
311 : /* Compute auth */
312 2 : h_tweak(s.auth, s.auth_input, sizeof(s.auth_input), T->t_mac);
313 :
314 2 : bad |= (tor_memneq(s.auth, auth_candidate, DIGEST256_LEN) << 2);
315 :
316 2 : crypto_expand_key_material_rfc5869_sha256(
317 : s.secret_input, sizeof(s.secret_input),
318 : (const uint8_t*)T->t_key, strlen(T->t_key),
319 : (const uint8_t*)T->m_expand, strlen(T->m_expand),
320 : key_out, key_out_len);
321 :
322 2 : memwipe(&s, 0, sizeof(s));
323 :
324 2 : if (bad) {
325 1 : if (bad & 4) {
326 1 : if (msg_out)
327 1 : *msg_out = NULL; /* Don't report this one; we probably just had the
328 : * wrong onion key.*/
329 1 : log_fn(LOG_INFO, LD_PROTOCOL,
330 : "Invalid result from curve25519 handshake: %d", bad);
331 : }
332 1 : if (bad & 3) {
333 1 : if (msg_out)
334 1 : *msg_out = "Zero output from curve25519 handshake";
335 1 : log_fn(LOG_WARN, LD_PROTOCOL,
336 : "Invalid result from curve25519 handshake: %d", bad);
337 : }
338 : }
339 :
340 2 : return bad ? -1 : 0;
341 : }
|