hs_ntor.c (26303B)
1 /* Copyright (c) 2017-2021, The Tor Project, Inc. */ 2 /* See LICENSE for licensing information */ 3 4 /** \file hs_ntor.c 5 * \brief Implements the ntor variant used in Tor hidden services. 6 * 7 * \details 8 * This module handles the variant of the ntor handshake that is documented in 9 * section [NTOR-WITH-EXTRA-DATA] of rend-spec-ng.txt . 10 * 11 * The functions in this file provide an API that should be used when sending 12 * or receiving INTRODUCE1/RENDEZVOUS1 cells to generate the various key 13 * material required to create and handle those cells. 14 * 15 * In the case of INTRODUCE1 it provides encryption and MAC keys to 16 * encode/decode the encrypted blob (see hs_ntor_intro_cell_keys_t). The 17 * relevant pub functions are hs_ntor_{client,service}_get_introduce1_keys(). 18 * 19 * In the case of RENDEZVOUS1 it calculates the MAC required to authenticate 20 * the cell, and also provides the key seed that is used to derive the crypto 21 * material for rendezvous encryption (see hs_ntor_rend_cell_keys_t). The 22 * relevant pub functions are hs_ntor_{client,service}_get_rendezvous1_keys(). 23 * It also provides a function (hs_ntor_circuit_key_expansion()) that does the 24 * rendezvous key expansion to setup end-to-end rend circuit keys. 25 */ 26 27 #include "core/or/or.h" 28 #include "lib/crypt_ops/crypto_util.h" 29 #include "lib/crypt_ops/crypto_curve25519.h" 30 #include "lib/crypt_ops/crypto_ed25519.h" 31 #include "core/crypto/hs_ntor.h" 32 33 /* String constants used by the ntor HS protocol */ 34 #define PROTOID "tor-hs-ntor-curve25519-sha3-256-1" 35 #define PROTOID_LEN (sizeof(PROTOID) - 1) 36 #define SERVER_STR "Server" 37 #define SERVER_STR_LEN (sizeof(SERVER_STR) - 1) 38 39 /* Protocol-specific tweaks to our crypto inputs */ 40 #define T_HSENC PROTOID ":hs_key_extract" 41 #define T_HSENC_LEN (sizeof(T_HSENC) - 1) 42 #define T_HSVERIFY PROTOID ":hs_verify" 43 #define T_HSMAC PROTOID ":hs_mac" 44 #define M_HSEXPAND PROTOID ":hs_key_expand" 45 #define M_HSEXPAND_LEN (sizeof(M_HSEXPAND) - 1) 46 47 /************************* Helper functions: *******************************/ 48 49 /** Helper macro: copy <b>len</b> bytes from <b>inp</b> to <b>ptr</b> and 50 *advance <b>ptr</b> by the number of bytes copied. Stolen from onion_ntor.c */ 51 #define APPEND(ptr, inp, len) \ 52 STMT_BEGIN { \ 53 memcpy(ptr, (inp), (len)); \ 54 ptr += len; \ 55 } STMT_END 56 57 /* Length of EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID */ 58 #define REND_SECRET_HS_INPUT_LEN (CURVE25519_OUTPUT_LEN * 2 + \ 59 ED25519_PUBKEY_LEN + CURVE25519_PUBKEY_LEN * 3 + PROTOID_LEN) 60 /* Length of auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" */ 61 #define REND_AUTH_INPUT_LEN (DIGEST256_LEN + ED25519_PUBKEY_LEN + \ 62 CURVE25519_PUBKEY_LEN * 3 + PROTOID_LEN + SERVER_STR_LEN) 63 64 /** Helper function: Compute the last part of the HS ntor handshake which 65 * derives key material necessary to create and handle RENDEZVOUS1 66 * cells. Function used by both client and service. The actual calculations is 67 * as follows: 68 * 69 * NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc) 70 * verify = MAC(rend_secret_hs_input, t_hsverify) 71 * auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" 72 * auth_input_mac = MAC(auth_input, t_hsmac) 73 * 74 * where in the above, AUTH_KEY is <b>intro_auth_pubkey</b>, B is 75 * <b>intro_enc_pubkey</b>, Y is <b>service_ephemeral_rend_pubkey</b>, and X 76 * is <b>client_ephemeral_enc_pubkey</b>. The provided 77 * <b>rend_secret_hs_input</b> is of size REND_SECRET_HS_INPUT_LEN. 78 * 79 * The final results of NTOR_KEY_SEED and auth_input_mac are placed in 80 * <b>hs_ntor_rend_cell_keys_out</b>. Return 0 if everything went fine. */ 81 static int 82 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 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 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 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 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 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 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 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 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 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 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 }