1 /* Copyright (c) 2017, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
5 * \brief Implements the ntor variant used in Tor hidden services.
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.
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) \
50 memcpy(ptr, (inp), (len)); \
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
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. */
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
)
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
];
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
);
112 APPEND(ptr
, intro_enc_pubkey
->public_key
, CURVE25519_PUBKEY_LEN
);
115 service_ephemeral_rend_pubkey
->public_key
, CURVE25519_PUBKEY_LEN
);
118 client_ephemeral_enc_pubkey
->public_key
, CURVE25519_PUBKEY_LEN
);
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
));
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
166 * If everything went well, fill <b>hs_ntor_intro_cell_keys_out</b> with the
167 * necessary key material, and return 0. */
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
];
179 /* Let's build info */
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 */
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
);
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.
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
)
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
);
239 APPEND(ptr
, client_ephemeral_enc_pubkey
->public_key
, CURVE25519_PUBKEY_LEN
);
241 APPEND(ptr
, intro_enc_pubkey
->public_key
, CURVE25519_PUBKEY_LEN
);
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
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.
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
)
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
);
282 APPEND(ptr
, intro_enc_pubkey
->public_key
, CURVE25519_PUBKEY_LEN
);
285 client_ephemeral_enc_pubkey
->public_key
, CURVE25519_PUBKEY_LEN
);
288 service_ephemeral_rend_pubkey
->public_key
, CURVE25519_PUBKEY_LEN
);
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]
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
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
)
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
,
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
);
350 memwipe(secret_input
, 0, sizeof(secret_input
));
352 memwipe(hs_ntor_intro_cell_keys_out
, 0, sizeof(hs_ntor_intro_cell_keys_t
));
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)
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
)
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
,
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
,
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
));
424 memwipe(hs_ntor_rend_cell_keys_out
, 0, sizeof(hs_ntor_rend_cell_keys_t
));
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]
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
)
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
,
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
));
485 memwipe(hs_ntor_intro_cell_keys_out
, 0, sizeof(hs_ntor_intro_cell_keys_t
));
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)
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
)
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
,
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
,
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
));
558 memwipe(hs_ntor_rend_cell_keys_out
, 0, sizeof(hs_ntor_rend_cell_keys_t
));
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
)
593 uint8_t kdf_input
[NTOR_KEY_EXPANSION_KDF_INPUT_LEN
];
596 /* Sanity checks on lengths to make sure we are good */
597 if (BUG(seed_len
!= DIGEST256_LEN
)) {
600 if (BUG(keys_out_len
!= HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN
)) {
604 /* Let's build the input to the KDF */
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
);