/* Copyright (c) 2017-2018, 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/or.h"
#include "lib/crypt_ops/crypto_util.h"
#include "or/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;
}