Line data Source code
1 : /* Copyright (c) 2017-2021, The Tor Project, Inc. */
2 : /* See LICENSE for licensing information */
3 :
4 : /** \file hs_ntor.c
5 : * \brief Implements the ntor variant used in Tor hidden services.
6 : *
7 : * \details
8 : * This module handles the variant of the ntor handshake that is documented in
9 : * section [NTOR-WITH-EXTRA-DATA] of rend-spec-ng.txt .
10 : *
11 : * The functions in this file provide an API that should be used when sending
12 : * or receiving INTRODUCE1/RENDEZVOUS1 cells to generate the various key
13 : * material required to create and handle those cells.
14 : *
15 : * In the case of INTRODUCE1 it provides encryption and MAC keys to
16 : * encode/decode the encrypted blob (see hs_ntor_intro_cell_keys_t). The
17 : * relevant pub functions are hs_ntor_{client,service}_get_introduce1_keys().
18 : *
19 : * In the case of RENDEZVOUS1 it calculates the MAC required to authenticate
20 : * the cell, and also provides the key seed that is used to derive the crypto
21 : * material for rendezvous encryption (see hs_ntor_rend_cell_keys_t). The
22 : * relevant pub functions are hs_ntor_{client,service}_get_rendezvous1_keys().
23 : * It also provides a function (hs_ntor_circuit_key_expansion()) that does the
24 : * rendezvous key expansion to setup end-to-end rend circuit keys.
25 : */
26 :
27 : #include "core/or/or.h"
28 : #include "lib/crypt_ops/crypto_util.h"
29 : #include "lib/crypt_ops/crypto_curve25519.h"
30 : #include "lib/crypt_ops/crypto_ed25519.h"
31 : #include "core/crypto/hs_ntor.h"
32 :
33 : /* String constants used by the ntor HS protocol */
34 : #define PROTOID "tor-hs-ntor-curve25519-sha3-256-1"
35 : #define PROTOID_LEN (sizeof(PROTOID) - 1)
36 : #define SERVER_STR "Server"
37 : #define SERVER_STR_LEN (sizeof(SERVER_STR) - 1)
38 :
39 : /* Protocol-specific tweaks to our crypto inputs */
40 : #define T_HSENC PROTOID ":hs_key_extract"
41 : #define T_HSENC_LEN (sizeof(T_HSENC) - 1)
42 : #define T_HSVERIFY PROTOID ":hs_verify"
43 : #define T_HSMAC PROTOID ":hs_mac"
44 : #define M_HSEXPAND PROTOID ":hs_key_expand"
45 : #define M_HSEXPAND_LEN (sizeof(M_HSEXPAND) - 1)
46 :
47 : /************************* Helper functions: *******************************/
48 :
49 : /** Helper macro: copy <b>len</b> bytes from <b>inp</b> to <b>ptr</b> and
50 : *advance <b>ptr</b> by the number of bytes copied. Stolen from onion_ntor.c */
51 : #define APPEND(ptr, inp, len) \
52 : STMT_BEGIN { \
53 : memcpy(ptr, (inp), (len)); \
54 : ptr += len; \
55 : } STMT_END
56 :
57 : /* Length of EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID */
58 : #define REND_SECRET_HS_INPUT_LEN (CURVE25519_OUTPUT_LEN * 2 + \
59 : ED25519_PUBKEY_LEN + CURVE25519_PUBKEY_LEN * 3 + PROTOID_LEN)
60 : /* Length of auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" */
61 : #define REND_AUTH_INPUT_LEN (DIGEST256_LEN + ED25519_PUBKEY_LEN + \
62 : CURVE25519_PUBKEY_LEN * 3 + PROTOID_LEN + SERVER_STR_LEN)
63 :
64 : /** Helper function: Compute the last part of the HS ntor handshake which
65 : * derives key material necessary to create and handle RENDEZVOUS1
66 : * cells. Function used by both client and service. The actual calculations is
67 : * as follows:
68 : *
69 : * NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc)
70 : * verify = MAC(rend_secret_hs_input, t_hsverify)
71 : * auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server"
72 : * auth_input_mac = MAC(auth_input, t_hsmac)
73 : *
74 : * where in the above, AUTH_KEY is <b>intro_auth_pubkey</b>, B is
75 : * <b>intro_enc_pubkey</b>, Y is <b>service_ephemeral_rend_pubkey</b>, and X
76 : * is <b>client_ephemeral_enc_pubkey</b>. The provided
77 : * <b>rend_secret_hs_input</b> is of size REND_SECRET_HS_INPUT_LEN.
78 : *
79 : * The final results of NTOR_KEY_SEED and auth_input_mac are placed in
80 : * <b>hs_ntor_rend_cell_keys_out</b>. Return 0 if everything went fine. */
81 : static int
82 4 : get_rendezvous1_key_material(const uint8_t *rend_secret_hs_input,
83 : const ed25519_public_key_t *intro_auth_pubkey,
84 : const curve25519_public_key_t *intro_enc_pubkey,
85 : const curve25519_public_key_t *service_ephemeral_rend_pubkey,
86 : const curve25519_public_key_t *client_ephemeral_enc_pubkey,
87 : hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out)
88 : {
89 4 : int bad = 0;
90 4 : uint8_t ntor_key_seed[DIGEST256_LEN];
91 4 : uint8_t ntor_verify[DIGEST256_LEN];
92 4 : uint8_t rend_auth_input[REND_AUTH_INPUT_LEN];
93 4 : uint8_t rend_cell_auth[DIGEST256_LEN];
94 4 : uint8_t *ptr;
95 :
96 : /* Let's build NTOR_KEY_SEED */
97 4 : crypto_mac_sha3_256(ntor_key_seed, sizeof(ntor_key_seed),
98 : rend_secret_hs_input, REND_SECRET_HS_INPUT_LEN,
99 : (const uint8_t *)T_HSENC, strlen(T_HSENC));
100 4 : bad |= safe_mem_is_zero(ntor_key_seed, DIGEST256_LEN);
101 :
102 : /* Let's build ntor_verify */
103 4 : crypto_mac_sha3_256(ntor_verify, sizeof(ntor_verify),
104 : rend_secret_hs_input, REND_SECRET_HS_INPUT_LEN,
105 : (const uint8_t *)T_HSVERIFY, strlen(T_HSVERIFY));
106 4 : bad |= safe_mem_is_zero(ntor_verify, DIGEST256_LEN);
107 :
108 : /* Let's build auth_input: */
109 4 : ptr = rend_auth_input;
110 : /* Append ntor_verify */
111 4 : APPEND(ptr, ntor_verify, sizeof(ntor_verify));
112 : /* Append AUTH_KEY */
113 4 : APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN);
114 : /* Append B */
115 4 : APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
116 : /* Append Y */
117 4 : APPEND(ptr,
118 : service_ephemeral_rend_pubkey->public_key, CURVE25519_PUBKEY_LEN);
119 : /* Append X */
120 4 : APPEND(ptr,
121 : client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
122 : /* Append PROTOID */
123 4 : APPEND(ptr, PROTOID, strlen(PROTOID));
124 : /* Append "Server" */
125 4 : APPEND(ptr, SERVER_STR, strlen(SERVER_STR));
126 4 : tor_assert(ptr == rend_auth_input + sizeof(rend_auth_input));
127 :
128 : /* Let's build auth_input_mac that goes in RENDEZVOUS1 cell */
129 4 : crypto_mac_sha3_256(rend_cell_auth, sizeof(rend_cell_auth),
130 : rend_auth_input, sizeof(rend_auth_input),
131 : (const uint8_t *)T_HSMAC, strlen(T_HSMAC));
132 4 : bad |= safe_mem_is_zero(ntor_verify, DIGEST256_LEN);
133 :
134 : { /* Get the computed RENDEZVOUS1 material! */
135 4 : memcpy(&hs_ntor_rend_cell_keys_out->rend_cell_auth_mac,
136 : rend_cell_auth, DIGEST256_LEN);
137 4 : memcpy(&hs_ntor_rend_cell_keys_out->ntor_key_seed,
138 : ntor_key_seed, DIGEST256_LEN);
139 : }
140 :
141 4 : memwipe(rend_cell_auth, 0, sizeof(rend_cell_auth));
142 4 : memwipe(rend_auth_input, 0, sizeof(rend_auth_input));
143 4 : memwipe(ntor_key_seed, 0, sizeof(ntor_key_seed));
144 :
145 4 : return bad;
146 : }
147 :
148 : /** Length of secret_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID */
149 : #define INTRO_SECRET_HS_INPUT_LEN (CURVE25519_OUTPUT_LEN +ED25519_PUBKEY_LEN +\
150 : CURVE25519_PUBKEY_LEN + CURVE25519_PUBKEY_LEN + PROTOID_LEN)
151 : /* Length of info = m_hsexpand | subcredential */
152 : #define INFO_BLOB_LEN (M_HSEXPAND_LEN + DIGEST256_LEN)
153 : /* Length of KDF input = intro_secret_hs_input | t_hsenc | info */
154 : #define KDF_INPUT_LEN (INTRO_SECRET_HS_INPUT_LEN + T_HSENC_LEN + INFO_BLOB_LEN)
155 :
156 : /** Helper function: Compute the part of the HS ntor handshake that generates
157 : * key material for creating and handling INTRODUCE1 cells. Function used
158 : * by both client and service. Specifically, calculate the following:
159 : *
160 : * info = m_hsexpand | subcredential
161 : * hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN)
162 : * ENC_KEY = hs_keys[0:S_KEY_LEN]
163 : * MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN]
164 : *
165 : * where intro_secret_hs_input is <b>secret_input</b> (of size
166 : * INTRO_SECRET_HS_INPUT_LEN), and <b>subcredential</b> is of size
167 : * DIGEST256_LEN.
168 : *
169 : * If everything went well, fill <b>hs_ntor_intro_cell_keys_out</b> with the
170 : * necessary key material, and return 0. */
171 : static void
172 33 : get_introduce1_key_material(const uint8_t *secret_input,
173 : const hs_subcredential_t *subcredential,
174 : hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
175 : {
176 33 : uint8_t keystream[CIPHER256_KEY_LEN + DIGEST256_LEN];
177 33 : uint8_t info_blob[INFO_BLOB_LEN];
178 33 : uint8_t kdf_input[KDF_INPUT_LEN];
179 33 : uint8_t *ptr;
180 :
181 : /* Let's build info */
182 33 : ptr = info_blob;
183 33 : APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND));
184 33 : APPEND(ptr, subcredential->subcred, SUBCRED_LEN);
185 33 : tor_assert(ptr == info_blob + sizeof(info_blob));
186 :
187 : /* Let's build the input to the KDF */
188 33 : ptr = kdf_input;
189 33 : APPEND(ptr, secret_input, INTRO_SECRET_HS_INPUT_LEN);
190 33 : APPEND(ptr, T_HSENC, strlen(T_HSENC));
191 33 : APPEND(ptr, info_blob, sizeof(info_blob));
192 33 : tor_assert(ptr == kdf_input + sizeof(kdf_input));
193 :
194 : /* Now we need to run kdf_input over SHAKE-256 */
195 33 : crypto_xof(keystream, sizeof(keystream),
196 : kdf_input, sizeof(kdf_input));
197 :
198 : { /* Get the keys */
199 33 : memcpy(&hs_ntor_intro_cell_keys_out->enc_key, keystream,CIPHER256_KEY_LEN);
200 33 : memcpy(&hs_ntor_intro_cell_keys_out->mac_key,
201 : keystream+CIPHER256_KEY_LEN, DIGEST256_LEN);
202 : }
203 :
204 33 : memwipe(keystream, 0, sizeof(keystream));
205 33 : memwipe(kdf_input, 0, sizeof(kdf_input));
206 33 : }
207 :
208 : /** Helper function: Calculate the 'intro_secret_hs_input' element used by the
209 : * HS ntor handshake and place it in <b>secret_input_out</b>. This function is
210 : * used by both client and service code.
211 : *
212 : * For the client-side it looks like this:
213 : *
214 : * intro_secret_hs_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID
215 : *
216 : * whereas for the service-side it looks like this:
217 : *
218 : * intro_secret_hs_input = EXP(X,b) | AUTH_KEY | X | B | PROTOID
219 : *
220 : * In this function, <b>dh_result</b> carries the EXP() result (and has size
221 : * CURVE25519_OUTPUT_LEN) <b>intro_auth_pubkey</b> is AUTH_KEY,
222 : * <b>client_ephemeral_enc_pubkey</b> is X, and <b>intro_enc_pubkey</b> is B.
223 : */
224 : static void
225 13 : get_intro_secret_hs_input(const uint8_t *dh_result,
226 : const ed25519_public_key_t *intro_auth_pubkey,
227 : const curve25519_public_key_t *client_ephemeral_enc_pubkey,
228 : const curve25519_public_key_t *intro_enc_pubkey,
229 : uint8_t *secret_input_out)
230 : {
231 13 : uint8_t *ptr;
232 :
233 : /* Append EXP() */
234 13 : ptr = secret_input_out;
235 13 : APPEND(ptr, dh_result, CURVE25519_OUTPUT_LEN);
236 : /* Append AUTH_KEY */
237 13 : APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN);
238 : /* Append X */
239 13 : APPEND(ptr, client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
240 : /* Append B */
241 13 : APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
242 : /* Append PROTOID */
243 13 : APPEND(ptr, PROTOID, strlen(PROTOID));
244 13 : tor_assert(ptr == secret_input_out + INTRO_SECRET_HS_INPUT_LEN);
245 13 : }
246 :
247 : /** Calculate the 'rend_secret_hs_input' element used by the HS ntor handshake
248 : * and place it in <b>rend_secret_hs_input_out</b>. This function is used by
249 : * both client and service code.
250 : *
251 : * The computation on the client side is:
252 : * rend_secret_hs_input = EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID
253 : * whereas on the service side it is:
254 : * rend_secret_hs_input = EXP(Y,x) | EXP(B,x) | AUTH_KEY | B | X | Y | PROTOID
255 : *
256 : * where:
257 : * <b>dh_result1</b> and <b>dh_result2</b> carry the two EXP() results (of size
258 : * CURVE25519_OUTPUT_LEN)
259 : * <b>intro_auth_pubkey</b> is AUTH_KEY,
260 : * <b>intro_enc_pubkey</b> is B,
261 : * <b>client_ephemeral_enc_pubkey</b> is X, and
262 : * <b>service_ephemeral_rend_pubkey</b> is Y.
263 : */
264 : static void
265 4 : get_rend_secret_hs_input(const uint8_t *dh_result1, const uint8_t *dh_result2,
266 : const ed25519_public_key_t *intro_auth_pubkey,
267 : const curve25519_public_key_t *intro_enc_pubkey,
268 : const curve25519_public_key_t *client_ephemeral_enc_pubkey,
269 : const curve25519_public_key_t *service_ephemeral_rend_pubkey,
270 : uint8_t *rend_secret_hs_input_out)
271 : {
272 4 : uint8_t *ptr;
273 :
274 4 : ptr = rend_secret_hs_input_out;
275 : /* Append the first EXP() */
276 4 : APPEND(ptr, dh_result1, CURVE25519_OUTPUT_LEN);
277 : /* Append the other EXP() */
278 4 : APPEND(ptr, dh_result2, CURVE25519_OUTPUT_LEN);
279 : /* Append AUTH_KEY */
280 4 : APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN);
281 : /* Append B */
282 4 : APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
283 : /* Append X */
284 4 : APPEND(ptr,
285 : client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
286 : /* Append Y */
287 4 : APPEND(ptr,
288 : service_ephemeral_rend_pubkey->public_key, CURVE25519_PUBKEY_LEN);
289 : /* Append PROTOID */
290 4 : APPEND(ptr, PROTOID, strlen(PROTOID));
291 4 : tor_assert(ptr == rend_secret_hs_input_out + REND_SECRET_HS_INPUT_LEN);
292 4 : }
293 :
294 : /************************* Public functions: *******************************/
295 :
296 : /* Public function: Do the appropriate ntor calculations and derive the keys
297 : * needed to encrypt and authenticate INTRODUCE1 cells. Return 0 and place the
298 : * final key material in <b>hs_ntor_intro_cell_keys_out</b> if everything went
299 : * well, otherwise return -1;
300 : *
301 : * The relevant calculations are as follows:
302 : *
303 : * intro_secret_hs_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID
304 : * info = m_hsexpand | subcredential
305 : * hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN)
306 : * ENC_KEY = hs_keys[0:S_KEY_LEN]
307 : * MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN]
308 : *
309 : * where:
310 : * <b>intro_auth_pubkey</b> is AUTH_KEY (found in HS descriptor),
311 : * <b>intro_enc_pubkey</b> is B (also found in HS descriptor),
312 : * <b>client_ephemeral_enc_keypair</b> is freshly generated keypair (x,X)
313 : * <b>subcredential</b> is the hidden service subcredential (of size
314 : * DIGEST256_LEN). */
315 : int
316 5 : hs_ntor_client_get_introduce1_keys(
317 : const ed25519_public_key_t *intro_auth_pubkey,
318 : const curve25519_public_key_t *intro_enc_pubkey,
319 : const curve25519_keypair_t *client_ephemeral_enc_keypair,
320 : const hs_subcredential_t *subcredential,
321 : hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
322 : {
323 5 : int bad = 0;
324 5 : uint8_t secret_input[INTRO_SECRET_HS_INPUT_LEN];
325 5 : uint8_t dh_result[CURVE25519_OUTPUT_LEN];
326 :
327 5 : tor_assert(intro_auth_pubkey);
328 5 : tor_assert(intro_enc_pubkey);
329 5 : tor_assert(client_ephemeral_enc_keypair);
330 5 : tor_assert(subcredential);
331 5 : tor_assert(hs_ntor_intro_cell_keys_out);
332 :
333 : /* Calculate EXP(B,x) */
334 5 : curve25519_handshake(dh_result,
335 : &client_ephemeral_enc_keypair->seckey,
336 : intro_enc_pubkey);
337 5 : bad |= safe_mem_is_zero(dh_result, CURVE25519_OUTPUT_LEN);
338 :
339 : /* Get intro_secret_hs_input */
340 5 : get_intro_secret_hs_input(dh_result, intro_auth_pubkey,
341 : &client_ephemeral_enc_keypair->pubkey,
342 : intro_enc_pubkey, secret_input);
343 5 : bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN);
344 :
345 : /* Get ENC_KEY and MAC_KEY! */
346 5 : get_introduce1_key_material(secret_input, subcredential,
347 : hs_ntor_intro_cell_keys_out);
348 :
349 : /* Cleanup */
350 5 : memwipe(secret_input, 0, sizeof(secret_input));
351 5 : if (bad) {
352 0 : memwipe(hs_ntor_intro_cell_keys_out, 0, sizeof(hs_ntor_intro_cell_keys_t));
353 : }
354 :
355 5 : return bad ? -1 : 0;
356 : }
357 :
358 : /* Public function: Do the appropriate ntor calculations and derive the keys
359 : * needed to verify RENDEZVOUS1 cells and encrypt further rendezvous
360 : * traffic. Return 0 and place the final key material in
361 : * <b>hs_ntor_rend_cell_keys_out</b> if everything went well, else return -1.
362 : *
363 : * The relevant calculations are as follows:
364 : *
365 : * rend_secret_hs_input = EXP(Y,x) | EXP(B,x) | AUTH_KEY | B | X | Y | PROTOID
366 : * NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc)
367 : * verify = MAC(rend_secret_hs_input, t_hsverify)
368 : * auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server"
369 : * auth_input_mac = MAC(auth_input, t_hsmac)
370 : *
371 : * where:
372 : * <b>intro_auth_pubkey</b> is AUTH_KEY (found in HS descriptor),
373 : * <b>client_ephemeral_enc_keypair</b> is freshly generated keypair (x,X)
374 : * <b>intro_enc_pubkey</b> is B (also found in HS descriptor),
375 : * <b>service_ephemeral_rend_pubkey</b> is Y (SERVER_PK in RENDEZVOUS1 cell) */
376 : int
377 2 : hs_ntor_client_get_rendezvous1_keys(
378 : const ed25519_public_key_t *intro_auth_pubkey,
379 : const curve25519_keypair_t *client_ephemeral_enc_keypair,
380 : const curve25519_public_key_t *intro_enc_pubkey,
381 : const curve25519_public_key_t *service_ephemeral_rend_pubkey,
382 : hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out)
383 : {
384 2 : int bad = 0;
385 2 : uint8_t rend_secret_hs_input[REND_SECRET_HS_INPUT_LEN];
386 2 : uint8_t dh_result1[CURVE25519_OUTPUT_LEN];
387 2 : uint8_t dh_result2[CURVE25519_OUTPUT_LEN];
388 :
389 2 : tor_assert(intro_auth_pubkey);
390 2 : tor_assert(client_ephemeral_enc_keypair);
391 2 : tor_assert(intro_enc_pubkey);
392 2 : tor_assert(service_ephemeral_rend_pubkey);
393 2 : tor_assert(hs_ntor_rend_cell_keys_out);
394 :
395 : /* Compute EXP(Y, x) */
396 2 : curve25519_handshake(dh_result1,
397 : &client_ephemeral_enc_keypair->seckey,
398 : service_ephemeral_rend_pubkey);
399 2 : bad |= safe_mem_is_zero(dh_result1, CURVE25519_OUTPUT_LEN);
400 :
401 : /* Compute EXP(B, x) */
402 2 : curve25519_handshake(dh_result2,
403 : &client_ephemeral_enc_keypair->seckey,
404 : intro_enc_pubkey);
405 2 : bad |= safe_mem_is_zero(dh_result2, CURVE25519_OUTPUT_LEN);
406 :
407 : /* Get rend_secret_hs_input */
408 2 : get_rend_secret_hs_input(dh_result1, dh_result2,
409 : intro_auth_pubkey, intro_enc_pubkey,
410 : &client_ephemeral_enc_keypair->pubkey,
411 : service_ephemeral_rend_pubkey,
412 : rend_secret_hs_input);
413 :
414 : /* Get NTOR_KEY_SEED and the auth_input MAC */
415 2 : bad |= get_rendezvous1_key_material(rend_secret_hs_input,
416 : intro_auth_pubkey,
417 : intro_enc_pubkey,
418 : service_ephemeral_rend_pubkey,
419 : &client_ephemeral_enc_keypair->pubkey,
420 : hs_ntor_rend_cell_keys_out);
421 :
422 2 : memwipe(rend_secret_hs_input, 0, sizeof(rend_secret_hs_input));
423 2 : if (bad) {
424 0 : memwipe(hs_ntor_rend_cell_keys_out, 0, sizeof(hs_ntor_rend_cell_keys_t));
425 : }
426 :
427 2 : return bad ? -1 : 0;
428 : }
429 :
430 : /* Public function: Do the appropriate ntor calculations and derive the keys
431 : * needed to decrypt and verify INTRODUCE1 cells. Return 0 and place the final
432 : * key material in <b>hs_ntor_intro_cell_keys_out</b> if everything went well,
433 : * otherwise return -1;
434 : *
435 : * The relevant calculations are as follows:
436 : *
437 : * intro_secret_hs_input = EXP(X,b) | AUTH_KEY | X | B | PROTOID
438 : * info = m_hsexpand | subcredential
439 : * hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN)
440 : * HS_DEC_KEY = hs_keys[0:S_KEY_LEN]
441 : * HS_MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN]
442 : *
443 : * where:
444 : * <b>intro_auth_pubkey</b> is AUTH_KEY (introduction point auth key),
445 : * <b>intro_enc_keypair</b> is (b,B) (introduction point encryption keypair),
446 : * <b>client_ephemeral_enc_pubkey</b> is X (CLIENT_PK in INTRODUCE2 cell),
447 : * <b>subcredential</b> is the HS subcredential (of size DIGEST256_LEN) */
448 : int
449 1 : hs_ntor_service_get_introduce1_keys(
450 : const ed25519_public_key_t *intro_auth_pubkey,
451 : const curve25519_keypair_t *intro_enc_keypair,
452 : const curve25519_public_key_t *client_ephemeral_enc_pubkey,
453 : const hs_subcredential_t *subcredential,
454 : hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
455 : {
456 1 : return hs_ntor_service_get_introduce1_keys_multi(
457 : intro_auth_pubkey,
458 : intro_enc_keypair,
459 : client_ephemeral_enc_pubkey,
460 : 1,
461 : subcredential,
462 : hs_ntor_intro_cell_keys_out);
463 : }
464 :
465 : /**
466 : * As hs_ntor_service_get_introduce1_keys(), but take multiple subcredentials
467 : * as input, and yield multiple sets of keys as output.
468 : **/
469 : int
470 8 : hs_ntor_service_get_introduce1_keys_multi(
471 : const struct ed25519_public_key_t *intro_auth_pubkey,
472 : const struct curve25519_keypair_t *intro_enc_keypair,
473 : const struct curve25519_public_key_t *client_ephemeral_enc_pubkey,
474 : size_t n_subcredentials,
475 : const hs_subcredential_t *subcredentials,
476 : hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
477 : {
478 8 : int bad = 0;
479 8 : uint8_t secret_input[INTRO_SECRET_HS_INPUT_LEN];
480 8 : uint8_t dh_result[CURVE25519_OUTPUT_LEN];
481 :
482 8 : tor_assert(intro_auth_pubkey);
483 8 : tor_assert(intro_enc_keypair);
484 8 : tor_assert(client_ephemeral_enc_pubkey);
485 8 : tor_assert(n_subcredentials >= 1);
486 8 : tor_assert(subcredentials);
487 8 : tor_assert(hs_ntor_intro_cell_keys_out);
488 :
489 : /* Compute EXP(X, b) */
490 8 : curve25519_handshake(dh_result,
491 : &intro_enc_keypair->seckey,
492 : client_ephemeral_enc_pubkey);
493 8 : bad |= safe_mem_is_zero(dh_result, CURVE25519_OUTPUT_LEN);
494 :
495 : /* Get intro_secret_hs_input */
496 8 : get_intro_secret_hs_input(dh_result, intro_auth_pubkey,
497 : client_ephemeral_enc_pubkey,
498 : &intro_enc_keypair->pubkey,
499 : secret_input);
500 8 : bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN);
501 :
502 36 : for (unsigned i = 0; i < n_subcredentials; ++i) {
503 : /* Get ENC_KEY and MAC_KEY! */
504 28 : get_introduce1_key_material(secret_input, &subcredentials[i],
505 28 : &hs_ntor_intro_cell_keys_out[i]);
506 : }
507 :
508 8 : memwipe(secret_input, 0, sizeof(secret_input));
509 8 : if (bad) {
510 1 : memwipe(hs_ntor_intro_cell_keys_out, 0,
511 : sizeof(hs_ntor_intro_cell_keys_t) * n_subcredentials);
512 : }
513 :
514 8 : return bad ? -1 : 0;
515 : }
516 :
517 : /* Public function: Do the appropriate ntor calculations and derive the keys
518 : * needed to create and authenticate RENDEZVOUS1 cells. Return 0 and place the
519 : * final key material in <b>hs_ntor_rend_cell_keys_out</b> if all went fine,
520 : * return -1 if error happened.
521 : *
522 : * The relevant calculations are as follows:
523 : *
524 : * rend_secret_hs_input = EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID
525 : * NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc)
526 : * verify = MAC(rend_secret_hs_input, t_hsverify)
527 : * auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server"
528 : * auth_input_mac = MAC(auth_input, t_hsmac)
529 : *
530 : * where:
531 : * <b>intro_auth_pubkey</b> is AUTH_KEY (intro point auth key),
532 : * <b>intro_enc_keypair</b> is (b,B) (intro point enc keypair)
533 : * <b>service_ephemeral_rend_keypair</b> is a fresh (y,Y) keypair
534 : * <b>client_ephemeral_enc_pubkey</b> is X (CLIENT_PK in INTRODUCE2 cell) */
535 : int
536 2 : hs_ntor_service_get_rendezvous1_keys(
537 : const ed25519_public_key_t *intro_auth_pubkey,
538 : const curve25519_keypair_t *intro_enc_keypair,
539 : const curve25519_keypair_t *service_ephemeral_rend_keypair,
540 : const curve25519_public_key_t *client_ephemeral_enc_pubkey,
541 : hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out)
542 : {
543 2 : int bad = 0;
544 2 : uint8_t rend_secret_hs_input[REND_SECRET_HS_INPUT_LEN];
545 2 : uint8_t dh_result1[CURVE25519_OUTPUT_LEN];
546 2 : uint8_t dh_result2[CURVE25519_OUTPUT_LEN];
547 :
548 2 : tor_assert(intro_auth_pubkey);
549 2 : tor_assert(intro_enc_keypair);
550 2 : tor_assert(service_ephemeral_rend_keypair);
551 2 : tor_assert(client_ephemeral_enc_pubkey);
552 2 : tor_assert(hs_ntor_rend_cell_keys_out);
553 :
554 : /* Compute EXP(X, y) */
555 2 : curve25519_handshake(dh_result1,
556 : &service_ephemeral_rend_keypair->seckey,
557 : client_ephemeral_enc_pubkey);
558 2 : bad |= safe_mem_is_zero(dh_result1, CURVE25519_OUTPUT_LEN);
559 :
560 : /* Compute EXP(X, b) */
561 2 : curve25519_handshake(dh_result2,
562 : &intro_enc_keypair->seckey,
563 : client_ephemeral_enc_pubkey);
564 2 : bad |= safe_mem_is_zero(dh_result2, CURVE25519_OUTPUT_LEN);
565 :
566 : /* Get rend_secret_hs_input */
567 2 : get_rend_secret_hs_input(dh_result1, dh_result2,
568 : intro_auth_pubkey,
569 : &intro_enc_keypair->pubkey,
570 : client_ephemeral_enc_pubkey,
571 : &service_ephemeral_rend_keypair->pubkey,
572 : rend_secret_hs_input);
573 :
574 : /* Get NTOR_KEY_SEED and AUTH_INPUT_MAC! */
575 2 : bad |= get_rendezvous1_key_material(rend_secret_hs_input,
576 : intro_auth_pubkey,
577 : &intro_enc_keypair->pubkey,
578 : &service_ephemeral_rend_keypair->pubkey,
579 : client_ephemeral_enc_pubkey,
580 : hs_ntor_rend_cell_keys_out);
581 :
582 2 : memwipe(rend_secret_hs_input, 0, sizeof(rend_secret_hs_input));
583 2 : if (bad) {
584 0 : memwipe(hs_ntor_rend_cell_keys_out, 0, sizeof(hs_ntor_rend_cell_keys_t));
585 : }
586 :
587 2 : return bad ? -1 : 0;
588 : }
589 :
590 : /** Given a received RENDEZVOUS2 MAC in <b>mac</b> (of length DIGEST256_LEN),
591 : * and the RENDEZVOUS1 key material in <b>hs_ntor_rend_cell_keys</b>, return 1
592 : * if the MAC is good, otherwise return 0. */
593 : int
594 1 : hs_ntor_client_rendezvous2_mac_is_good(
595 : const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys,
596 : const uint8_t *rcvd_mac)
597 : {
598 1 : tor_assert(rcvd_mac);
599 1 : tor_assert(hs_ntor_rend_cell_keys);
600 :
601 1 : return tor_memeq(hs_ntor_rend_cell_keys->rend_cell_auth_mac,
602 : rcvd_mac, DIGEST256_LEN);
603 : }
604 :
605 : /* Input length to KDF for key expansion */
606 : #define NTOR_KEY_EXPANSION_KDF_INPUT_LEN (DIGEST256_LEN + M_HSEXPAND_LEN)
607 :
608 : /** Given the rendezvous key seed in <b>ntor_key_seed</b> (of size
609 : * DIGEST256_LEN), do the circuit key expansion as specified by section
610 : * '4.2.1. Key expansion' and place the keys in <b>keys_out</b> (which must be
611 : * of size HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN).
612 : *
613 : * Return 0 if things went well, else return -1. */
614 : int
615 5 : hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, size_t seed_len,
616 : uint8_t *keys_out, size_t keys_out_len)
617 : {
618 5 : uint8_t *ptr;
619 5 : uint8_t kdf_input[NTOR_KEY_EXPANSION_KDF_INPUT_LEN];
620 :
621 : /* Sanity checks on lengths to make sure we are good */
622 5 : if (BUG(seed_len != DIGEST256_LEN)) {
623 0 : return -1;
624 : }
625 5 : if (BUG(keys_out_len != HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN)) {
626 0 : return -1;
627 : }
628 :
629 : /* Let's build the input to the KDF */
630 5 : ptr = kdf_input;
631 5 : APPEND(ptr, ntor_key_seed, DIGEST256_LEN);
632 5 : APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND));
633 5 : tor_assert(ptr == kdf_input + sizeof(kdf_input));
634 :
635 : /* Generate the keys */
636 5 : crypto_xof(keys_out, HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN,
637 : kdf_input, sizeof(kdf_input));
638 :
639 5 : return 0;
640 : }
|