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
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)82 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   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(ntor_verify, 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. */
171 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)172 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   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,
201            keystream+CIPHER256_KEY_LEN, DIGEST256_LEN);
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  */
224 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)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)
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  */
264 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)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)
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). */
315 int
hs_ntor_client_get_introduce1_keys(const ed25519_public_key_t * intro_auth_pubkey,const curve25519_public_key_t * intro_enc_pubkey,const curve25519_keypair_t * client_ephemeral_enc_keypair,const hs_subcredential_t * subcredential,hs_ntor_intro_cell_keys_t * hs_ntor_intro_cell_keys_out)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 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   if (bad) {
352     memwipe(hs_ntor_intro_cell_keys_out, 0, sizeof(hs_ntor_intro_cell_keys_t));
353   }
354 
355   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
hs_ntor_client_get_rendezvous1_keys(const ed25519_public_key_t * intro_auth_pubkey,const curve25519_keypair_t * client_ephemeral_enc_keypair,const curve25519_public_key_t * intro_enc_pubkey,const curve25519_public_key_t * service_ephemeral_rend_pubkey,hs_ntor_rend_cell_keys_t * hs_ntor_rend_cell_keys_out)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)
383 {
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];
388 
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);
394 
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);
400 
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);
406 
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);
413 
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);
421 
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));
425   }
426 
427   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
hs_ntor_service_get_introduce1_keys(const ed25519_public_key_t * intro_auth_pubkey,const curve25519_keypair_t * intro_enc_keypair,const curve25519_public_key_t * client_ephemeral_enc_pubkey,const hs_subcredential_t * subcredential,hs_ntor_intro_cell_keys_t * hs_ntor_intro_cell_keys_out)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 hs_subcredential_t *subcredential,
454                     hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
455 {
456   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
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)470 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   int bad = 0;
479   uint8_t secret_input[INTRO_SECRET_HS_INPUT_LEN];
480   uint8_t dh_result[CURVE25519_OUTPUT_LEN];
481 
482   tor_assert(intro_auth_pubkey);
483   tor_assert(intro_enc_keypair);
484   tor_assert(client_ephemeral_enc_pubkey);
485   tor_assert(n_subcredentials >= 1);
486   tor_assert(subcredentials);
487   tor_assert(hs_ntor_intro_cell_keys_out);
488 
489   /* Compute EXP(X, b) */
490   curve25519_handshake(dh_result,
491                        &intro_enc_keypair->seckey,
492                        client_ephemeral_enc_pubkey);
493   bad |= safe_mem_is_zero(dh_result, CURVE25519_OUTPUT_LEN);
494 
495   /* Get intro_secret_hs_input */
496   get_intro_secret_hs_input(dh_result, intro_auth_pubkey,
497                             client_ephemeral_enc_pubkey,
498                             &intro_enc_keypair->pubkey,
499                             secret_input);
500   bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN);
501 
502   for (unsigned i = 0; i < n_subcredentials; ++i) {
503     /* Get ENC_KEY and MAC_KEY! */
504     get_introduce1_key_material(secret_input, &subcredentials[i],
505                                 &hs_ntor_intro_cell_keys_out[i]);
506   }
507 
508   memwipe(secret_input,  0, sizeof(secret_input));
509   if (bad) {
510     memwipe(hs_ntor_intro_cell_keys_out, 0,
511             sizeof(hs_ntor_intro_cell_keys_t) * n_subcredentials);
512   }
513 
514   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
hs_ntor_service_get_rendezvous1_keys(const ed25519_public_key_t * intro_auth_pubkey,const curve25519_keypair_t * intro_enc_keypair,const curve25519_keypair_t * service_ephemeral_rend_keypair,const curve25519_public_key_t * client_ephemeral_enc_pubkey,hs_ntor_rend_cell_keys_t * hs_ntor_rend_cell_keys_out)536 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   int bad = 0;
544   uint8_t rend_secret_hs_input[REND_SECRET_HS_INPUT_LEN];
545   uint8_t dh_result1[CURVE25519_OUTPUT_LEN];
546   uint8_t dh_result2[CURVE25519_OUTPUT_LEN];
547 
548   tor_assert(intro_auth_pubkey);
549   tor_assert(intro_enc_keypair);
550   tor_assert(service_ephemeral_rend_keypair);
551   tor_assert(client_ephemeral_enc_pubkey);
552   tor_assert(hs_ntor_rend_cell_keys_out);
553 
554   /* Compute EXP(X, y) */
555   curve25519_handshake(dh_result1,
556                        &service_ephemeral_rend_keypair->seckey,
557                        client_ephemeral_enc_pubkey);
558   bad |= safe_mem_is_zero(dh_result1, CURVE25519_OUTPUT_LEN);
559 
560   /* Compute EXP(X, b) */
561   curve25519_handshake(dh_result2,
562                        &intro_enc_keypair->seckey,
563                        client_ephemeral_enc_pubkey);
564   bad |= safe_mem_is_zero(dh_result2, CURVE25519_OUTPUT_LEN);
565 
566   /* Get rend_secret_hs_input */
567   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   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   memwipe(rend_secret_hs_input, 0, sizeof(rend_secret_hs_input));
583   if (bad) {
584     memwipe(hs_ntor_rend_cell_keys_out, 0, sizeof(hs_ntor_rend_cell_keys_t));
585   }
586 
587   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
hs_ntor_client_rendezvous2_mac_is_good(const hs_ntor_rend_cell_keys_t * hs_ntor_rend_cell_keys,const uint8_t * rcvd_mac)594 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   tor_assert(rcvd_mac);
599   tor_assert(hs_ntor_rend_cell_keys);
600 
601   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
hs_ntor_circuit_key_expansion(const uint8_t * ntor_key_seed,size_t seed_len,uint8_t * keys_out,size_t keys_out_len)615 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   uint8_t *ptr;
619   uint8_t kdf_input[NTOR_KEY_EXPANSION_KDF_INPUT_LEN];
620 
621   /* Sanity checks on lengths to make sure we are good */
622   if (BUG(seed_len != DIGEST256_LEN)) {
623     return -1;
624   }
625   if (BUG(keys_out_len != HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN)) {
626     return -1;
627   }
628 
629   /* Let's build the input to the KDF */
630   ptr = kdf_input;
631   APPEND(ptr, ntor_key_seed, DIGEST256_LEN);
632   APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND));
633   tor_assert(ptr == kdf_input + sizeof(kdf_input));
634 
635   /* Generate the keys */
636   crypto_xof(keys_out, HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN,
637              kdf_input, sizeof(kdf_input));
638 
639   return 0;
640 }
641