Cache control file
[tor/appveyor.git] / src / or / hs_ntor.c
bloba416bc46c3ad8cac003acdf2ddb1afc6e6995c99
1 /* Copyright (c) 2017, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 /** \file hs_ntor.c
5 * \brief Implements the ntor variant used in Tor hidden services.
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 .
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.
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().
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.
27 #include "or.h"
28 #include "hs_ntor.h"
30 /* String constants used by the ntor HS protocol */
31 #define PROTOID "tor-hs-ntor-curve25519-sha3-256-1"
32 #define PROTOID_LEN (sizeof(PROTOID) - 1)
33 #define SERVER_STR "Server"
34 #define SERVER_STR_LEN (sizeof(SERVER_STR) - 1)
36 /* Protocol-specific tweaks to our crypto inputs */
37 #define T_HSENC PROTOID ":hs_key_extract"
38 #define T_HSENC_LEN (sizeof(T_HSENC) - 1)
39 #define T_HSVERIFY PROTOID ":hs_verify"
40 #define T_HSMAC PROTOID ":hs_mac"
41 #define M_HSEXPAND PROTOID ":hs_key_expand"
42 #define M_HSEXPAND_LEN (sizeof(M_HSEXPAND) - 1)
44 /************************* Helper functions: *******************************/
46 /** Helper macro: copy <b>len</b> bytes from <b>inp</b> to <b>ptr</b> and
47 *advance <b>ptr</b> by the number of bytes copied. Stolen from onion_ntor.c */
48 #define APPEND(ptr, inp, len) \
49 STMT_BEGIN { \
50 memcpy(ptr, (inp), (len)); \
51 ptr += len; \
52 } STMT_END
54 /* Length of EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID */
55 #define REND_SECRET_HS_INPUT_LEN (CURVE25519_OUTPUT_LEN * 2 + \
56 ED25519_PUBKEY_LEN + CURVE25519_PUBKEY_LEN * 3 + PROTOID_LEN)
57 /* Length of auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" */
58 #define REND_AUTH_INPUT_LEN (DIGEST256_LEN + ED25519_PUBKEY_LEN + \
59 CURVE25519_PUBKEY_LEN * 3 + PROTOID_LEN + SERVER_STR_LEN)
61 /** Helper function: Compute the last part of the HS ntor handshake which
62 * derives key material necessary to create and handle RENDEZVOUS1
63 * cells. Function used by both client and service. The actual calculations is
64 * as follows:
66 * NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc)
67 * verify = MAC(rend_secret_hs_input, t_hsverify)
68 * auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server"
69 * auth_input_mac = MAC(auth_input, t_hsmac)
71 * where in the above, AUTH_KEY is <b>intro_auth_pubkey</b>, B is
72 * <b>intro_enc_pubkey</b>, Y is <b>service_ephemeral_rend_pubkey</b>, and X
73 * is <b>client_ephemeral_enc_pubkey</b>. The provided
74 * <b>rend_secret_hs_input</b> is of size REND_SECRET_HS_INPUT_LEN.
76 * The final results of NTOR_KEY_SEED and auth_input_mac are placed in
77 * <b>hs_ntor_rend_cell_keys_out</b>. Return 0 if everything went fine. */
78 static int
79 get_rendezvous1_key_material(const uint8_t *rend_secret_hs_input,
80 const ed25519_public_key_t *intro_auth_pubkey,
81 const curve25519_public_key_t *intro_enc_pubkey,
82 const curve25519_public_key_t *service_ephemeral_rend_pubkey,
83 const curve25519_public_key_t *client_ephemeral_enc_pubkey,
84 hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out)
86 int bad = 0;
87 uint8_t ntor_key_seed[DIGEST256_LEN];
88 uint8_t ntor_verify[DIGEST256_LEN];
89 uint8_t rend_auth_input[REND_AUTH_INPUT_LEN];
90 uint8_t rend_cell_auth[DIGEST256_LEN];
91 uint8_t *ptr;
93 /* Let's build NTOR_KEY_SEED */
94 crypto_mac_sha3_256(ntor_key_seed, sizeof(ntor_key_seed),
95 rend_secret_hs_input, REND_SECRET_HS_INPUT_LEN,
96 (const uint8_t *)T_HSENC, strlen(T_HSENC));
97 bad |= safe_mem_is_zero(ntor_key_seed, DIGEST256_LEN);
99 /* Let's build ntor_verify */
100 crypto_mac_sha3_256(ntor_verify, sizeof(ntor_verify),
101 rend_secret_hs_input, REND_SECRET_HS_INPUT_LEN,
102 (const uint8_t *)T_HSVERIFY, strlen(T_HSVERIFY));
103 bad |= safe_mem_is_zero(ntor_verify, DIGEST256_LEN);
105 /* Let's build auth_input: */
106 ptr = rend_auth_input;
107 /* Append ntor_verify */
108 APPEND(ptr, ntor_verify, sizeof(ntor_verify));
109 /* Append AUTH_KEY */
110 APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN);
111 /* Append B */
112 APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
113 /* Append Y */
114 APPEND(ptr,
115 service_ephemeral_rend_pubkey->public_key, CURVE25519_PUBKEY_LEN);
116 /* Append X */
117 APPEND(ptr,
118 client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
119 /* Append PROTOID */
120 APPEND(ptr, PROTOID, strlen(PROTOID));
121 /* Append "Server" */
122 APPEND(ptr, SERVER_STR, strlen(SERVER_STR));
123 tor_assert(ptr == rend_auth_input + sizeof(rend_auth_input));
125 /* Let's build auth_input_mac that goes in RENDEZVOUS1 cell */
126 crypto_mac_sha3_256(rend_cell_auth, sizeof(rend_cell_auth),
127 rend_auth_input, sizeof(rend_auth_input),
128 (const uint8_t *)T_HSMAC, strlen(T_HSMAC));
129 bad |= safe_mem_is_zero(ntor_verify, DIGEST256_LEN);
131 { /* Get the computed RENDEZVOUS1 material! */
132 memcpy(&hs_ntor_rend_cell_keys_out->rend_cell_auth_mac,
133 rend_cell_auth, DIGEST256_LEN);
134 memcpy(&hs_ntor_rend_cell_keys_out->ntor_key_seed,
135 ntor_key_seed, DIGEST256_LEN);
138 memwipe(rend_cell_auth, 0, sizeof(rend_cell_auth));
139 memwipe(rend_auth_input, 0, sizeof(rend_auth_input));
140 memwipe(ntor_key_seed, 0, sizeof(ntor_key_seed));
142 return bad;
145 /** Length of secret_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID */
146 #define INTRO_SECRET_HS_INPUT_LEN (CURVE25519_OUTPUT_LEN +ED25519_PUBKEY_LEN +\
147 CURVE25519_PUBKEY_LEN + CURVE25519_PUBKEY_LEN + PROTOID_LEN)
148 /* Length of info = m_hsexpand | subcredential */
149 #define INFO_BLOB_LEN (M_HSEXPAND_LEN + DIGEST256_LEN)
150 /* Length of KDF input = intro_secret_hs_input | t_hsenc | info */
151 #define KDF_INPUT_LEN (INTRO_SECRET_HS_INPUT_LEN + T_HSENC_LEN + INFO_BLOB_LEN)
153 /** Helper function: Compute the part of the HS ntor handshake that generates
154 * key material for creating and handling INTRODUCE1 cells. Function used
155 * by both client and service. Specifically, calculate the following:
157 * info = m_hsexpand | subcredential
158 * hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN)
159 * ENC_KEY = hs_keys[0:S_KEY_LEN]
160 * MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN]
162 * where intro_secret_hs_input is <b>secret_input</b> (of size
163 * INTRO_SECRET_HS_INPUT_LEN), and <b>subcredential</b> is of size
164 * DIGEST256_LEN.
166 * If everything went well, fill <b>hs_ntor_intro_cell_keys_out</b> with the
167 * necessary key material, and return 0. */
168 static void
169 get_introduce1_key_material(const uint8_t *secret_input,
170 const uint8_t *subcredential,
171 hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
173 uint8_t keystream[CIPHER256_KEY_LEN + DIGEST256_LEN];
174 uint8_t info_blob[INFO_BLOB_LEN];
175 uint8_t kdf_input[KDF_INPUT_LEN];
176 crypto_xof_t *xof;
177 uint8_t *ptr;
179 /* Let's build info */
180 ptr = info_blob;
181 APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND));
182 APPEND(ptr, subcredential, DIGEST256_LEN);
183 tor_assert(ptr == info_blob + sizeof(info_blob));
185 /* Let's build the input to the KDF */
186 ptr = kdf_input;
187 APPEND(ptr, secret_input, INTRO_SECRET_HS_INPUT_LEN);
188 APPEND(ptr, T_HSENC, strlen(T_HSENC));
189 APPEND(ptr, info_blob, sizeof(info_blob));
190 tor_assert(ptr == kdf_input + sizeof(kdf_input));
192 /* Now we need to run kdf_input over SHAKE-256 */
193 xof = crypto_xof_new();
194 crypto_xof_add_bytes(xof, kdf_input, sizeof(kdf_input));
195 crypto_xof_squeeze_bytes(xof, keystream, sizeof(keystream)) ;
196 crypto_xof_free(xof);
198 { /* Get the keys */
199 memcpy(&hs_ntor_intro_cell_keys_out->enc_key, keystream,CIPHER256_KEY_LEN);
200 memcpy(&hs_ntor_intro_cell_keys_out->mac_key,
201 keystream+CIPHER256_KEY_LEN, DIGEST256_LEN);
204 memwipe(keystream, 0, sizeof(keystream));
205 memwipe(kdf_input, 0, sizeof(kdf_input));
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.
212 * For the client-side it looks like this:
214 * intro_secret_hs_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID
216 * whereas for the service-side it looks like this:
218 * intro_secret_hs_input = EXP(X,b) | AUTH_KEY | X | B | PROTOID
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.
224 static void
225 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)
231 uint8_t *ptr;
233 /* Append EXP() */
234 ptr = secret_input_out;
235 APPEND(ptr, dh_result, CURVE25519_OUTPUT_LEN);
236 /* Append AUTH_KEY */
237 APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN);
238 /* Append X */
239 APPEND(ptr, client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
240 /* Append B */
241 APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
242 /* Append PROTOID */
243 APPEND(ptr, PROTOID, strlen(PROTOID));
244 tor_assert(ptr == secret_input_out + INTRO_SECRET_HS_INPUT_LEN);
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.
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
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.
264 static void
265 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)
272 uint8_t *ptr;
274 ptr = rend_secret_hs_input_out;
275 /* Append the first EXP() */
276 APPEND(ptr, dh_result1, CURVE25519_OUTPUT_LEN);
277 /* Append the other EXP() */
278 APPEND(ptr, dh_result2, CURVE25519_OUTPUT_LEN);
279 /* Append AUTH_KEY */
280 APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN);
281 /* Append B */
282 APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
283 /* Append X */
284 APPEND(ptr,
285 client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
286 /* Append Y */
287 APPEND(ptr,
288 service_ephemeral_rend_pubkey->public_key, CURVE25519_PUBKEY_LEN);
289 /* Append PROTOID */
290 APPEND(ptr, PROTOID, strlen(PROTOID));
291 tor_assert(ptr == rend_secret_hs_input_out + REND_SECRET_HS_INPUT_LEN);
294 /************************* Public functions: *******************************/
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;
301 * The relevant calculations are as follows:
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]
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). */
316 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 uint8_t *subcredential,
321 hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
323 int bad = 0;
324 uint8_t secret_input[INTRO_SECRET_HS_INPUT_LEN];
325 uint8_t dh_result[CURVE25519_OUTPUT_LEN];
327 tor_assert(intro_auth_pubkey);
328 tor_assert(intro_enc_pubkey);
329 tor_assert(client_ephemeral_enc_keypair);
330 tor_assert(subcredential);
331 tor_assert(hs_ntor_intro_cell_keys_out);
333 /* Calculate EXP(B,x) */
334 curve25519_handshake(dh_result,
335 &client_ephemeral_enc_keypair->seckey,
336 intro_enc_pubkey);
337 bad |= safe_mem_is_zero(dh_result, CURVE25519_OUTPUT_LEN);
339 /* Get intro_secret_hs_input */
340 get_intro_secret_hs_input(dh_result, intro_auth_pubkey,
341 &client_ephemeral_enc_keypair->pubkey,
342 intro_enc_pubkey, secret_input);
343 bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN);
345 /* Get ENC_KEY and MAC_KEY! */
346 get_introduce1_key_material(secret_input, subcredential,
347 hs_ntor_intro_cell_keys_out);
349 /* Cleanup */
350 memwipe(secret_input, 0, sizeof(secret_input));
351 if (bad) {
352 memwipe(hs_ntor_intro_cell_keys_out, 0, sizeof(hs_ntor_intro_cell_keys_t));
355 return bad ? -1 : 0;
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.
363 * The relevant calculations are as follows:
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)
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) */
377 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)
384 int bad = 0;
385 uint8_t rend_secret_hs_input[REND_SECRET_HS_INPUT_LEN];
386 uint8_t dh_result1[CURVE25519_OUTPUT_LEN];
387 uint8_t dh_result2[CURVE25519_OUTPUT_LEN];
389 tor_assert(intro_auth_pubkey);
390 tor_assert(client_ephemeral_enc_keypair);
391 tor_assert(intro_enc_pubkey);
392 tor_assert(service_ephemeral_rend_pubkey);
393 tor_assert(hs_ntor_rend_cell_keys_out);
395 /* Compute EXP(Y, x) */
396 curve25519_handshake(dh_result1,
397 &client_ephemeral_enc_keypair->seckey,
398 service_ephemeral_rend_pubkey);
399 bad |= safe_mem_is_zero(dh_result1, CURVE25519_OUTPUT_LEN);
401 /* Compute EXP(B, x) */
402 curve25519_handshake(dh_result2,
403 &client_ephemeral_enc_keypair->seckey,
404 intro_enc_pubkey);
405 bad |= safe_mem_is_zero(dh_result2, CURVE25519_OUTPUT_LEN);
407 /* Get rend_secret_hs_input */
408 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);
414 /* Get NTOR_KEY_SEED and the auth_input MAC */
415 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);
422 memwipe(rend_secret_hs_input, 0, sizeof(rend_secret_hs_input));
423 if (bad) {
424 memwipe(hs_ntor_rend_cell_keys_out, 0, sizeof(hs_ntor_rend_cell_keys_t));
427 return bad ? -1 : 0;
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;
435 * The relevant calculations are as follows:
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]
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) */
449 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 uint8_t *subcredential,
454 hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
456 int bad = 0;
457 uint8_t secret_input[INTRO_SECRET_HS_INPUT_LEN];
458 uint8_t dh_result[CURVE25519_OUTPUT_LEN];
460 tor_assert(intro_auth_pubkey);
461 tor_assert(intro_enc_keypair);
462 tor_assert(client_ephemeral_enc_pubkey);
463 tor_assert(subcredential);
464 tor_assert(hs_ntor_intro_cell_keys_out);
466 /* Compute EXP(X, b) */
467 curve25519_handshake(dh_result,
468 &intro_enc_keypair->seckey,
469 client_ephemeral_enc_pubkey);
470 bad |= safe_mem_is_zero(dh_result, CURVE25519_OUTPUT_LEN);
472 /* Get intro_secret_hs_input */
473 get_intro_secret_hs_input(dh_result, intro_auth_pubkey,
474 client_ephemeral_enc_pubkey,
475 &intro_enc_keypair->pubkey,
476 secret_input);
477 bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN);
479 /* Get ENC_KEY and MAC_KEY! */
480 get_introduce1_key_material(secret_input, subcredential,
481 hs_ntor_intro_cell_keys_out);
483 memwipe(secret_input, 0, sizeof(secret_input));
484 if (bad) {
485 memwipe(hs_ntor_intro_cell_keys_out, 0, sizeof(hs_ntor_intro_cell_keys_t));
488 return bad ? -1 : 0;
491 /* Public function: Do the appropriate ntor calculations and derive the keys
492 * needed to create and authenticate RENDEZVOUS1 cells. Return 0 and place the
493 * final key material in <b>hs_ntor_rend_cell_keys_out</b> if all went fine,
494 * return -1 if error happened.
496 * The relevant calculations are as follows:
498 * rend_secret_hs_input = EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID
499 * NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc)
500 * verify = MAC(rend_secret_hs_input, t_hsverify)
501 * auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server"
502 * auth_input_mac = MAC(auth_input, t_hsmac)
504 * where:
505 * <b>intro_auth_pubkey</b> is AUTH_KEY (intro point auth key),
506 * <b>intro_enc_keypair</b> is (b,B) (intro point enc keypair)
507 * <b>service_ephemeral_rend_keypair</b> is a fresh (y,Y) keypair
508 * <b>client_ephemeral_enc_pubkey</b> is X (CLIENT_PK in INTRODUCE2 cell) */
510 hs_ntor_service_get_rendezvous1_keys(
511 const ed25519_public_key_t *intro_auth_pubkey,
512 const curve25519_keypair_t *intro_enc_keypair,
513 const curve25519_keypair_t *service_ephemeral_rend_keypair,
514 const curve25519_public_key_t *client_ephemeral_enc_pubkey,
515 hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out)
517 int bad = 0;
518 uint8_t rend_secret_hs_input[REND_SECRET_HS_INPUT_LEN];
519 uint8_t dh_result1[CURVE25519_OUTPUT_LEN];
520 uint8_t dh_result2[CURVE25519_OUTPUT_LEN];
522 tor_assert(intro_auth_pubkey);
523 tor_assert(intro_enc_keypair);
524 tor_assert(service_ephemeral_rend_keypair);
525 tor_assert(client_ephemeral_enc_pubkey);
526 tor_assert(hs_ntor_rend_cell_keys_out);
528 /* Compute EXP(X, y) */
529 curve25519_handshake(dh_result1,
530 &service_ephemeral_rend_keypair->seckey,
531 client_ephemeral_enc_pubkey);
532 bad |= safe_mem_is_zero(dh_result1, CURVE25519_OUTPUT_LEN);
534 /* Compute EXP(X, b) */
535 curve25519_handshake(dh_result2,
536 &intro_enc_keypair->seckey,
537 client_ephemeral_enc_pubkey);
538 bad |= safe_mem_is_zero(dh_result2, CURVE25519_OUTPUT_LEN);
540 /* Get rend_secret_hs_input */
541 get_rend_secret_hs_input(dh_result1, dh_result2,
542 intro_auth_pubkey,
543 &intro_enc_keypair->pubkey,
544 client_ephemeral_enc_pubkey,
545 &service_ephemeral_rend_keypair->pubkey,
546 rend_secret_hs_input);
548 /* Get NTOR_KEY_SEED and AUTH_INPUT_MAC! */
549 bad |= get_rendezvous1_key_material(rend_secret_hs_input,
550 intro_auth_pubkey,
551 &intro_enc_keypair->pubkey,
552 &service_ephemeral_rend_keypair->pubkey,
553 client_ephemeral_enc_pubkey,
554 hs_ntor_rend_cell_keys_out);
556 memwipe(rend_secret_hs_input, 0, sizeof(rend_secret_hs_input));
557 if (bad) {
558 memwipe(hs_ntor_rend_cell_keys_out, 0, sizeof(hs_ntor_rend_cell_keys_t));
561 return bad ? -1 : 0;
564 /** Given a received RENDEZVOUS2 MAC in <b>mac</b> (of length DIGEST256_LEN),
565 * and the RENDEZVOUS1 key material in <b>hs_ntor_rend_cell_keys</b>, return 1
566 * if the MAC is good, otherwise return 0. */
568 hs_ntor_client_rendezvous2_mac_is_good(
569 const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys,
570 const uint8_t *rcvd_mac)
572 tor_assert(rcvd_mac);
573 tor_assert(hs_ntor_rend_cell_keys);
575 return tor_memeq(hs_ntor_rend_cell_keys->rend_cell_auth_mac,
576 rcvd_mac, DIGEST256_LEN);
579 /* Input length to KDF for key expansion */
580 #define NTOR_KEY_EXPANSION_KDF_INPUT_LEN (DIGEST256_LEN + M_HSEXPAND_LEN)
582 /** Given the rendezvous key seed in <b>ntor_key_seed</b> (of size
583 * DIGEST256_LEN), do the circuit key expansion as specified by section
584 * '4.2.1. Key expansion' and place the keys in <b>keys_out</b> (which must be
585 * of size HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN).
587 * Return 0 if things went well, else return -1. */
589 hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, size_t seed_len,
590 uint8_t *keys_out, size_t keys_out_len)
592 uint8_t *ptr;
593 uint8_t kdf_input[NTOR_KEY_EXPANSION_KDF_INPUT_LEN];
594 crypto_xof_t *xof;
596 /* Sanity checks on lengths to make sure we are good */
597 if (BUG(seed_len != DIGEST256_LEN)) {
598 return -1;
600 if (BUG(keys_out_len != HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN)) {
601 return -1;
604 /* Let's build the input to the KDF */
605 ptr = kdf_input;
606 APPEND(ptr, ntor_key_seed, DIGEST256_LEN);
607 APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND));
608 tor_assert(ptr == kdf_input + sizeof(kdf_input));
610 /* Generate the keys */
611 xof = crypto_xof_new();
612 crypto_xof_add_bytes(xof, kdf_input, sizeof(kdf_input));
613 crypto_xof_squeeze_bytes(xof, keys_out, HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN);
614 crypto_xof_free(xof);
616 return 0;