Tor 0.4.9.8
Loading...
Searching...
No Matches
hs_ntor.c
Go to the documentation of this file.
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"
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. */
81static int
82get_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 int bad = 0;
90 uint8_t ntor_key_seed[DIGEST256_LEN];
91 uint8_t ntor_verify[DIGEST256_LEN];
92 uint8_t rend_auth_input[REND_AUTH_INPUT_LEN];
93 uint8_t rend_cell_auth[DIGEST256_LEN];
94 uint8_t *ptr;
95
96 /* Let's build NTOR_KEY_SEED */
97 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 bad |= safe_mem_is_zero(ntor_key_seed, DIGEST256_LEN);
101
102 /* Let's build ntor_verify */
103 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 bad |= safe_mem_is_zero(ntor_verify, DIGEST256_LEN);
107
108 /* Let's build auth_input: */
109 ptr = rend_auth_input;
110 /* Append ntor_verify */
111 APPEND(ptr, ntor_verify, sizeof(ntor_verify));
112 /* Append AUTH_KEY */
113 APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN);
114 /* Append B */
115 APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
116 /* Append Y */
117 APPEND(ptr,
118 service_ephemeral_rend_pubkey->public_key, CURVE25519_PUBKEY_LEN);
119 /* Append X */
120 APPEND(ptr,
121 client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
122 /* Append PROTOID */
123 APPEND(ptr, PROTOID, strlen(PROTOID));
124 /* Append "Server" */
125 APPEND(ptr, SERVER_STR, strlen(SERVER_STR));
126 tor_assert(ptr == rend_auth_input + sizeof(rend_auth_input));
127
128 /* Let's build auth_input_mac that goes in RENDEZVOUS1 cell */
129 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 bad |= safe_mem_is_zero(rend_cell_auth, DIGEST256_LEN);
133
134 { /* Get the computed RENDEZVOUS1 material! */
135 memcpy(&hs_ntor_rend_cell_keys_out->rend_cell_auth_mac,
136 rend_cell_auth, DIGEST256_LEN);
137 memcpy(&hs_ntor_rend_cell_keys_out->ntor_key_seed,
138 ntor_key_seed, DIGEST256_LEN);
139 }
140
141 memwipe(rend_cell_auth, 0, sizeof(rend_cell_auth));
142 memwipe(rend_auth_input, 0, sizeof(rend_auth_input));
143 memwipe(ntor_key_seed, 0, sizeof(ntor_key_seed));
144
145 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. */
171static void
172get_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 uint8_t keystream[CIPHER256_KEY_LEN + DIGEST256_LEN];
177 uint8_t info_blob[INFO_BLOB_LEN];
178 uint8_t kdf_input[KDF_INPUT_LEN];
179 uint8_t *ptr;
180
181 /* Let's build info */
182 ptr = info_blob;
183 APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND));
184 APPEND(ptr, subcredential->subcred, SUBCRED_LEN);
185 tor_assert(ptr == info_blob + sizeof(info_blob));
186
187 /* Let's build the input to the KDF */
188 ptr = kdf_input;
189 APPEND(ptr, secret_input, INTRO_SECRET_HS_INPUT_LEN);
190 APPEND(ptr, T_HSENC, strlen(T_HSENC));
191 APPEND(ptr, info_blob, sizeof(info_blob));
192 tor_assert(ptr == kdf_input + sizeof(kdf_input));
193
194 /* Now we need to run kdf_input over SHAKE-256 */
195 crypto_xof(keystream, sizeof(keystream),
196 kdf_input, sizeof(kdf_input));
197
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,
202 }
203
204 memwipe(keystream, 0, sizeof(keystream));
205 memwipe(kdf_input, 0, sizeof(kdf_input));
206}
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 */
224static void
225get_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 uint8_t *ptr;
232
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);
245}
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 */
264static void
265get_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 uint8_t *ptr;
273
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);
292}
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). */
315int
316hs_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 int bad = 0;
324 uint8_t secret_input[INTRO_SECRET_HS_INPUT_LEN];
325 uint8_t dh_result[CURVE25519_OUTPUT_LEN];
326
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);
332
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);
338
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);
344
345 /* Get ENC_KEY and MAC_KEY! */
346 get_introduce1_key_material(secret_input, subcredential,
347 hs_ntor_intro_cell_keys_out);
348
349 /* Cleanup */
350 memwipe(secret_input, 0, sizeof(secret_input));
351 memwipe(dh_result, 0, sizeof(dh_result));
352 if (bad) {
353 memwipe(hs_ntor_intro_cell_keys_out, 0, sizeof(hs_ntor_intro_cell_keys_t));
354 }
355
356 return bad ? -1 : 0;
357}
358
359/* Public function: Do the appropriate ntor calculations and derive the keys
360 * needed to verify RENDEZVOUS1 cells and encrypt further rendezvous
361 * traffic. Return 0 and place the final key material in
362 * <b>hs_ntor_rend_cell_keys_out</b> if everything went well, else return -1.
363 *
364 * The relevant calculations are as follows:
365 *
366 * rend_secret_hs_input = EXP(Y,x) | EXP(B,x) | AUTH_KEY | B | X | Y | PROTOID
367 * NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc)
368 * verify = MAC(rend_secret_hs_input, t_hsverify)
369 * auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server"
370 * auth_input_mac = MAC(auth_input, t_hsmac)
371 *
372 * where:
373 * <b>intro_auth_pubkey</b> is AUTH_KEY (found in HS descriptor),
374 * <b>client_ephemeral_enc_keypair</b> is freshly generated keypair (x,X)
375 * <b>intro_enc_pubkey</b> is B (also found in HS descriptor),
376 * <b>service_ephemeral_rend_pubkey</b> is Y (SERVER_PK in RENDEZVOUS1 cell) */
377int
378hs_ntor_client_get_rendezvous1_keys(
379 const ed25519_public_key_t *intro_auth_pubkey,
380 const curve25519_keypair_t *client_ephemeral_enc_keypair,
381 const curve25519_public_key_t *intro_enc_pubkey,
382 const curve25519_public_key_t *service_ephemeral_rend_pubkey,
383 hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out)
384{
385 int bad = 0;
386 uint8_t rend_secret_hs_input[REND_SECRET_HS_INPUT_LEN];
387 uint8_t dh_result1[CURVE25519_OUTPUT_LEN];
388 uint8_t dh_result2[CURVE25519_OUTPUT_LEN];
389
390 tor_assert(intro_auth_pubkey);
391 tor_assert(client_ephemeral_enc_keypair);
392 tor_assert(intro_enc_pubkey);
393 tor_assert(service_ephemeral_rend_pubkey);
394 tor_assert(hs_ntor_rend_cell_keys_out);
395
396 /* Compute EXP(Y, x) */
397 curve25519_handshake(dh_result1,
398 &client_ephemeral_enc_keypair->seckey,
399 service_ephemeral_rend_pubkey);
400 bad |= safe_mem_is_zero(dh_result1, CURVE25519_OUTPUT_LEN);
401
402 /* Compute EXP(B, x) */
403 curve25519_handshake(dh_result2,
404 &client_ephemeral_enc_keypair->seckey,
405 intro_enc_pubkey);
406 bad |= safe_mem_is_zero(dh_result2, CURVE25519_OUTPUT_LEN);
407
408 /* Get rend_secret_hs_input */
409 get_rend_secret_hs_input(dh_result1, dh_result2,
410 intro_auth_pubkey, intro_enc_pubkey,
411 &client_ephemeral_enc_keypair->pubkey,
412 service_ephemeral_rend_pubkey,
413 rend_secret_hs_input);
414
415 /* Get NTOR_KEY_SEED and the auth_input MAC */
416 bad |= get_rendezvous1_key_material(rend_secret_hs_input,
417 intro_auth_pubkey,
418 intro_enc_pubkey,
419 service_ephemeral_rend_pubkey,
420 &client_ephemeral_enc_keypair->pubkey,
421 hs_ntor_rend_cell_keys_out);
422
423 memwipe(dh_result1, 0, sizeof(dh_result1));
424 memwipe(dh_result2, 0, sizeof(dh_result2));
425 memwipe(rend_secret_hs_input, 0, sizeof(rend_secret_hs_input));
426 if (bad) {
427 memwipe(hs_ntor_rend_cell_keys_out, 0, sizeof(hs_ntor_rend_cell_keys_t));
428 }
429
430 return bad ? -1 : 0;
431}
432
433/* Public function: Do the appropriate ntor calculations and derive the keys
434 * needed to decrypt and verify INTRODUCE1 cells. Return 0 and place the final
435 * key material in <b>hs_ntor_intro_cell_keys_out</b> if everything went well,
436 * otherwise return -1;
437 *
438 * The relevant calculations are as follows:
439 *
440 * intro_secret_hs_input = EXP(X,b) | AUTH_KEY | X | B | PROTOID
441 * info = m_hsexpand | subcredential
442 * hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN)
443 * HS_DEC_KEY = hs_keys[0:S_KEY_LEN]
444 * HS_MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN]
445 *
446 * where:
447 * <b>intro_auth_pubkey</b> is AUTH_KEY (introduction point auth key),
448 * <b>intro_enc_keypair</b> is (b,B) (introduction point encryption keypair),
449 * <b>client_ephemeral_enc_pubkey</b> is X (CLIENT_PK in INTRODUCE2 cell),
450 * <b>subcredential</b> is the HS subcredential (of size DIGEST256_LEN) */
451int
452hs_ntor_service_get_introduce1_keys(
453 const ed25519_public_key_t *intro_auth_pubkey,
454 const curve25519_keypair_t *intro_enc_keypair,
455 const curve25519_public_key_t *client_ephemeral_enc_pubkey,
456 const hs_subcredential_t *subcredential,
457 hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
458{
460 intro_auth_pubkey,
461 intro_enc_keypair,
462 client_ephemeral_enc_pubkey,
463 1,
464 subcredential,
465 hs_ntor_intro_cell_keys_out);
466}
467
468/**
469 * As hs_ntor_service_get_introduce1_keys(), but take multiple subcredentials
470 * as input, and yield multiple sets of keys as output.
471 **/
472int
474 const struct ed25519_public_key_t *intro_auth_pubkey,
475 const struct curve25519_keypair_t *intro_enc_keypair,
476 const struct curve25519_public_key_t *client_ephemeral_enc_pubkey,
477 size_t n_subcredentials,
478 const hs_subcredential_t *subcredentials,
479 hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
480{
481 int bad = 0;
482 uint8_t secret_input[INTRO_SECRET_HS_INPUT_LEN];
483 uint8_t dh_result[CURVE25519_OUTPUT_LEN];
484
485 tor_assert(intro_auth_pubkey);
486 tor_assert(intro_enc_keypair);
487 tor_assert(client_ephemeral_enc_pubkey);
488 tor_assert(n_subcredentials >= 1);
489 tor_assert(subcredentials);
490 tor_assert(hs_ntor_intro_cell_keys_out);
491
492 /* Compute EXP(X, b) */
493 curve25519_handshake(dh_result,
494 &intro_enc_keypair->seckey,
495 client_ephemeral_enc_pubkey);
496 bad |= safe_mem_is_zero(dh_result, CURVE25519_OUTPUT_LEN);
497
498 /* Get intro_secret_hs_input */
499 get_intro_secret_hs_input(dh_result, intro_auth_pubkey,
500 client_ephemeral_enc_pubkey,
501 &intro_enc_keypair->pubkey,
502 secret_input);
503 bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN);
504
505 for (unsigned i = 0; i < n_subcredentials; ++i) {
506 /* Get ENC_KEY and MAC_KEY! */
507 get_introduce1_key_material(secret_input, &subcredentials[i],
508 &hs_ntor_intro_cell_keys_out[i]);
509 }
510
511 memwipe(dh_result, 0, sizeof(dh_result));
512 memwipe(secret_input, 0, sizeof(secret_input));
513 if (bad) {
514 memwipe(hs_ntor_intro_cell_keys_out, 0,
515 sizeof(hs_ntor_intro_cell_keys_t) * n_subcredentials);
516 }
517
518 return bad ? -1 : 0;
519}
520
521/* Public function: Do the appropriate ntor calculations and derive the keys
522 * needed to create and authenticate RENDEZVOUS1 cells. Return 0 and place the
523 * final key material in <b>hs_ntor_rend_cell_keys_out</b> if all went fine,
524 * return -1 if error happened.
525 *
526 * The relevant calculations are as follows:
527 *
528 * rend_secret_hs_input = EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID
529 * NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc)
530 * verify = MAC(rend_secret_hs_input, t_hsverify)
531 * auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server"
532 * auth_input_mac = MAC(auth_input, t_hsmac)
533 *
534 * where:
535 * <b>intro_auth_pubkey</b> is AUTH_KEY (intro point auth key),
536 * <b>intro_enc_keypair</b> is (b,B) (intro point enc keypair)
537 * <b>service_ephemeral_rend_keypair</b> is a fresh (y,Y) keypair
538 * <b>client_ephemeral_enc_pubkey</b> is X (CLIENT_PK in INTRODUCE2 cell) */
539int
540hs_ntor_service_get_rendezvous1_keys(
541 const ed25519_public_key_t *intro_auth_pubkey,
542 const curve25519_keypair_t *intro_enc_keypair,
543 const curve25519_keypair_t *service_ephemeral_rend_keypair,
544 const curve25519_public_key_t *client_ephemeral_enc_pubkey,
545 hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out)
546{
547 int bad = 0;
548 uint8_t rend_secret_hs_input[REND_SECRET_HS_INPUT_LEN];
549 uint8_t dh_result1[CURVE25519_OUTPUT_LEN];
550 uint8_t dh_result2[CURVE25519_OUTPUT_LEN];
551
552 tor_assert(intro_auth_pubkey);
553 tor_assert(intro_enc_keypair);
554 tor_assert(service_ephemeral_rend_keypair);
555 tor_assert(client_ephemeral_enc_pubkey);
556 tor_assert(hs_ntor_rend_cell_keys_out);
557
558 /* Compute EXP(X, y) */
559 curve25519_handshake(dh_result1,
560 &service_ephemeral_rend_keypair->seckey,
561 client_ephemeral_enc_pubkey);
562 bad |= safe_mem_is_zero(dh_result1, CURVE25519_OUTPUT_LEN);
563
564 /* Compute EXP(X, b) */
565 curve25519_handshake(dh_result2,
566 &intro_enc_keypair->seckey,
567 client_ephemeral_enc_pubkey);
568 bad |= safe_mem_is_zero(dh_result2, CURVE25519_OUTPUT_LEN);
569
570 /* Get rend_secret_hs_input */
571 get_rend_secret_hs_input(dh_result1, dh_result2,
572 intro_auth_pubkey,
573 &intro_enc_keypair->pubkey,
574 client_ephemeral_enc_pubkey,
575 &service_ephemeral_rend_keypair->pubkey,
576 rend_secret_hs_input);
577
578 /* Get NTOR_KEY_SEED and AUTH_INPUT_MAC! */
579 bad |= get_rendezvous1_key_material(rend_secret_hs_input,
580 intro_auth_pubkey,
581 &intro_enc_keypair->pubkey,
582 &service_ephemeral_rend_keypair->pubkey,
583 client_ephemeral_enc_pubkey,
584 hs_ntor_rend_cell_keys_out);
585
586 memwipe(dh_result1, 0, sizeof(dh_result1));
587 memwipe(dh_result2, 0, sizeof(dh_result2));
588 memwipe(rend_secret_hs_input, 0, sizeof(rend_secret_hs_input));
589 if (bad) {
590 memwipe(hs_ntor_rend_cell_keys_out, 0, sizeof(hs_ntor_rend_cell_keys_t));
591 }
592
593 return bad ? -1 : 0;
594}
595
596/** Given a received RENDEZVOUS2 MAC in <b>mac</b> (of length DIGEST256_LEN),
597 * and the RENDEZVOUS1 key material in <b>hs_ntor_rend_cell_keys</b>, return 1
598 * if the MAC is good, otherwise return 0. */
599int
601 const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys,
602 const uint8_t *rcvd_mac)
603{
604 tor_assert(rcvd_mac);
605 tor_assert(hs_ntor_rend_cell_keys);
606
607 return tor_memeq(hs_ntor_rend_cell_keys->rend_cell_auth_mac,
608 rcvd_mac, DIGEST256_LEN);
609}
610
611/* Input length to KDF for key expansion */
612#define NTOR_KEY_EXPANSION_KDF_INPUT_LEN (DIGEST256_LEN + M_HSEXPAND_LEN)
613
614/** Given the rendezvous key seed in <b>ntor_key_seed</b> (of size
615 * DIGEST256_LEN), do the circuit key expansion as specified by section
616 * '4.2.1. Key expansion' and place the keys in <b>keys_out</b> (which must be
617 * of size HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN).
618 *
619 * Return 0 if things went well, else return -1. */
620int
621hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, size_t seed_len,
622 uint8_t *keys_out, size_t keys_out_len)
623{
624 uint8_t *ptr;
625 uint8_t kdf_input[NTOR_KEY_EXPANSION_KDF_INPUT_LEN];
626
627 /* Sanity checks on lengths to make sure we are good */
628 if (BUG(seed_len != DIGEST256_LEN)) {
629 return -1;
630 }
631 if (BUG(keys_out_len != HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN)) {
632 return -1;
633 }
634
635 /* Let's build the input to the KDF */
636 ptr = kdf_input;
637 APPEND(ptr, ntor_key_seed, DIGEST256_LEN);
638 APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND));
639 tor_assert(ptr == kdf_input + sizeof(kdf_input));
640
641 /* Generate the keys */
642 crypto_xof(keys_out, HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN,
643 kdf_input, sizeof(kdf_input));
644
645 memwipe(kdf_input, 0, sizeof(kdf_input));
646 return 0;
647}
#define CIPHER256_KEY_LEN
void curve25519_handshake(uint8_t *output, const curve25519_secret_key_t *skey, const curve25519_public_key_t *pkey)
Header for crypto_curve25519.c.
void crypto_mac_sha3_256(uint8_t *mac_out, size_t len_out, const uint8_t *key, size_t key_len, const uint8_t *msg, size_t msg_len)
void crypto_xof(uint8_t *output, size_t output_len, const uint8_t *input, size_t input_len)
Header for crypto_ed25519.c.
void memwipe(void *mem, uint8_t byte, size_t sz)
Definition crypto_util.c:55
Common functions for cryptographic routines.
int tor_memeq(const void *a, const void *b, size_t sz)
Definition di_ops.c:107
int safe_mem_is_zero(const void *mem, size_t sz)
Definition di_ops.c:224
#define DIGEST256_LEN
int hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, size_t seed_len, uint8_t *keys_out, size_t keys_out_len)
Definition hs_ntor.c:621
#define INTRO_SECRET_HS_INPUT_LEN
Definition hs_ntor.c:149
static void get_rend_secret_hs_input(const uint8_t *dh_result1, const uint8_t *dh_result2, const ed25519_public_key_t *intro_auth_pubkey, const curve25519_public_key_t *intro_enc_pubkey, const curve25519_public_key_t *client_ephemeral_enc_pubkey, const curve25519_public_key_t *service_ephemeral_rend_pubkey, uint8_t *rend_secret_hs_input_out)
Definition hs_ntor.c:265
static void get_introduce1_key_material(const uint8_t *secret_input, const hs_subcredential_t *subcredential, hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
Definition hs_ntor.c:172
#define APPEND(ptr, inp, len)
Definition hs_ntor.c:51
int hs_ntor_service_get_introduce1_keys_multi(const struct ed25519_public_key_t *intro_auth_pubkey, const struct curve25519_keypair_t *intro_enc_keypair, const struct curve25519_public_key_t *client_ephemeral_enc_pubkey, size_t n_subcredentials, const hs_subcredential_t *subcredentials, hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
Definition hs_ntor.c:473
int hs_ntor_client_rendezvous2_mac_is_good(const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys, const uint8_t *rcvd_mac)
Definition hs_ntor.c:600
static void get_intro_secret_hs_input(const uint8_t *dh_result, const ed25519_public_key_t *intro_auth_pubkey, const curve25519_public_key_t *client_ephemeral_enc_pubkey, const curve25519_public_key_t *intro_enc_pubkey, uint8_t *secret_input_out)
Definition hs_ntor.c:225
static int get_rendezvous1_key_material(const uint8_t *rend_secret_hs_input, const ed25519_public_key_t *intro_auth_pubkey, const curve25519_public_key_t *intro_enc_pubkey, const curve25519_public_key_t *service_ephemeral_rend_pubkey, const curve25519_public_key_t *client_ephemeral_enc_pubkey, hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out)
Definition hs_ntor.c:82
Header for hs_ntor.c.
Master header file for Tor-specific functionality.
#define tor_assert(expr)
Definition util_bug.h:103
#define CURVE25519_OUTPUT_LEN
#define ED25519_PUBKEY_LEN
#define CURVE25519_PUBKEY_LEN