mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-27 13:53:31 +01:00
Merge branch 'asn/prop224-ntor-v2-squashed'
This commit is contained in:
commit
755c88a474
2
.gitignore
vendored
2
.gitignore
vendored
@ -181,6 +181,7 @@ uptime-*.json
|
||||
/src/test/test-child
|
||||
/src/test/test-memwipe
|
||||
/src/test/test-ntor-cl
|
||||
/src/test/test-hs-ntor-cl
|
||||
/src/test/test-switch-id
|
||||
/src/test/test-timers
|
||||
/src/test/test_workqueue
|
||||
@ -189,6 +190,7 @@ uptime-*.json
|
||||
/src/test/test-bt-cl.exe
|
||||
/src/test/test-child.exe
|
||||
/src/test/test-ntor-cl.exe
|
||||
/src/test/test-hs-ntor-cl.exe
|
||||
/src/test/test-memwipe.exe
|
||||
/src/test/test-switch-id.exe
|
||||
/src/test/test-timers.exe
|
||||
|
626
src/or/hs_ntor.c
Normal file
626
src/or/hs_ntor.c
Normal file
@ -0,0 +1,626 @@
|
||||
/* 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 <b>len</b> bytes from <b>inp</b> to <b>ptr</b> and
|
||||
*advance <b>ptr</b> 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 <b>intro_auth_pubkey</b>, B is
|
||||
* <b>intro_enc_pubkey</b>, Y is <b>service_ephemeral_rend_pubkey</b>, and X
|
||||
* is <b>client_ephemeral_enc_pubkey</b>. The provided
|
||||
* <b>rend_secret_hs_input</b> is of size REND_SECRET_HS_INPUT_LEN.
|
||||
*
|
||||
* The final results of NTOR_KEY_SEED and auth_input_mac are placed in
|
||||
* <b>hs_ntor_rend_cell_keys_out</b>. 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 <b>secret_input</b> (of size
|
||||
* INTRO_SECRET_HS_INPUT_LEN), and <b>subcredential</b> is of size
|
||||
* DIGEST256_LEN.
|
||||
*
|
||||
* If everything went well, fill <b>hs_ntor_intro_cell_keys_out</b> 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 <b>secret_input_out</b>. 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, <b>dh_result</b> carries the EXP() result (and has size
|
||||
* CURVE25519_OUTPUT_LEN) <b>intro_auth_pubkey</b> is AUTH_KEY,
|
||||
* <b>client_ephemeral_enc_pubkey</b> is X, and <b>intro_enc_pubkey</b> 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 <b>rend_secret_hs_input_out</b>. 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:
|
||||
* <b>dh_result1</b> and <b>dh_result2</b> carry the two EXP() results (of size
|
||||
* CURVE25519_OUTPUT_LEN)
|
||||
* <b>intro_auth_pubkey</b> is AUTH_KEY,
|
||||
* <b>intro_enc_pubkey</b> is B,
|
||||
* <b>client_ephemeral_enc_pubkey</b> is X, and
|
||||
* <b>service_ephemeral_rend_pubkey</b> 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 <b>hs_ntor_intro_cell_keys_out</b> 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:
|
||||
* <b>intro_auth_pubkey</b> is AUTH_KEY (found in HS descriptor),
|
||||
* <b>intro_enc_pubkey</b> is B (also found in HS descriptor),
|
||||
* <b>client_ephemeral_enc_keypair</b> is freshly generated keypair (x,X)
|
||||
* <b>subcredential</b> 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
|
||||
* <b>hs_ntor_rend_cell_keys_out</b> 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:
|
||||
* <b>intro_auth_pubkey</b> is AUTH_KEY (found in HS descriptor),
|
||||
* <b>client_ephemeral_enc_keypair</b> is freshly generated keypair (x,X)
|
||||
* <b>intro_enc_pubkey</b> is B (also found in HS descriptor),
|
||||
* <b>service_ephemeral_rend_pubkey</b> 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 <b>hs_ntor_intro_cell_keys_out</b> 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:
|
||||
* <b>intro_auth_pubkey</b> is AUTH_KEY (introduction point auth key),
|
||||
* <b>intro_enc_keypair</b> is (b,B) (introduction point encryption keypair),
|
||||
* <b>client_ephemeral_enc_pubkey</b> is X (CLIENT_PK in INTRODUCE2 cell),
|
||||
* <b>subcredential</b> 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 <b>hs_ntor_rend_cell_keys_out</b> 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:
|
||||
* <b>intro_auth_pubkey</b> is AUTH_KEY (intro point auth key),
|
||||
* <b>intro_enc_keypair</b> is (b,B) (intro point enc keypair)
|
||||
* <b>service_ephemeral_rend_keypair</b> is a fresh (y,Y) keypair
|
||||
* <b>client_ephemeral_enc_pubkey</b> 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 <b>mac</b> (of length DIGEST256_LEN),
|
||||
* and the RENDEZVOUS1 key material in <b>hs_ntor_rend_cell_keys</b>, 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)
|
||||
/* Output length of KDF for key expansion */
|
||||
#define NTOR_KEY_EXPANSION_KDF_OUTPUT_LEN (DIGEST256_LEN*3+CIPHER256_KEY_LEN*2)
|
||||
|
||||
/** Given the rendezvous key material in <b>hs_ntor_rend_cell_keys</b>, do the
|
||||
* circuit key expansion as specified by section '4.2.1. Key expansion' and
|
||||
* return a hs_ntor_rend_circuit_keys_t structure with the computed keys. */
|
||||
hs_ntor_rend_circuit_keys_t *
|
||||
hs_ntor_circuit_key_expansion(
|
||||
const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys)
|
||||
{
|
||||
uint8_t *ptr;
|
||||
uint8_t kdf_input[NTOR_KEY_EXPANSION_KDF_INPUT_LEN];
|
||||
uint8_t keys[NTOR_KEY_EXPANSION_KDF_OUTPUT_LEN];
|
||||
crypto_xof_t *xof;
|
||||
hs_ntor_rend_circuit_keys_t *rend_circuit_keys = NULL;
|
||||
|
||||
/* Let's build the input to the KDF */
|
||||
ptr = kdf_input;
|
||||
APPEND(ptr, hs_ntor_rend_cell_keys->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, sizeof(keys));
|
||||
crypto_xof_free(xof);
|
||||
|
||||
/* Generate keys structure and assign keys to it */
|
||||
rend_circuit_keys = tor_malloc_zero(sizeof(hs_ntor_rend_circuit_keys_t));
|
||||
ptr = keys;
|
||||
memcpy(rend_circuit_keys->KH, ptr, DIGEST256_LEN);
|
||||
ptr += DIGEST256_LEN;;
|
||||
memcpy(rend_circuit_keys->Df, ptr, DIGEST256_LEN);
|
||||
ptr += DIGEST256_LEN;
|
||||
memcpy(rend_circuit_keys->Db, ptr, DIGEST256_LEN);
|
||||
ptr += DIGEST256_LEN;
|
||||
memcpy(rend_circuit_keys->Kf, ptr, CIPHER256_KEY_LEN);
|
||||
ptr += CIPHER256_KEY_LEN;
|
||||
memcpy(rend_circuit_keys->Kb, ptr, CIPHER256_KEY_LEN);
|
||||
ptr += CIPHER256_KEY_LEN;
|
||||
tor_assert(ptr == keys + sizeof(keys));
|
||||
|
||||
return rend_circuit_keys;
|
||||
}
|
||||
|
77
src/or/hs_ntor.h
Normal file
77
src/or/hs_ntor.h
Normal file
@ -0,0 +1,77 @@
|
||||
/* Copyright (c) 2017, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#ifndef TOR_HS_NTOR_H
|
||||
#define TOR_HS_NTOR_H
|
||||
|
||||
#include "or.h"
|
||||
|
||||
/* Key material needed to encode/decode INTRODUCE1 cells */
|
||||
typedef struct {
|
||||
/* Key used for encryption of encrypted INTRODUCE1 blob */
|
||||
uint8_t enc_key[CIPHER256_KEY_LEN];
|
||||
/* MAC key used to protect encrypted INTRODUCE1 blob */
|
||||
uint8_t mac_key[DIGEST256_LEN];
|
||||
} hs_ntor_intro_cell_keys_t;
|
||||
|
||||
/* Key material needed to encode/decode RENDEZVOUS1 cells */
|
||||
typedef struct {
|
||||
/* This is the MAC of the HANDSHAKE_INFO field */
|
||||
uint8_t rend_cell_auth_mac[DIGEST256_LEN];
|
||||
/* This is the key seed used to derive further rendezvous crypto keys as
|
||||
* detailed in section 4.2.1 of rend-spec-ng.txt. */
|
||||
uint8_t ntor_key_seed[DIGEST256_LEN];
|
||||
} hs_ntor_rend_cell_keys_t;
|
||||
|
||||
/* Key material resulting from key expansion as detailed in section "4.2.1. Key
|
||||
* expansion" of rend-spec-ng.txt. */
|
||||
typedef struct {
|
||||
/* Per-circuit key material used in ESTABLISH_INTRO cell */
|
||||
uint8_t KH[DIGEST256_LEN];
|
||||
/* Authentication key for outgoing RELAY cells */
|
||||
uint8_t Df[DIGEST256_LEN];
|
||||
/* Authentication key for incoming RELAY cells */
|
||||
uint8_t Db[DIGEST256_LEN];
|
||||
/* Encryption key for outgoing RELAY cells */
|
||||
uint8_t Kf[CIPHER256_KEY_LEN];
|
||||
/* Decryption key for incoming RELAY cells */
|
||||
uint8_t Kb[CIPHER256_KEY_LEN];
|
||||
} hs_ntor_rend_circuit_keys_t;
|
||||
|
||||
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 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 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 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);
|
||||
|
||||
hs_ntor_rend_circuit_keys_t *hs_ntor_circuit_key_expansion(
|
||||
const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys);
|
||||
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
@ -50,6 +50,7 @@ LIBTOR_A_SOURCES = \
|
||||
src/or/geoip.c \
|
||||
src/or/hs_intropoint.c \
|
||||
src/or/hs_circuitmap.c \
|
||||
src/or/hs_ntor.c \
|
||||
src/or/hs_service.c \
|
||||
src/or/entrynodes.c \
|
||||
src/or/ext_orport.c \
|
||||
@ -175,6 +176,7 @@ ORHEADERS = \
|
||||
src/or/hs_descriptor.h \
|
||||
src/or/hs_intropoint.h \
|
||||
src/or/hs_circuitmap.h \
|
||||
src/or/hs_ntor.h \
|
||||
src/or/hs_service.h \
|
||||
src/or/keypin.h \
|
||||
src/or/main.h \
|
||||
|
408
src/test/hs_ntor_ref.py
Normal file
408
src/test/hs_ntor_ref.py
Normal file
@ -0,0 +1,408 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright 2017, The Tor Project, Inc
|
||||
# See LICENSE for licensing information
|
||||
|
||||
"""
|
||||
hs_ntor_ref.py
|
||||
|
||||
This module is a reference implementation of the modified ntor protocol
|
||||
proposed for Tor hidden services in proposal 224 (Next Generation Hidden
|
||||
Services) in section [NTOR-WITH-EXTRA-DATA].
|
||||
|
||||
The modified ntor protocol is a single-round protocol, with three steps in total:
|
||||
|
||||
1: Client generates keys and sends them to service via INTRODUCE cell
|
||||
|
||||
2: Service computes key material based on client's keys, and sends its own
|
||||
keys to client via RENDEZVOUS cell
|
||||
|
||||
3: Client computes key material as well.
|
||||
|
||||
It's meant to be used to validate Tor's HS ntor implementation by conducting
|
||||
various integration tests. Specifically it conducts the following three tests:
|
||||
|
||||
- Tests our Python implementation by running the whole protocol in Python and
|
||||
making sure that results are consistent.
|
||||
|
||||
- Tests little-t-tor ntor implementation. We use this Python code to instrument
|
||||
little-t-tor and carry out the handshake by using little-t-tor code. The
|
||||
small C wrapper at src/test/test-hs-ntor-cl is used for this Python module to
|
||||
interface with little-t-tor.
|
||||
|
||||
- Cross-tests Python and little-t-tor implementation by running half of the
|
||||
protocol in Python code and the other in little-t-tor. This is actually two
|
||||
tests so that all parts of the protocol are run both by little-t-tor and
|
||||
Python.
|
||||
|
||||
It requires the curve25519 python module from the curve25519-donna package.
|
||||
|
||||
The whole logic and concept for this test suite was taken from ntor_ref.py.
|
||||
|
||||
*** DO NOT USE THIS IN PRODUCTION. ***
|
||||
"""
|
||||
|
||||
import struct
|
||||
import os, sys
|
||||
import binascii
|
||||
import subprocess
|
||||
|
||||
try:
|
||||
import curve25519
|
||||
curve25519mod = curve25519.keys
|
||||
except ImportError:
|
||||
curve25519 = None
|
||||
import slownacl_curve25519
|
||||
curve25519mod = slownacl_curve25519
|
||||
|
||||
try:
|
||||
import sha3
|
||||
except ImportError:
|
||||
# error code 77 tells automake to skip this test
|
||||
sys.exit(77)
|
||||
|
||||
# Import Nick's ntor reference implementation in Python
|
||||
# We are gonna use a few of its utilities.
|
||||
from ntor_ref import hash_nil
|
||||
from ntor_ref import PrivateKey
|
||||
|
||||
# String constants used in this protocol
|
||||
PROTOID = "tor-hs-ntor-curve25519-sha3-256-1"
|
||||
T_HSENC = PROTOID + ":hs_key_extract"
|
||||
T_HSVERIFY = PROTOID + ":hs_verify"
|
||||
T_HSMAC = PROTOID + ":hs_mac"
|
||||
M_HSEXPAND = PROTOID + ":hs_key_expand"
|
||||
|
||||
INTRO_SECRET_LEN = 161
|
||||
REND_SECRET_LEN = 225
|
||||
AUTH_INPUT_LEN = 199
|
||||
|
||||
# Implements MAC(k,m) = H(htonll(len(k)) | k | m)
|
||||
def mac(k,m):
|
||||
def htonll(num):
|
||||
return struct.pack('!q', num)
|
||||
|
||||
s = sha3.SHA3256()
|
||||
s.update(htonll(len(k)))
|
||||
s.update(k)
|
||||
s.update(m)
|
||||
return s.digest()
|
||||
|
||||
######################################################################
|
||||
|
||||
# Functions that implement the modified HS ntor protocol
|
||||
|
||||
"""As client compute key material for INTRODUCE cell 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]
|
||||
"""
|
||||
def intro2_ntor_client(intro_auth_pubkey_str, intro_enc_pubkey,
|
||||
client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, subcredential):
|
||||
|
||||
dh_result = client_ephemeral_enc_privkey.get_shared_key(intro_enc_pubkey, hash_nil)
|
||||
secret = dh_result + intro_auth_pubkey_str + client_ephemeral_enc_pubkey.serialize() + intro_enc_pubkey.serialize() + PROTOID
|
||||
assert(len(secret) == INTRO_SECRET_LEN)
|
||||
info = M_HSEXPAND + subcredential
|
||||
|
||||
kdf = sha3.SHAKE256()
|
||||
kdf.update(secret + T_HSENC + info)
|
||||
key_material = kdf.squeeze(64*8)
|
||||
|
||||
enc_key = key_material[0:32]
|
||||
mac_key = key_material[32:64]
|
||||
|
||||
return enc_key, mac_key
|
||||
|
||||
"""Wrapper over intro2_ntor_client()"""
|
||||
def client_part1(intro_auth_pubkey_str, intro_enc_pubkey,
|
||||
client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, subcredential):
|
||||
enc_key, mac_key = intro2_ntor_client(intro_auth_pubkey_str, intro_enc_pubkey, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, subcredential)
|
||||
assert(enc_key)
|
||||
assert(mac_key)
|
||||
|
||||
return enc_key, mac_key
|
||||
|
||||
"""As service compute key material for INTRODUCE cell 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]
|
||||
"""
|
||||
def intro2_ntor_service(intro_auth_pubkey_str, client_enc_pubkey, service_enc_privkey, service_enc_pubkey, subcredential):
|
||||
dh_result = service_enc_privkey.get_shared_key(client_enc_pubkey, hash_nil)
|
||||
secret = dh_result + intro_auth_pubkey_str + client_enc_pubkey.serialize() + service_enc_pubkey.serialize() + PROTOID
|
||||
assert(len(secret) == INTRO_SECRET_LEN)
|
||||
info = M_HSEXPAND + subcredential
|
||||
|
||||
kdf = sha3.SHAKE256()
|
||||
kdf.update(secret + T_HSENC + info)
|
||||
key_material = kdf.squeeze(64*8)
|
||||
|
||||
enc_key = key_material[0:32]
|
||||
mac_key = key_material[32:64]
|
||||
|
||||
return enc_key, mac_key
|
||||
|
||||
"""As service compute key material for INTRODUCE and REDNEZVOUS cells.
|
||||
|
||||
Use intro2_ntor_service() to calculate the INTRODUCE key material, and use
|
||||
the following computations to do the RENDEZVOUS ones:
|
||||
|
||||
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)
|
||||
"""
|
||||
def service_part1(intro_auth_pubkey_str, client_enc_pubkey, intro_enc_privkey, intro_enc_pubkey, subcredential):
|
||||
intro_enc_key, intro_mac_key = intro2_ntor_service(intro_auth_pubkey_str, client_enc_pubkey, intro_enc_privkey, intro_enc_pubkey, subcredential)
|
||||
assert(intro_enc_key)
|
||||
assert(intro_mac_key)
|
||||
|
||||
service_ephemeral_privkey = PrivateKey()
|
||||
service_ephemeral_pubkey = service_ephemeral_privkey.get_public()
|
||||
|
||||
dh_result1 = service_ephemeral_privkey.get_shared_key(client_enc_pubkey, hash_nil)
|
||||
dh_result2 = intro_enc_privkey.get_shared_key(client_enc_pubkey, hash_nil)
|
||||
rend_secret_hs_input = dh_result1 + dh_result2 + intro_auth_pubkey_str + intro_enc_pubkey.serialize() + client_enc_pubkey.serialize() + service_ephemeral_pubkey.serialize() + PROTOID
|
||||
assert(len(rend_secret_hs_input) == REND_SECRET_LEN)
|
||||
|
||||
ntor_key_seed = mac(rend_secret_hs_input, T_HSENC)
|
||||
verify = mac(rend_secret_hs_input, T_HSVERIFY)
|
||||
auth_input = verify + intro_auth_pubkey_str + intro_enc_pubkey.serialize() + service_ephemeral_pubkey.serialize() + client_enc_pubkey.serialize() + PROTOID + "Server"
|
||||
assert(len(auth_input) == AUTH_INPUT_LEN)
|
||||
auth_input_mac = mac(auth_input, T_HSMAC)
|
||||
|
||||
assert(ntor_key_seed)
|
||||
assert(auth_input_mac)
|
||||
assert(service_ephemeral_pubkey)
|
||||
|
||||
return intro_enc_key, intro_mac_key, ntor_key_seed, auth_input_mac, service_ephemeral_pubkey
|
||||
|
||||
"""As client compute key material for rendezvous cells as follows:
|
||||
|
||||
rend_secret_hs_input = EXP(Y,x) | EXP(B,x) | AUTH_KEY | B | X | Y | PROTOID
|
||||
NTOR_KEY_SEED = MAC(ntor_secret_input, t_hsenc)
|
||||
verify = MAC(ntor_secret_input, t_hsverify)
|
||||
auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server"
|
||||
AUTH_INPUT_MAC = MAC(auth_input, t_hsmac)
|
||||
"""
|
||||
def client_part2(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey,
|
||||
intro_enc_pubkey, service_ephemeral_rend_pubkey):
|
||||
dh_result1 = client_ephemeral_enc_privkey.get_shared_key(service_ephemeral_rend_pubkey, hash_nil)
|
||||
dh_result2 = client_ephemeral_enc_privkey.get_shared_key(intro_enc_pubkey, hash_nil)
|
||||
rend_secret_hs_input = dh_result1 + dh_result2 + intro_auth_pubkey_str + intro_enc_pubkey.serialize() + client_ephemeral_enc_pubkey.serialize() + service_ephemeral_rend_pubkey.serialize() + PROTOID
|
||||
assert(len(rend_secret_hs_input) == REND_SECRET_LEN)
|
||||
|
||||
ntor_key_seed = mac(rend_secret_hs_input, T_HSENC)
|
||||
verify = mac(rend_secret_hs_input, T_HSVERIFY)
|
||||
auth_input = verify + intro_auth_pubkey_str + intro_enc_pubkey.serialize() + service_ephemeral_rend_pubkey.serialize() + client_ephemeral_enc_pubkey.serialize() + PROTOID + "Server"
|
||||
assert(len(auth_input) == AUTH_INPUT_LEN)
|
||||
auth_input_mac = mac(auth_input, T_HSMAC)
|
||||
|
||||
assert(ntor_key_seed)
|
||||
assert(auth_input_mac)
|
||||
|
||||
return ntor_key_seed, auth_input_mac
|
||||
|
||||
#################################################################################
|
||||
|
||||
"""
|
||||
Utilities for communicating with the little-t-tor ntor wrapper to conduct the
|
||||
integration tests
|
||||
"""
|
||||
|
||||
PROG = b"./src/test/test-hs-ntor-cl"
|
||||
enhex=lambda s: binascii.b2a_hex(s)
|
||||
dehex=lambda s: binascii.a2b_hex(s.strip())
|
||||
|
||||
def tor_client1(intro_auth_pubkey_str, intro_enc_pubkey,
|
||||
client_ephemeral_enc_privkey, subcredential):
|
||||
p = subprocess.Popen([PROG, "client1",
|
||||
enhex(intro_auth_pubkey_str),
|
||||
enhex(intro_enc_pubkey.serialize()),
|
||||
enhex(client_ephemeral_enc_privkey.serialize()),
|
||||
enhex(subcredential)],
|
||||
stdout=subprocess.PIPE)
|
||||
return map(dehex, p.stdout.readlines())
|
||||
|
||||
def tor_server1(intro_auth_pubkey_str, intro_enc_privkey,
|
||||
client_ephemeral_enc_pubkey, subcredential):
|
||||
p = subprocess.Popen([PROG, "server1",
|
||||
enhex(intro_auth_pubkey_str),
|
||||
enhex(intro_enc_privkey.serialize()),
|
||||
enhex(client_ephemeral_enc_pubkey.serialize()),
|
||||
enhex(subcredential)],
|
||||
stdout=subprocess.PIPE)
|
||||
return map(dehex, p.stdout.readlines())
|
||||
|
||||
def tor_client2(intro_auth_pubkey_str, client_ephemeral_enc_privkey,
|
||||
intro_enc_pubkey, service_ephemeral_rend_pubkey, subcredential):
|
||||
p = subprocess.Popen([PROG, "client2",
|
||||
enhex(intro_auth_pubkey_str),
|
||||
enhex(client_ephemeral_enc_privkey.serialize()),
|
||||
enhex(intro_enc_pubkey.serialize()),
|
||||
enhex(service_ephemeral_rend_pubkey.serialize()),
|
||||
enhex(subcredential)],
|
||||
stdout=subprocess.PIPE)
|
||||
return map(dehex, p.stdout.readlines())
|
||||
|
||||
##################################################################################
|
||||
|
||||
# Perform a pure python ntor test
|
||||
def do_pure_python_ntor_test():
|
||||
# Initialize all needed key material
|
||||
client_ephemeral_enc_privkey = PrivateKey()
|
||||
client_ephemeral_enc_pubkey = client_ephemeral_enc_privkey.get_public()
|
||||
intro_enc_privkey = PrivateKey()
|
||||
intro_enc_pubkey = intro_enc_privkey.get_public()
|
||||
intro_auth_pubkey_str = os.urandom(32)
|
||||
subcredential = os.urandom(32)
|
||||
|
||||
client_enc_key, client_mac_key = client_part1(intro_auth_pubkey_str, intro_enc_pubkey, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey, subcredential)
|
||||
|
||||
service_enc_key, service_mac_key, service_ntor_key_seed, service_auth_input_mac, service_ephemeral_pubkey = service_part1(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, intro_enc_privkey, intro_enc_pubkey, subcredential)
|
||||
|
||||
assert(client_enc_key == service_enc_key)
|
||||
assert(client_mac_key == service_mac_key)
|
||||
|
||||
client_ntor_key_seed, client_auth_input_mac = client_part2(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey,
|
||||
intro_enc_pubkey, service_ephemeral_pubkey)
|
||||
|
||||
assert(client_ntor_key_seed == service_ntor_key_seed)
|
||||
assert(client_auth_input_mac == service_auth_input_mac)
|
||||
|
||||
print "DONE: python dance [%s]" % repr(client_auth_input_mac)
|
||||
|
||||
# Perform a pure little-t-tor integration test.
|
||||
def do_little_t_tor_ntor_test():
|
||||
# Initialize all needed key material
|
||||
subcredential = os.urandom(32)
|
||||
client_ephemeral_enc_privkey = PrivateKey()
|
||||
client_ephemeral_enc_pubkey = client_ephemeral_enc_privkey.get_public()
|
||||
intro_enc_privkey = PrivateKey()
|
||||
intro_enc_pubkey = intro_enc_privkey.get_public() # service-side enc key
|
||||
intro_auth_pubkey_str = os.urandom(32)
|
||||
|
||||
client_enc_key, client_mac_key = tor_client1(intro_auth_pubkey_str, intro_enc_pubkey,
|
||||
client_ephemeral_enc_privkey, subcredential)
|
||||
assert(client_enc_key)
|
||||
assert(client_mac_key)
|
||||
|
||||
service_enc_key, service_mac_key, service_ntor_auth_mac, service_ntor_key_seed, service_eph_pubkey = tor_server1(intro_auth_pubkey_str,
|
||||
intro_enc_privkey,
|
||||
client_ephemeral_enc_pubkey,
|
||||
subcredential)
|
||||
assert(service_enc_key)
|
||||
assert(service_mac_key)
|
||||
assert(service_ntor_auth_mac)
|
||||
assert(service_ntor_key_seed)
|
||||
|
||||
assert(client_enc_key == service_enc_key)
|
||||
assert(client_mac_key == service_mac_key)
|
||||
|
||||
# Turn from bytes to key
|
||||
service_eph_pubkey = curve25519mod.Public(service_eph_pubkey)
|
||||
|
||||
client_ntor_auth_mac, client_ntor_key_seed = tor_client2(intro_auth_pubkey_str, client_ephemeral_enc_privkey,
|
||||
intro_enc_pubkey, service_eph_pubkey, subcredential)
|
||||
assert(client_ntor_auth_mac)
|
||||
assert(client_ntor_key_seed)
|
||||
|
||||
assert(client_ntor_key_seed == service_ntor_key_seed)
|
||||
assert(client_ntor_auth_mac == service_ntor_auth_mac)
|
||||
|
||||
print "DONE: tor dance [%s]" % repr(client_ntor_auth_mac)
|
||||
|
||||
"""
|
||||
Do mixed test as follows:
|
||||
1. C -> S (python mode)
|
||||
2. C <- S (tor mode)
|
||||
3. Client computes keys (python mode)
|
||||
"""
|
||||
def do_first_mixed_test():
|
||||
subcredential = os.urandom(32)
|
||||
|
||||
client_ephemeral_enc_privkey = PrivateKey()
|
||||
client_ephemeral_enc_pubkey = client_ephemeral_enc_privkey.get_public()
|
||||
intro_enc_privkey = PrivateKey()
|
||||
intro_enc_pubkey = intro_enc_privkey.get_public() # service-side enc key
|
||||
|
||||
intro_auth_pubkey_str = os.urandom(32)
|
||||
|
||||
# Let's do mixed
|
||||
client_enc_key, client_mac_key = client_part1(intro_auth_pubkey_str, intro_enc_pubkey,
|
||||
client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey,
|
||||
subcredential)
|
||||
|
||||
service_enc_key, service_mac_key, service_ntor_auth_mac, service_ntor_key_seed, service_eph_pubkey = tor_server1(intro_auth_pubkey_str,
|
||||
intro_enc_privkey,
|
||||
client_ephemeral_enc_pubkey,
|
||||
subcredential)
|
||||
assert(service_enc_key)
|
||||
assert(service_mac_key)
|
||||
assert(service_ntor_auth_mac)
|
||||
assert(service_ntor_key_seed)
|
||||
assert(service_eph_pubkey)
|
||||
|
||||
assert(client_enc_key == service_enc_key)
|
||||
assert(client_mac_key == service_mac_key)
|
||||
|
||||
# Turn from bytes to key
|
||||
service_eph_pubkey = curve25519mod.Public(service_eph_pubkey)
|
||||
|
||||
client_ntor_key_seed, client_auth_input_mac = client_part2(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, client_ephemeral_enc_privkey,
|
||||
intro_enc_pubkey, service_eph_pubkey)
|
||||
|
||||
assert(client_auth_input_mac == service_ntor_auth_mac)
|
||||
assert(client_ntor_key_seed == service_ntor_key_seed)
|
||||
|
||||
print "DONE: 1st mixed dance [%s]" % repr(client_auth_input_mac)
|
||||
|
||||
"""
|
||||
Do mixed test as follows:
|
||||
1. C -> S (tor mode)
|
||||
2. C <- S (python mode)
|
||||
3. Client computes keys (tor mode)
|
||||
"""
|
||||
def do_second_mixed_test():
|
||||
subcredential = os.urandom(32)
|
||||
|
||||
client_ephemeral_enc_privkey = PrivateKey()
|
||||
client_ephemeral_enc_pubkey = client_ephemeral_enc_privkey.get_public()
|
||||
intro_enc_privkey = PrivateKey()
|
||||
intro_enc_pubkey = intro_enc_privkey.get_public() # service-side enc key
|
||||
|
||||
intro_auth_pubkey_str = os.urandom(32)
|
||||
|
||||
# Let's do mixed
|
||||
client_enc_key, client_mac_key = tor_client1(intro_auth_pubkey_str, intro_enc_pubkey,
|
||||
client_ephemeral_enc_privkey, subcredential)
|
||||
assert(client_enc_key)
|
||||
assert(client_mac_key)
|
||||
|
||||
service_enc_key, service_mac_key, service_ntor_key_seed, service_ntor_auth_mac, service_ephemeral_pubkey = service_part1(intro_auth_pubkey_str, client_ephemeral_enc_pubkey, intro_enc_privkey, intro_enc_pubkey, subcredential)
|
||||
|
||||
client_ntor_auth_mac, client_ntor_key_seed = tor_client2(intro_auth_pubkey_str, client_ephemeral_enc_privkey,
|
||||
intro_enc_pubkey, service_ephemeral_pubkey, subcredential)
|
||||
assert(client_ntor_auth_mac)
|
||||
assert(client_ntor_key_seed)
|
||||
|
||||
assert(client_ntor_key_seed == service_ntor_key_seed)
|
||||
assert(client_ntor_auth_mac == service_ntor_auth_mac)
|
||||
|
||||
print "DONE: 2nd mixed dance [%s]" % repr(client_ntor_auth_mac)
|
||||
|
||||
def do_mixed_tests():
|
||||
do_first_mixed_test()
|
||||
do_second_mixed_test()
|
||||
|
||||
if __name__ == '__main__':
|
||||
do_pure_python_ntor_test()
|
||||
do_little_t_tor_ntor_test()
|
||||
do_mixed_tests()
|
@ -20,7 +20,7 @@ TESTSCRIPTS = \
|
||||
src/test/test_switch_id.sh
|
||||
|
||||
if USEPYTHON
|
||||
TESTSCRIPTS += src/test/test_ntor.sh src/test/test_bt.sh
|
||||
TESTSCRIPTS += src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh
|
||||
endif
|
||||
|
||||
TESTS += src/test/test src/test/test-slow src/test/test-memwipe \
|
||||
@ -254,6 +254,7 @@ noinst_HEADERS+= \
|
||||
src/test/vote_descriptors.inc
|
||||
|
||||
noinst_PROGRAMS+= src/test/test-ntor-cl
|
||||
noinst_PROGRAMS+= src/test/test-hs-ntor-cl
|
||||
src_test_test_ntor_cl_SOURCES = src/test/test_ntor_cl.c
|
||||
src_test_test_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
|
||||
src_test_test_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \
|
||||
@ -264,6 +265,17 @@ src_test_test_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \
|
||||
src_test_test_ntor_cl_AM_CPPFLAGS = \
|
||||
-I"$(top_srcdir)/src/or"
|
||||
|
||||
src_test_test_hs_ntor_cl_SOURCES = src/test/test_hs_ntor_cl.c
|
||||
src_test_test_hs_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
|
||||
src_test_test_hs_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \
|
||||
src/common/libor-ctime.a \
|
||||
src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \
|
||||
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
|
||||
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
|
||||
src_test_test_hs_ntor_cl_AM_CPPFLAGS = \
|
||||
-I"$(top_srcdir)/src/or"
|
||||
|
||||
|
||||
noinst_PROGRAMS += src/test/test-bt-cl
|
||||
src_test_test_bt_cl_SOURCES = src/test/test_bt_cl.c
|
||||
src_test_test_bt_cl_LDADD = src/common/libor-testing.a \
|
||||
@ -276,12 +288,13 @@ src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS)
|
||||
EXTRA_DIST += \
|
||||
src/test/bt_test.py \
|
||||
src/test/ntor_ref.py \
|
||||
src/test/hs_ntor_ref.py \
|
||||
src/test/fuzz_static_testcases.sh \
|
||||
src/test/slownacl_curve25519.py \
|
||||
src/test/zero_length_keys.sh \
|
||||
src/test/test_keygen.sh \
|
||||
src/test/test_zero_length_keys.sh \
|
||||
src/test/test_ntor.sh src/test/test_bt.sh \
|
||||
src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh \
|
||||
src/test/test-network.sh \
|
||||
src/test/test_switch_id.sh \
|
||||
src/test/test_workqueue_cancel.sh \
|
||||
|
11
src/test/test_hs_ntor.sh
Executable file
11
src/test/test_hs_ntor.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
# Validate Tor's ntor implementation.
|
||||
|
||||
exitcode=0
|
||||
|
||||
# Run the python integration test sand return the exitcode of the python
|
||||
# script. The python script might ask the testsuite to skip it if not all
|
||||
# python dependencies are covered.
|
||||
"${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/hs_ntor_ref.py" || exitcode=$?
|
||||
|
||||
exit ${exitcode}
|
255
src/test/test_hs_ntor_cl.c
Normal file
255
src/test/test_hs_ntor_cl.c
Normal file
@ -0,0 +1,255 @@
|
||||
/* Copyright (c) 2017, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/** This is a wrapper over the little-t-tor HS ntor functions. The wrapper is
|
||||
* used by src/test/hs_ntor_ref.py to conduct the HS ntor integration
|
||||
* tests.
|
||||
*
|
||||
* The logic of this wrapper is basically copied from src/test/test_ntor_cl.c
|
||||
*/
|
||||
|
||||
#include "orconfig.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ONION_NTOR_PRIVATE
|
||||
#include "or.h"
|
||||
#include "util.h"
|
||||
#include "compat.h"
|
||||
#include "crypto.h"
|
||||
#include "crypto_curve25519.h"
|
||||
#include "hs_ntor.h"
|
||||
#include "onion_ntor.h"
|
||||
|
||||
#define N_ARGS(n) STMT_BEGIN { \
|
||||
if (argc < (n)) { \
|
||||
fprintf(stderr, "%s needs %d arguments.\n",argv[1],n); \
|
||||
return 1; \
|
||||
} \
|
||||
} STMT_END
|
||||
#define BASE16(idx, var, n) STMT_BEGIN { \
|
||||
const char *s = argv[(idx)]; \
|
||||
if (base16_decode((char*)var, n, s, strlen(s)) < (int)n ) { \
|
||||
fprintf(stderr, "couldn't decode argument %d (%s)\n",idx,s); \
|
||||
return 1; \
|
||||
} \
|
||||
} STMT_END
|
||||
#define INT(idx, var) STMT_BEGIN { \
|
||||
var = atoi(argv[(idx)]); \
|
||||
if (var <= 0) { \
|
||||
fprintf(stderr, "bad integer argument %d (%s)\n",idx,argv[(idx)]); \
|
||||
} \
|
||||
} STMT_END
|
||||
|
||||
/** The first part of the HS ntor protocol. The client-side computes all
|
||||
necessary key material and sends the appropriate message to the service. */
|
||||
static int
|
||||
client1(int argc, char **argv)
|
||||
{
|
||||
int retval;
|
||||
|
||||
/* Inputs */
|
||||
curve25519_public_key_t intro_enc_pubkey;
|
||||
ed25519_public_key_t intro_auth_pubkey;
|
||||
curve25519_keypair_t client_ephemeral_enc_keypair;
|
||||
uint8_t subcredential[DIGEST256_LEN];
|
||||
|
||||
/* Output */
|
||||
hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys;
|
||||
|
||||
char buf[256];
|
||||
|
||||
N_ARGS(6);
|
||||
BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN);
|
||||
BASE16(3, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
|
||||
BASE16(4, client_ephemeral_enc_keypair.seckey.secret_key,
|
||||
CURVE25519_SECKEY_LEN);
|
||||
BASE16(5, subcredential, DIGEST256_LEN);
|
||||
|
||||
/* Generate keypair */
|
||||
curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey,
|
||||
&client_ephemeral_enc_keypair.seckey);
|
||||
|
||||
retval = hs_ntor_client_get_introduce1_keys(&intro_auth_pubkey,
|
||||
&intro_enc_pubkey,
|
||||
&client_ephemeral_enc_keypair,
|
||||
subcredential,
|
||||
&hs_ntor_intro_cell_keys);
|
||||
if (retval < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Send ENC_KEY */
|
||||
base16_encode(buf, sizeof(buf),
|
||||
(const char*)hs_ntor_intro_cell_keys.enc_key,
|
||||
sizeof(hs_ntor_intro_cell_keys.enc_key));
|
||||
printf("%s\n", buf);
|
||||
/* Send MAC_KEY */
|
||||
base16_encode(buf, sizeof(buf),
|
||||
(const char*)hs_ntor_intro_cell_keys.mac_key,
|
||||
sizeof(hs_ntor_intro_cell_keys.mac_key));
|
||||
printf("%s\n", buf);
|
||||
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/** The second part of the HS ntor protocol. The service-side computes all
|
||||
necessary key material and sends the appropriate message to the client */
|
||||
static int
|
||||
server1(int argc, char **argv)
|
||||
{
|
||||
int retval;
|
||||
|
||||
/* Inputs */
|
||||
curve25519_keypair_t intro_enc_keypair;
|
||||
ed25519_public_key_t intro_auth_pubkey;
|
||||
curve25519_public_key_t client_ephemeral_enc_pubkey;
|
||||
uint8_t subcredential[DIGEST256_LEN];
|
||||
|
||||
/* Output */
|
||||
hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys;
|
||||
hs_ntor_rend_cell_keys_t hs_ntor_rend_cell_keys;
|
||||
curve25519_keypair_t service_ephemeral_rend_keypair;
|
||||
|
||||
char buf[256];
|
||||
|
||||
N_ARGS(6);
|
||||
BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN);
|
||||
BASE16(3, intro_enc_keypair.seckey.secret_key, CURVE25519_SECKEY_LEN);
|
||||
BASE16(4, client_ephemeral_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
|
||||
BASE16(5, subcredential, DIGEST256_LEN);
|
||||
|
||||
/* Generate keypair */
|
||||
curve25519_public_key_generate(&intro_enc_keypair.pubkey,
|
||||
&intro_enc_keypair.seckey);
|
||||
curve25519_keypair_generate(&service_ephemeral_rend_keypair, 0);
|
||||
|
||||
/* Get INTRODUCE1 keys */
|
||||
retval = hs_ntor_service_get_introduce1_keys(&intro_auth_pubkey,
|
||||
&intro_enc_keypair,
|
||||
&client_ephemeral_enc_pubkey,
|
||||
subcredential,
|
||||
&hs_ntor_intro_cell_keys);
|
||||
if (retval < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Get RENDEZVOUS1 keys */
|
||||
retval = hs_ntor_service_get_rendezvous1_keys(&intro_auth_pubkey,
|
||||
&intro_enc_keypair,
|
||||
&service_ephemeral_rend_keypair,
|
||||
&client_ephemeral_enc_pubkey,
|
||||
&hs_ntor_rend_cell_keys);
|
||||
if (retval < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Send ENC_KEY */
|
||||
base16_encode(buf, sizeof(buf),
|
||||
(const char*)hs_ntor_intro_cell_keys.enc_key,
|
||||
sizeof(hs_ntor_intro_cell_keys.enc_key));
|
||||
printf("%s\n", buf);
|
||||
/* Send MAC_KEY */
|
||||
base16_encode(buf, sizeof(buf),
|
||||
(const char*)hs_ntor_intro_cell_keys.mac_key,
|
||||
sizeof(hs_ntor_intro_cell_keys.mac_key));
|
||||
printf("%s\n", buf);
|
||||
/* Send AUTH_MAC */
|
||||
base16_encode(buf, sizeof(buf),
|
||||
(const char*)hs_ntor_rend_cell_keys.rend_cell_auth_mac,
|
||||
sizeof(hs_ntor_rend_cell_keys.rend_cell_auth_mac));
|
||||
printf("%s\n", buf);
|
||||
/* Send NTOR_KEY_SEED */
|
||||
base16_encode(buf, sizeof(buf),
|
||||
(const char*)hs_ntor_rend_cell_keys.ntor_key_seed,
|
||||
sizeof(hs_ntor_rend_cell_keys.ntor_key_seed));
|
||||
printf("%s\n", buf);
|
||||
/* Send service ephemeral pubkey (Y) */
|
||||
base16_encode(buf, sizeof(buf),
|
||||
(const char*)service_ephemeral_rend_keypair.pubkey.public_key,
|
||||
sizeof(service_ephemeral_rend_keypair.pubkey.public_key));
|
||||
printf("%s\n", buf);
|
||||
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/** The final step of the ntor protocol, the client computes and returns the
|
||||
* rendezvous key material. */
|
||||
static int
|
||||
client2(int argc, char **argv)
|
||||
{
|
||||
int retval;
|
||||
|
||||
/* Inputs */
|
||||
curve25519_public_key_t intro_enc_pubkey;
|
||||
ed25519_public_key_t intro_auth_pubkey;
|
||||
curve25519_keypair_t client_ephemeral_enc_keypair;
|
||||
curve25519_public_key_t service_ephemeral_rend_pubkey;
|
||||
uint8_t subcredential[DIGEST256_LEN];
|
||||
|
||||
/* Output */
|
||||
hs_ntor_rend_cell_keys_t hs_ntor_rend_cell_keys;
|
||||
|
||||
char buf[256];
|
||||
|
||||
N_ARGS(7);
|
||||
BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN);
|
||||
BASE16(3, client_ephemeral_enc_keypair.seckey.secret_key,
|
||||
CURVE25519_SECKEY_LEN);
|
||||
BASE16(4, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
|
||||
BASE16(5, service_ephemeral_rend_pubkey.public_key, CURVE25519_PUBKEY_LEN);
|
||||
BASE16(6, subcredential, DIGEST256_LEN);
|
||||
|
||||
/* Generate keypair */
|
||||
curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey,
|
||||
&client_ephemeral_enc_keypair.seckey);
|
||||
|
||||
/* Get RENDEZVOUS1 keys */
|
||||
retval = hs_ntor_client_get_rendezvous1_keys(&intro_auth_pubkey,
|
||||
&client_ephemeral_enc_keypair,
|
||||
&intro_enc_pubkey,
|
||||
&service_ephemeral_rend_pubkey,
|
||||
&hs_ntor_rend_cell_keys);
|
||||
if (retval < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Send AUTH_MAC */
|
||||
base16_encode(buf, sizeof(buf),
|
||||
(const char*)hs_ntor_rend_cell_keys.rend_cell_auth_mac,
|
||||
sizeof(hs_ntor_rend_cell_keys.rend_cell_auth_mac));
|
||||
printf("%s\n", buf);
|
||||
/* Send NTOR_KEY_SEED */
|
||||
base16_encode(buf, sizeof(buf),
|
||||
(const char*)hs_ntor_rend_cell_keys.ntor_key_seed,
|
||||
sizeof(hs_ntor_rend_cell_keys.ntor_key_seed));
|
||||
printf("%s\n", buf);
|
||||
|
||||
done:
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Perform a different part of the protocol depdning on the argv used. */
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "I need arguments. Read source for more info.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
curve25519_init();
|
||||
if (!strcmp(argv[1], "client1")) {
|
||||
return client1(argc, argv);
|
||||
} else if (!strcmp(argv[1], "server1")) {
|
||||
return server1(argc, argv);
|
||||
} else if (!strcmp(argv[1], "client2")) {
|
||||
return client2(argc, argv);
|
||||
} else {
|
||||
fprintf(stderr, "What's a %s?\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
#include "hs_service.h"
|
||||
#include "hs_intropoint.h"
|
||||
|
||||
#include "hs_ntor.h"
|
||||
|
||||
/** We simulate the creation of an outgoing ESTABLISH_INTRO cell, and then we
|
||||
* parse it from the receiver side. */
|
||||
static void
|
||||
@ -100,11 +102,106 @@ test_gen_establish_intro_cell_bad(void *arg)
|
||||
UNMOCK(ed25519_sign_prefixed);
|
||||
}
|
||||
|
||||
/** Test the HS ntor handshake. Simulate the sending of an encrypted INTRODUCE1
|
||||
* cell, and verify the proper derivation of decryption keys on the other end.
|
||||
* Then simulate the sending of an authenticated RENDEZVOUS1 cell and verify
|
||||
* the proper verification on the other end. */
|
||||
static void
|
||||
test_hs_ntor(void *arg)
|
||||
{
|
||||
int retval;
|
||||
|
||||
uint8_t subcredential[DIGEST256_LEN];
|
||||
|
||||
ed25519_keypair_t service_intro_auth_keypair;
|
||||
curve25519_keypair_t service_intro_enc_keypair;
|
||||
curve25519_keypair_t service_ephemeral_rend_keypair;
|
||||
|
||||
curve25519_keypair_t client_ephemeral_enc_keypair;
|
||||
|
||||
hs_ntor_intro_cell_keys_t client_hs_ntor_intro_cell_keys;
|
||||
hs_ntor_intro_cell_keys_t service_hs_ntor_intro_cell_keys;
|
||||
|
||||
hs_ntor_rend_cell_keys_t service_hs_ntor_rend_cell_keys;
|
||||
hs_ntor_rend_cell_keys_t client_hs_ntor_rend_cell_keys;
|
||||
|
||||
(void) arg;
|
||||
|
||||
/* Generate fake data for this unittest */
|
||||
{
|
||||
/* Generate fake subcredential */
|
||||
memset(subcredential, 'Z', DIGEST256_LEN);
|
||||
|
||||
/* service */
|
||||
curve25519_keypair_generate(&service_intro_enc_keypair, 0);
|
||||
ed25519_keypair_generate(&service_intro_auth_keypair, 0);
|
||||
curve25519_keypair_generate(&service_ephemeral_rend_keypair, 0);
|
||||
/* client */
|
||||
curve25519_keypair_generate(&client_ephemeral_enc_keypair, 0);
|
||||
}
|
||||
|
||||
/* Client: Simulate the sending of an encrypted INTRODUCE1 cell */
|
||||
retval =
|
||||
hs_ntor_client_get_introduce1_keys(&service_intro_auth_keypair.pubkey,
|
||||
&service_intro_enc_keypair.pubkey,
|
||||
&client_ephemeral_enc_keypair,
|
||||
subcredential,
|
||||
&client_hs_ntor_intro_cell_keys);
|
||||
tt_int_op(retval, ==, 0);
|
||||
|
||||
/* Service: Simulate the decryption of the received INTRODUCE1 */
|
||||
retval =
|
||||
hs_ntor_service_get_introduce1_keys(&service_intro_auth_keypair.pubkey,
|
||||
&service_intro_enc_keypair,
|
||||
&client_ephemeral_enc_keypair.pubkey,
|
||||
subcredential,
|
||||
&service_hs_ntor_intro_cell_keys);
|
||||
tt_int_op(retval, ==, 0);
|
||||
|
||||
/* Test that the INTRODUCE1 encryption/mac keys match! */
|
||||
tt_mem_op(client_hs_ntor_intro_cell_keys.enc_key, OP_EQ,
|
||||
service_hs_ntor_intro_cell_keys.enc_key,
|
||||
CIPHER256_KEY_LEN);
|
||||
tt_mem_op(client_hs_ntor_intro_cell_keys.mac_key, OP_EQ,
|
||||
service_hs_ntor_intro_cell_keys.mac_key,
|
||||
DIGEST256_LEN);
|
||||
|
||||
/* Service: Simulate creation of RENDEZVOUS1 key material. */
|
||||
retval =
|
||||
hs_ntor_service_get_rendezvous1_keys(&service_intro_auth_keypair.pubkey,
|
||||
&service_intro_enc_keypair,
|
||||
&service_ephemeral_rend_keypair,
|
||||
&client_ephemeral_enc_keypair.pubkey,
|
||||
&service_hs_ntor_rend_cell_keys);
|
||||
tt_int_op(retval, ==, 0);
|
||||
|
||||
/* Client: Simulate the verification of a received RENDEZVOUS1 cell */
|
||||
retval =
|
||||
hs_ntor_client_get_rendezvous1_keys(&service_intro_auth_keypair.pubkey,
|
||||
&client_ephemeral_enc_keypair,
|
||||
&service_intro_enc_keypair.pubkey,
|
||||
&service_ephemeral_rend_keypair.pubkey,
|
||||
&client_hs_ntor_rend_cell_keys);
|
||||
tt_int_op(retval, ==, 0);
|
||||
|
||||
/* Test that the RENDEZVOUS1 key material match! */
|
||||
tt_mem_op(client_hs_ntor_rend_cell_keys.rend_cell_auth_mac, OP_EQ,
|
||||
service_hs_ntor_rend_cell_keys.rend_cell_auth_mac,
|
||||
DIGEST256_LEN);
|
||||
tt_mem_op(client_hs_ntor_rend_cell_keys.ntor_key_seed, OP_EQ,
|
||||
service_hs_ntor_rend_cell_keys.ntor_key_seed,
|
||||
DIGEST256_LEN);
|
||||
|
||||
done:
|
||||
;
|
||||
}
|
||||
|
||||
struct testcase_t hs_service_tests[] = {
|
||||
{ "gen_establish_intro_cell", test_gen_establish_intro_cell, TT_FORK,
|
||||
NULL, NULL },
|
||||
{ "gen_establish_intro_cell_bad", test_gen_establish_intro_cell_bad, TT_FORK,
|
||||
NULL, NULL },
|
||||
{ "hs_ntor", test_hs_ntor, TT_FORK, NULL, NULL },
|
||||
|
||||
END_OF_TESTCASES
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user