/* Copyright (c) 2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** \file hs_ntor.c * \brief Implements the ntor variant used in Tor hidden services. * * \details * This module handles the variant of the ntor handshake that is documented in * section [NTOR-WITH-EXTRA-DATA] of rend-spec-ng.txt . * * The functions in this file provide an API that should be used when sending * or receiving INTRODUCE1/RENDEZVOUS1 cells to generate the various key * material required to create and handle those cells. * * In the case of INTRODUCE1 it provides encryption and MAC keys to * encode/decode the encrypted blob (see hs_ntor_intro_cell_keys_t). The * relevant pub functions are hs_ntor_{client,service}_get_introduce1_keys(). * * In the case of RENDEZVOUS1 it calculates the MAC required to authenticate * the cell, and also provides the key seed that is used to derive the crypto * material for rendezvous encryption (see hs_ntor_rend_cell_keys_t). The * relevant pub functions are hs_ntor_{client,service}_get_rendezvous1_keys(). * It also provides a function (hs_ntor_circuit_key_expansion()) that does the * rendezvous key expansion to setup end-to-end rend circuit keys. */ #include "or.h" #include "hs_ntor.h" /* String constants used by the ntor HS protocol */ #define PROTOID "tor-hs-ntor-curve25519-sha3-256-1" #define PROTOID_LEN (sizeof(PROTOID) - 1) #define SERVER_STR "Server" #define SERVER_STR_LEN (sizeof(SERVER_STR) - 1) /* Protocol-specific tweaks to our crypto inputs */ #define T_HSENC PROTOID ":hs_key_extract" #define T_HSENC_LEN (sizeof(T_HSENC) - 1) #define T_HSVERIFY PROTOID ":hs_verify" #define T_HSMAC PROTOID ":hs_mac" #define M_HSEXPAND PROTOID ":hs_key_expand" #define M_HSEXPAND_LEN (sizeof(M_HSEXPAND) - 1) /************************* Helper functions: *******************************/ /** Helper macro: copy len bytes from inp to ptr and *advance ptr by the number of bytes copied. Stolen from onion_ntor.c */ #define APPEND(ptr, inp, len) \ STMT_BEGIN { \ memcpy(ptr, (inp), (len)); \ ptr += len; \ } STMT_END /* Length of EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID */ #define REND_SECRET_HS_INPUT_LEN (CURVE25519_OUTPUT_LEN * 2 + \ ED25519_PUBKEY_LEN + CURVE25519_PUBKEY_LEN * 3 + PROTOID_LEN) /* Length of auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" */ #define REND_AUTH_INPUT_LEN (DIGEST256_LEN + ED25519_PUBKEY_LEN + \ CURVE25519_PUBKEY_LEN * 3 + PROTOID_LEN + SERVER_STR_LEN) /** Helper function: Compute the last part of the HS ntor handshake which * derives key material necessary to create and handle RENDEZVOUS1 * cells. Function used by both client and service. The actual calculations is * as follows: * * NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc) * verify = MAC(rend_secret_hs_input, t_hsverify) * auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" * auth_input_mac = MAC(auth_input, t_hsmac) * * where in the above, AUTH_KEY is intro_auth_pubkey, B is * intro_enc_pubkey, Y is service_ephemeral_rend_pubkey, and X * is client_ephemeral_enc_pubkey. The provided * rend_secret_hs_input is of size REND_SECRET_HS_INPUT_LEN. * * The final results of NTOR_KEY_SEED and auth_input_mac are placed in * hs_ntor_rend_cell_keys_out. Return 0 if everything went fine. */ 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) { int bad = 0; uint8_t ntor_key_seed[DIGEST256_LEN]; uint8_t ntor_verify[DIGEST256_LEN]; uint8_t rend_auth_input[REND_AUTH_INPUT_LEN]; uint8_t rend_cell_auth[DIGEST256_LEN]; uint8_t *ptr; /* Let's build NTOR_KEY_SEED */ crypto_mac_sha3_256(ntor_key_seed, sizeof(ntor_key_seed), rend_secret_hs_input, REND_SECRET_HS_INPUT_LEN, (const uint8_t *)T_HSENC, strlen(T_HSENC)); bad |= safe_mem_is_zero(ntor_key_seed, DIGEST256_LEN); /* Let's build ntor_verify */ crypto_mac_sha3_256(ntor_verify, sizeof(ntor_verify), rend_secret_hs_input, REND_SECRET_HS_INPUT_LEN, (const uint8_t *)T_HSVERIFY, strlen(T_HSVERIFY)); bad |= safe_mem_is_zero(ntor_verify, DIGEST256_LEN); /* Let's build auth_input: */ ptr = rend_auth_input; /* Append ntor_verify */ APPEND(ptr, ntor_verify, sizeof(ntor_verify)); /* Append AUTH_KEY */ APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN); /* Append B */ APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN); /* Append Y */ APPEND(ptr, service_ephemeral_rend_pubkey->public_key, CURVE25519_PUBKEY_LEN); /* Append X */ APPEND(ptr, client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN); /* Append PROTOID */ APPEND(ptr, PROTOID, strlen(PROTOID)); /* Append "Server" */ APPEND(ptr, SERVER_STR, strlen(SERVER_STR)); tor_assert(ptr == rend_auth_input + sizeof(rend_auth_input)); /* Let's build auth_input_mac that goes in RENDEZVOUS1 cell */ crypto_mac_sha3_256(rend_cell_auth, sizeof(rend_cell_auth), rend_auth_input, sizeof(rend_auth_input), (const uint8_t *)T_HSMAC, strlen(T_HSMAC)); bad |= safe_mem_is_zero(ntor_verify, DIGEST256_LEN); { /* Get the computed RENDEZVOUS1 material! */ memcpy(&hs_ntor_rend_cell_keys_out->rend_cell_auth_mac, rend_cell_auth, DIGEST256_LEN); memcpy(&hs_ntor_rend_cell_keys_out->ntor_key_seed, ntor_key_seed, DIGEST256_LEN); } memwipe(rend_cell_auth, 0, sizeof(rend_cell_auth)); memwipe(rend_auth_input, 0, sizeof(rend_auth_input)); memwipe(ntor_key_seed, 0, sizeof(ntor_key_seed)); return bad; } /** Length of secret_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID */ #define INTRO_SECRET_HS_INPUT_LEN (CURVE25519_OUTPUT_LEN +ED25519_PUBKEY_LEN +\ CURVE25519_PUBKEY_LEN + CURVE25519_PUBKEY_LEN + PROTOID_LEN) /* Length of info = m_hsexpand | subcredential */ #define INFO_BLOB_LEN (M_HSEXPAND_LEN + DIGEST256_LEN) /* Length of KDF input = intro_secret_hs_input | t_hsenc | info */ #define KDF_INPUT_LEN (INTRO_SECRET_HS_INPUT_LEN + T_HSENC_LEN + INFO_BLOB_LEN) /** Helper function: Compute the part of the HS ntor handshake that generates * key material for creating and handling INTRODUCE1 cells. Function used * by both client and service. Specifically, calculate the following: * * info = m_hsexpand | subcredential * hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN) * ENC_KEY = hs_keys[0:S_KEY_LEN] * MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN] * * where intro_secret_hs_input is secret_input (of size * INTRO_SECRET_HS_INPUT_LEN), and subcredential is of size * DIGEST256_LEN. * * If everything went well, fill hs_ntor_intro_cell_keys_out with the * necessary key material, and return 0. */ static void get_introduce1_key_material(const uint8_t *secret_input, const uint8_t *subcredential, hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out) { uint8_t keystream[CIPHER256_KEY_LEN + DIGEST256_LEN]; uint8_t info_blob[INFO_BLOB_LEN]; uint8_t kdf_input[KDF_INPUT_LEN]; crypto_xof_t *xof; uint8_t *ptr; /* Let's build info */ ptr = info_blob; APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND)); APPEND(ptr, subcredential, DIGEST256_LEN); tor_assert(ptr == info_blob + sizeof(info_blob)); /* Let's build the input to the KDF */ ptr = kdf_input; APPEND(ptr, secret_input, INTRO_SECRET_HS_INPUT_LEN); APPEND(ptr, T_HSENC, strlen(T_HSENC)); APPEND(ptr, info_blob, sizeof(info_blob)); tor_assert(ptr == kdf_input + sizeof(kdf_input)); /* Now we need to run kdf_input over SHAKE-256 */ xof = crypto_xof_new(); crypto_xof_add_bytes(xof, kdf_input, sizeof(kdf_input)); crypto_xof_squeeze_bytes(xof, keystream, sizeof(keystream)) ; crypto_xof_free(xof); { /* Get the keys */ memcpy(&hs_ntor_intro_cell_keys_out->enc_key, keystream,CIPHER256_KEY_LEN); memcpy(&hs_ntor_intro_cell_keys_out->mac_key, keystream+CIPHER256_KEY_LEN, DIGEST256_LEN); } memwipe(keystream, 0, sizeof(keystream)); memwipe(kdf_input, 0, sizeof(kdf_input)); } /** Helper function: Calculate the 'intro_secret_hs_input' element used by the * HS ntor handshake and place it in secret_input_out. This function is * used by both client and service code. * * For the client-side it looks like this: * * intro_secret_hs_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID * * whereas for the service-side it looks like this: * * intro_secret_hs_input = EXP(X,b) | AUTH_KEY | X | B | PROTOID * * In this function, dh_result carries the EXP() result (and has size * CURVE25519_OUTPUT_LEN) intro_auth_pubkey is AUTH_KEY, * client_ephemeral_enc_pubkey is X, and intro_enc_pubkey is B. */ 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) { uint8_t *ptr; /* Append EXP() */ ptr = secret_input_out; APPEND(ptr, dh_result, CURVE25519_OUTPUT_LEN); /* Append AUTH_KEY */ APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN); /* Append X */ APPEND(ptr, client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN); /* Append B */ APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN); /* Append PROTOID */ APPEND(ptr, PROTOID, strlen(PROTOID)); tor_assert(ptr == secret_input_out + INTRO_SECRET_HS_INPUT_LEN); } /** Calculate the 'rend_secret_hs_input' element used by the HS ntor handshake * and place it in rend_secret_hs_input_out. This function is used by * both client and service code. * * The computation on the client side is: * rend_secret_hs_input = EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID * whereas on the service side it is: * rend_secret_hs_input = EXP(Y,x) | EXP(B,x) | AUTH_KEY | B | X | Y | PROTOID * * where: * dh_result1 and dh_result2 carry the two EXP() results (of size * CURVE25519_OUTPUT_LEN) * intro_auth_pubkey is AUTH_KEY, * intro_enc_pubkey is B, * client_ephemeral_enc_pubkey is X, and * service_ephemeral_rend_pubkey is Y. */ 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) { uint8_t *ptr; ptr = rend_secret_hs_input_out; /* Append the first EXP() */ APPEND(ptr, dh_result1, CURVE25519_OUTPUT_LEN); /* Append the other EXP() */ APPEND(ptr, dh_result2, CURVE25519_OUTPUT_LEN); /* Append AUTH_KEY */ APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN); /* Append B */ APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN); /* Append X */ APPEND(ptr, client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN); /* Append Y */ APPEND(ptr, service_ephemeral_rend_pubkey->public_key, CURVE25519_PUBKEY_LEN); /* Append PROTOID */ APPEND(ptr, PROTOID, strlen(PROTOID)); tor_assert(ptr == rend_secret_hs_input_out + REND_SECRET_HS_INPUT_LEN); } /************************* Public functions: *******************************/ /* Public function: Do the appropriate ntor calculations and derive the keys * needed to encrypt and authenticate INTRODUCE1 cells. Return 0 and place the * final key material in hs_ntor_intro_cell_keys_out if everything went * well, otherwise return -1; * * The relevant calculations are as follows: * * intro_secret_hs_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID * info = m_hsexpand | subcredential * hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN) * ENC_KEY = hs_keys[0:S_KEY_LEN] * MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN] * * where: * intro_auth_pubkey is AUTH_KEY (found in HS descriptor), * intro_enc_pubkey is B (also found in HS descriptor), * client_ephemeral_enc_keypair is freshly generated keypair (x,X) * subcredential is the hidden service subcredential (of size * DIGEST256_LEN). */ 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 uint8_t *subcredential, hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out) { int bad = 0; uint8_t secret_input[INTRO_SECRET_HS_INPUT_LEN]; uint8_t dh_result[CURVE25519_OUTPUT_LEN]; tor_assert(intro_auth_pubkey); tor_assert(intro_enc_pubkey); tor_assert(client_ephemeral_enc_keypair); tor_assert(subcredential); tor_assert(hs_ntor_intro_cell_keys_out); /* Calculate EXP(B,x) */ curve25519_handshake(dh_result, &client_ephemeral_enc_keypair->seckey, intro_enc_pubkey); bad |= safe_mem_is_zero(dh_result, CURVE25519_OUTPUT_LEN); /* Get intro_secret_hs_input */ get_intro_secret_hs_input(dh_result, intro_auth_pubkey, &client_ephemeral_enc_keypair->pubkey, intro_enc_pubkey, secret_input); bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN); /* Get ENC_KEY and MAC_KEY! */ get_introduce1_key_material(secret_input, subcredential, hs_ntor_intro_cell_keys_out); /* Cleanup */ memwipe(secret_input, 0, sizeof(secret_input)); if (bad) { memwipe(hs_ntor_intro_cell_keys_out, 0, sizeof(hs_ntor_intro_cell_keys_t)); } return bad ? -1 : 0; } /* Public function: Do the appropriate ntor calculations and derive the keys * needed to verify RENDEZVOUS1 cells and encrypt further rendezvous * traffic. Return 0 and place the final key material in * hs_ntor_rend_cell_keys_out if everything went well, else return -1. * * The relevant calculations are as follows: * * rend_secret_hs_input = EXP(Y,x) | EXP(B,x) | AUTH_KEY | B | X | Y | PROTOID * NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc) * verify = MAC(rend_secret_hs_input, t_hsverify) * auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" * auth_input_mac = MAC(auth_input, t_hsmac) * * where: * intro_auth_pubkey is AUTH_KEY (found in HS descriptor), * client_ephemeral_enc_keypair is freshly generated keypair (x,X) * intro_enc_pubkey is B (also found in HS descriptor), * service_ephemeral_rend_pubkey is Y (SERVER_PK in RENDEZVOUS1 cell) */ 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) { int bad = 0; uint8_t rend_secret_hs_input[REND_SECRET_HS_INPUT_LEN]; uint8_t dh_result1[CURVE25519_OUTPUT_LEN]; uint8_t dh_result2[CURVE25519_OUTPUT_LEN]; tor_assert(intro_auth_pubkey); tor_assert(client_ephemeral_enc_keypair); tor_assert(intro_enc_pubkey); tor_assert(service_ephemeral_rend_pubkey); tor_assert(hs_ntor_rend_cell_keys_out); /* Compute EXP(Y, x) */ curve25519_handshake(dh_result1, &client_ephemeral_enc_keypair->seckey, service_ephemeral_rend_pubkey); bad |= safe_mem_is_zero(dh_result1, CURVE25519_OUTPUT_LEN); /* Compute EXP(B, x) */ curve25519_handshake(dh_result2, &client_ephemeral_enc_keypair->seckey, intro_enc_pubkey); bad |= safe_mem_is_zero(dh_result2, CURVE25519_OUTPUT_LEN); /* Get rend_secret_hs_input */ get_rend_secret_hs_input(dh_result1, dh_result2, intro_auth_pubkey, intro_enc_pubkey, &client_ephemeral_enc_keypair->pubkey, service_ephemeral_rend_pubkey, rend_secret_hs_input); /* Get NTOR_KEY_SEED and the auth_input MAC */ bad |= get_rendezvous1_key_material(rend_secret_hs_input, intro_auth_pubkey, intro_enc_pubkey, service_ephemeral_rend_pubkey, &client_ephemeral_enc_keypair->pubkey, hs_ntor_rend_cell_keys_out); memwipe(rend_secret_hs_input, 0, sizeof(rend_secret_hs_input)); if (bad) { memwipe(hs_ntor_rend_cell_keys_out, 0, sizeof(hs_ntor_rend_cell_keys_t)); } return bad ? -1 : 0; } /* Public function: Do the appropriate ntor calculations and derive the keys * needed to decrypt and verify INTRODUCE1 cells. Return 0 and place the final * key material in hs_ntor_intro_cell_keys_out if everything went well, * otherwise return -1; * * The relevant calculations are as follows: * * intro_secret_hs_input = EXP(X,b) | AUTH_KEY | X | B | PROTOID * info = m_hsexpand | subcredential * hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN) * HS_DEC_KEY = hs_keys[0:S_KEY_LEN] * HS_MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN] * * where: * intro_auth_pubkey is AUTH_KEY (introduction point auth key), * intro_enc_keypair is (b,B) (introduction point encryption keypair), * client_ephemeral_enc_pubkey is X (CLIENT_PK in INTRODUCE2 cell), * subcredential is the HS subcredential (of size DIGEST256_LEN) */ 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 uint8_t *subcredential, hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out) { int bad = 0; uint8_t secret_input[INTRO_SECRET_HS_INPUT_LEN]; uint8_t dh_result[CURVE25519_OUTPUT_LEN]; tor_assert(intro_auth_pubkey); tor_assert(intro_enc_keypair); tor_assert(client_ephemeral_enc_pubkey); tor_assert(subcredential); tor_assert(hs_ntor_intro_cell_keys_out); /* Compute EXP(X, b) */ curve25519_handshake(dh_result, &intro_enc_keypair->seckey, client_ephemeral_enc_pubkey); bad |= safe_mem_is_zero(dh_result, CURVE25519_OUTPUT_LEN); /* Get intro_secret_hs_input */ get_intro_secret_hs_input(dh_result, intro_auth_pubkey, client_ephemeral_enc_pubkey, &intro_enc_keypair->pubkey, secret_input); bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN); /* Get ENC_KEY and MAC_KEY! */ get_introduce1_key_material(secret_input, subcredential, hs_ntor_intro_cell_keys_out); memwipe(secret_input, 0, sizeof(secret_input)); if (bad) { memwipe(hs_ntor_intro_cell_keys_out, 0, sizeof(hs_ntor_intro_cell_keys_t)); } return bad ? -1 : 0; } /* Public function: Do the appropriate ntor calculations and derive the keys * needed to create and authenticate RENDEZVOUS1 cells. Return 0 and place the * final key material in hs_ntor_rend_cell_keys_out if all went fine, * return -1 if error happened. * * The relevant calculations are as follows: * * rend_secret_hs_input = EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID * NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc) * verify = MAC(rend_secret_hs_input, t_hsverify) * auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" * auth_input_mac = MAC(auth_input, t_hsmac) * * where: * intro_auth_pubkey is AUTH_KEY (intro point auth key), * intro_enc_keypair is (b,B) (intro point enc keypair) * service_ephemeral_rend_keypair is a fresh (y,Y) keypair * client_ephemeral_enc_pubkey is X (CLIENT_PK in INTRODUCE2 cell) */ 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) { int bad = 0; uint8_t rend_secret_hs_input[REND_SECRET_HS_INPUT_LEN]; uint8_t dh_result1[CURVE25519_OUTPUT_LEN]; uint8_t dh_result2[CURVE25519_OUTPUT_LEN]; tor_assert(intro_auth_pubkey); tor_assert(intro_enc_keypair); tor_assert(service_ephemeral_rend_keypair); tor_assert(client_ephemeral_enc_pubkey); tor_assert(hs_ntor_rend_cell_keys_out); /* Compute EXP(X, y) */ curve25519_handshake(dh_result1, &service_ephemeral_rend_keypair->seckey, client_ephemeral_enc_pubkey); bad |= safe_mem_is_zero(dh_result1, CURVE25519_OUTPUT_LEN); /* Compute EXP(X, b) */ curve25519_handshake(dh_result2, &intro_enc_keypair->seckey, client_ephemeral_enc_pubkey); bad |= safe_mem_is_zero(dh_result2, CURVE25519_OUTPUT_LEN); /* Get rend_secret_hs_input */ get_rend_secret_hs_input(dh_result1, dh_result2, intro_auth_pubkey, &intro_enc_keypair->pubkey, client_ephemeral_enc_pubkey, &service_ephemeral_rend_keypair->pubkey, rend_secret_hs_input); /* Get NTOR_KEY_SEED and AUTH_INPUT_MAC! */ bad |= get_rendezvous1_key_material(rend_secret_hs_input, intro_auth_pubkey, &intro_enc_keypair->pubkey, &service_ephemeral_rend_keypair->pubkey, client_ephemeral_enc_pubkey, hs_ntor_rend_cell_keys_out); memwipe(rend_secret_hs_input, 0, sizeof(rend_secret_hs_input)); if (bad) { memwipe(hs_ntor_rend_cell_keys_out, 0, sizeof(hs_ntor_rend_cell_keys_t)); } return bad ? -1 : 0; } /** Given a received RENDEZVOUS2 MAC in mac (of length DIGEST256_LEN), * and the RENDEZVOUS1 key material in hs_ntor_rend_cell_keys, return 1 * if the MAC is good, otherwise return 0. */ 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) { tor_assert(rcvd_mac); tor_assert(hs_ntor_rend_cell_keys); return tor_memeq(hs_ntor_rend_cell_keys->rend_cell_auth_mac, rcvd_mac, DIGEST256_LEN); } /* Input length to KDF for key expansion */ #define NTOR_KEY_EXPANSION_KDF_INPUT_LEN (DIGEST256_LEN + M_HSEXPAND_LEN) /** Given the rendezvous key seed in ntor_key_seed (of size * DIGEST256_LEN), do the circuit key expansion as specified by section * '4.2.1. Key expansion' and place the keys in keys_out (which must be * of size HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN). * * Return 0 if things went well, else return -1. */ 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) { uint8_t *ptr; uint8_t kdf_input[NTOR_KEY_EXPANSION_KDF_INPUT_LEN]; crypto_xof_t *xof; /* Sanity checks on lengths to make sure we are good */ if (BUG(seed_len != DIGEST256_LEN)) { return -1; } if (BUG(keys_out_len != HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN)) { return -1; } /* Let's build the input to the KDF */ ptr = kdf_input; APPEND(ptr, ntor_key_seed, DIGEST256_LEN); APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND)); tor_assert(ptr == kdf_input + sizeof(kdf_input)); /* Generate the keys */ xof = crypto_xof_new(); crypto_xof_add_bytes(xof, kdf_input, sizeof(kdf_input)); crypto_xof_squeeze_bytes(xof, keys_out, HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN); crypto_xof_free(xof); return 0; }