Merge branch 'ticket21859_032_01_squashed'

This commit is contained in:
Nick Mathewson 2017-07-07 11:17:53 -04:00
commit c387cc5022
36 changed files with 1255 additions and 248 deletions

View File

@ -1854,6 +1854,18 @@ struct crypto_digest_t {
} d; } d;
}; };
#ifdef TOR_UNIT_TESTS
digest_algorithm_t
crypto_digest_get_algorithm(crypto_digest_t *digest)
{
tor_assert(digest);
return digest->algorithm;
}
#endif
/** /**
* Return the number of bytes we need to malloc in order to get a * Return the number of bytes we need to malloc in order to get a
* crypto_digest_t for <b>alg</b>, or the number of bytes we need to wipe * crypto_digest_t for <b>alg</b>, or the number of bytes we need to wipe

View File

@ -20,6 +20,9 @@
#include "testsupport.h" #include "testsupport.h"
#include "compat.h" #include "compat.h"
#include <openssl/engine.h>
#include "keccak-tiny/keccak-tiny.h"
/* /*
Macro to create an arbitrary OpenSSL version number as used by Macro to create an arbitrary OpenSSL version number as used by
OPENSSL_VERSION_NUMBER or SSLeay(), since the actual numbers are a bit hard OPENSSL_VERSION_NUMBER or SSLeay(), since the actual numbers are a bit hard
@ -335,6 +338,7 @@ struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh);
void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in); void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in);
#ifdef CRYPTO_PRIVATE #ifdef CRYPTO_PRIVATE
STATIC int crypto_force_rand_ssleay(void); STATIC int crypto_force_rand_ssleay(void);
STATIC int crypto_strongest_rand_raw(uint8_t *out, size_t out_len); STATIC int crypto_strongest_rand_raw(uint8_t *out, size_t out_len);
@ -346,6 +350,7 @@ extern int break_strongest_rng_fallback;
#ifdef TOR_UNIT_TESTS #ifdef TOR_UNIT_TESTS
void crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src); void crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src);
digest_algorithm_t crypto_digest_get_algorithm(crypto_digest_t *digest);
#endif #endif
#endif #endif

View File

@ -46,6 +46,7 @@
#include "crypto.h" #include "crypto.h"
#include "directory.h" #include "directory.h"
#include "entrynodes.h" #include "entrynodes.h"
#include "hs_ntor.h"
#include "main.h" #include "main.h"
#include "microdesc.h" #include "microdesc.h"
#include "networkstatus.h" #include "networkstatus.h"
@ -1370,40 +1371,76 @@ circuit_extend(cell_t *cell, circuit_t *circ)
return 0; return 0;
} }
/** Initialize cpath-\>{f|b}_{crypto|digest} from the key material in /** Initialize cpath-\>{f|b}_{crypto|digest} from the key material in key_data.
* key_data. key_data must contain CPATH_KEY_MATERIAL bytes, which are *
* used as follows: * If <b>is_hs_v3</b> is set, this cpath will be used for next gen hidden
* service circuits and <b>key_data</b> must be at least
* HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN bytes in length.
*
* If <b>is_hs_v3</b> is not set, key_data must contain CPATH_KEY_MATERIAL_LEN
* bytes, which are used as follows:
* - 20 to initialize f_digest * - 20 to initialize f_digest
* - 20 to initialize b_digest * - 20 to initialize b_digest
* - 16 to key f_crypto * - 16 to key f_crypto
* - 16 to key b_crypto * - 16 to key b_crypto
* *
* (If 'reverse' is true, then f_XX and b_XX are swapped.) * (If 'reverse' is true, then f_XX and b_XX are swapped.)
*
* Return 0 if init was successful, else -1 if it failed.
*/ */
int int
circuit_init_cpath_crypto(crypt_path_t *cpath, const char *key_data, circuit_init_cpath_crypto(crypt_path_t *cpath,
int reverse) const char *key_data, size_t key_data_len,
int reverse, int is_hs_v3)
{ {
crypto_digest_t *tmp_digest; crypto_digest_t *tmp_digest;
crypto_cipher_t *tmp_crypto; crypto_cipher_t *tmp_crypto;
size_t digest_len = 0;
size_t cipher_key_len = 0;
tor_assert(cpath); tor_assert(cpath);
tor_assert(key_data); tor_assert(key_data);
tor_assert(!(cpath->f_crypto || cpath->b_crypto || tor_assert(!(cpath->f_crypto || cpath->b_crypto ||
cpath->f_digest || cpath->b_digest)); cpath->f_digest || cpath->b_digest));
cpath->f_digest = crypto_digest_new(); /* Basic key size validation */
crypto_digest_add_bytes(cpath->f_digest, key_data, DIGEST_LEN); if (is_hs_v3 && BUG(key_data_len != HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN)) {
cpath->b_digest = crypto_digest_new(); return -1;
crypto_digest_add_bytes(cpath->b_digest, key_data+DIGEST_LEN, DIGEST_LEN); } else if (!is_hs_v3 && BUG(key_data_len != CPATH_KEY_MATERIAL_LEN)) {
return -1;
}
if (!(cpath->f_crypto = /* If we are using this cpath for next gen onion services use SHA3-256,
crypto_cipher_new(key_data+(2*DIGEST_LEN)))) { otherwise use good ol' SHA1 */
if (is_hs_v3) {
digest_len = DIGEST256_LEN;
cipher_key_len = CIPHER256_KEY_LEN;
cpath->f_digest = crypto_digest256_new(DIGEST_SHA3_256);
cpath->b_digest = crypto_digest256_new(DIGEST_SHA3_256);
} else {
digest_len = DIGEST_LEN;
cipher_key_len = CIPHER_KEY_LEN;
cpath->f_digest = crypto_digest_new();
cpath->b_digest = crypto_digest_new();
}
tor_assert(digest_len != 0);
tor_assert(cipher_key_len != 0);
crypto_digest_add_bytes(cpath->f_digest, key_data, digest_len);
crypto_digest_add_bytes(cpath->b_digest, key_data+digest_len, digest_len);
cpath->f_crypto = crypto_cipher_new_with_bits(key_data+(2*digest_len),
cipher_key_len*8);
if (!cpath->f_crypto) {
log_warn(LD_BUG,"Forward cipher initialization failed."); log_warn(LD_BUG,"Forward cipher initialization failed.");
return -1; return -1;
} }
if (!(cpath->b_crypto =
crypto_cipher_new(key_data+(2*DIGEST_LEN)+CIPHER_KEY_LEN))) { cpath->b_crypto = crypto_cipher_new_with_bits(
key_data+(2*digest_len)+cipher_key_len,
cipher_key_len*8);
if (!cpath->b_crypto) {
log_warn(LD_BUG,"Backward cipher initialization failed."); log_warn(LD_BUG,"Backward cipher initialization failed.");
return -1; return -1;
} }
@ -1469,7 +1506,7 @@ circuit_finish_handshake(origin_circuit_t *circ,
onion_handshake_state_release(&hop->handshake_state); onion_handshake_state_release(&hop->handshake_state);
if (circuit_init_cpath_crypto(hop, keys, 0)<0) { if (circuit_init_cpath_crypto(hop, keys, sizeof(keys), 0, 0)<0) {
return -END_CIRC_REASON_TORPROTOCOL; return -END_CIRC_REASON_TORPROTOCOL;
} }
@ -1536,12 +1573,14 @@ circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer, int reason)
int int
onionskin_answer(or_circuit_t *circ, onionskin_answer(or_circuit_t *circ,
const created_cell_t *created_cell, const created_cell_t *created_cell,
const char *keys, const char *keys, size_t keys_len,
const uint8_t *rend_circ_nonce) const uint8_t *rend_circ_nonce)
{ {
cell_t cell; cell_t cell;
crypt_path_t *tmp_cpath; crypt_path_t *tmp_cpath;
tor_assert(keys_len == CPATH_KEY_MATERIAL_LEN);
if (created_cell_format(&cell, created_cell) < 0) { if (created_cell_format(&cell, created_cell) < 0) {
log_warn(LD_BUG,"couldn't format created cell (type=%d, len=%d)", log_warn(LD_BUG,"couldn't format created cell (type=%d, len=%d)",
(int)created_cell->cell_type, (int)created_cell->handshake_len); (int)created_cell->cell_type, (int)created_cell->handshake_len);
@ -1557,7 +1596,7 @@ onionskin_answer(or_circuit_t *circ,
log_debug(LD_CIRC,"init digest forward 0x%.8x, backward 0x%.8x.", log_debug(LD_CIRC,"init digest forward 0x%.8x, backward 0x%.8x.",
(unsigned int)get_uint32(keys), (unsigned int)get_uint32(keys),
(unsigned int)get_uint32(keys+20)); (unsigned int)get_uint32(keys+20));
if (circuit_init_cpath_crypto(tmp_cpath, keys, 0)<0) { if (circuit_init_cpath_crypto(tmp_cpath, keys, keys_len, 0, 0)<0) {
log_warn(LD_BUG,"Circuit initialization failed"); log_warn(LD_BUG,"Circuit initialization failed");
tor_free(tmp_cpath); tor_free(tmp_cpath);
return -1; return -1;
@ -2357,6 +2396,30 @@ onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop)
} }
} }
#ifdef TOR_UNIT_TESTS
/** Unittest helper function: Count number of hops in cpath linked list. */
unsigned int
cpath_get_n_hops(crypt_path_t **head_ptr)
{
unsigned int n_hops = 0;
crypt_path_t *tmp;
if (!*head_ptr) {
return 0;
}
tmp = *head_ptr;
if (tmp) {
n_hops++;
tmp = (*head_ptr)->next;
}
return n_hops;
}
#endif
/** A helper function used by onion_extend_cpath(). Use <b>purpose</b> /** A helper function used by onion_extend_cpath(). Use <b>purpose</b>
* and <b>state</b> and the cpath <b>head</b> (currently populated only * and <b>state</b> and the cpath <b>head</b> (currently populated only
* to length <b>cur_len</b> to decide a suitable middle hop for a * to length <b>cur_len</b> to decide a suitable middle hop for a

View File

@ -31,8 +31,9 @@ int circuit_timeout_want_to_count_circ(origin_circuit_t *circ);
int circuit_send_next_onion_skin(origin_circuit_t *circ); int circuit_send_next_onion_skin(origin_circuit_t *circ);
void circuit_note_clock_jumped(int seconds_elapsed); void circuit_note_clock_jumped(int seconds_elapsed);
int circuit_extend(cell_t *cell, circuit_t *circ); int circuit_extend(cell_t *cell, circuit_t *circ);
int circuit_init_cpath_crypto(crypt_path_t *cpath, const char *key_data, int circuit_init_cpath_crypto(crypt_path_t *cpath,
int reverse); const char *key_data, size_t key_data_len,
int reverse, int is_hs_v3);
struct created_cell_t; struct created_cell_t;
int circuit_finish_handshake(origin_circuit_t *circ, int circuit_finish_handshake(origin_circuit_t *circ,
const struct created_cell_t *created_cell); const struct created_cell_t *created_cell);
@ -40,7 +41,7 @@ int circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer,
int reason); int reason);
int onionskin_answer(or_circuit_t *circ, int onionskin_answer(or_circuit_t *circ,
const struct created_cell_t *created_cell, const struct created_cell_t *created_cell,
const char *keys, const char *keys, size_t keys_len,
const uint8_t *rend_circ_nonce); const uint8_t *rend_circ_nonce);
MOCK_DECL(int, circuit_all_predicted_ports_handled, (time_t now, MOCK_DECL(int, circuit_all_predicted_ports_handled, (time_t now,
int *need_uptime, int *need_uptime,
@ -83,6 +84,8 @@ MOCK_DECL(STATIC int, count_acceptable_nodes, (smartlist_t *nodes));
#if defined(ENABLE_TOR2WEB_MODE) || defined(TOR_UNIT_TESTS) #if defined(ENABLE_TOR2WEB_MODE) || defined(TOR_UNIT_TESTS)
STATIC const node_t *pick_tor2web_rendezvous_node(router_crn_flags_t flags, STATIC const node_t *pick_tor2web_rendezvous_node(router_crn_flags_t flags,
const or_options_t *options); const or_options_t *options);
unsigned int cpath_get_n_hops(crypt_path_t **head_ptr);
#endif #endif
#endif #endif

View File

@ -67,6 +67,7 @@
#include "main.h" #include "main.h"
#include "hs_circuitmap.h" #include "hs_circuitmap.h"
#include "hs_common.h" #include "hs_common.h"
#include "hs_ident.h"
#include "networkstatus.h" #include "networkstatus.h"
#include "nodelist.h" #include "nodelist.h"
#include "onion.h" #include "onion.h"
@ -957,6 +958,7 @@ circuit_free(circuit_t *circ)
crypto_pk_free(ocirc->intro_key); crypto_pk_free(ocirc->intro_key);
rend_data_free(ocirc->rend_data); rend_data_free(ocirc->rend_data);
hs_ident_circuit_free(ocirc->hs_ident);
tor_free(ocirc->dest_address); tor_free(ocirc->dest_address);
if (ocirc->socks_username) { if (ocirc->socks_username) {

View File

@ -42,6 +42,8 @@
#include "control.h" #include "control.h"
#include "entrynodes.h" #include "entrynodes.h"
#include "hs_common.h" #include "hs_common.h"
#include "hs_client.h"
#include "hs_ident.h"
#include "nodelist.h" #include "nodelist.h"
#include "networkstatus.h" #include "networkstatus.h"
#include "policies.h" #include "policies.h"
@ -55,6 +57,36 @@
static void circuit_expire_old_circuits_clientside(void); static void circuit_expire_old_circuits_clientside(void);
static void circuit_increment_failure_count(void); static void circuit_increment_failure_count(void);
/** Check whether the hidden service destination of the stream at
* <b>edge_conn</b> is the same as the destination of the circuit at
* <b>origin_circ</b>. */
static int
circuit_matches_with_rend_stream(const edge_connection_t *edge_conn,
const origin_circuit_t *origin_circ)
{
/* Check if this is a v2 rendezvous circ/stream */
if ((edge_conn->rend_data && !origin_circ->rend_data) ||
(!edge_conn->rend_data && origin_circ->rend_data) ||
(edge_conn->rend_data && origin_circ->rend_data &&
rend_cmp_service_ids(rend_data_get_address(edge_conn->rend_data),
rend_data_get_address(origin_circ->rend_data)))) {
/* this circ is not for this conn */
return 0;
}
/* Check if this is a v3 rendezvous circ/stream */
if ((edge_conn->hs_ident && !origin_circ->hs_ident) ||
(!edge_conn->hs_ident && origin_circ->hs_ident) ||
(edge_conn->hs_ident && origin_circ->hs_ident &&
!ed25519_pubkey_eq(&edge_conn->hs_ident->identity_pk,
&origin_circ->hs_ident->identity_pk))) {
/* this circ is not for this conn */
return 0;
}
return 1;
}
/** Return 1 if <b>circ</b> could be returned by circuit_get_best(). /** Return 1 if <b>circ</b> could be returned by circuit_get_best().
* Else return 0. * Else return 0.
*/ */
@ -169,14 +201,9 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ,
/* can't exit from this router */ /* can't exit from this router */
return 0; return 0;
} }
} else { /* not general */ } else { /* not general: this might be a rend circuit */
const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn); const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
if ((edge_conn->rend_data && !origin_circ->rend_data) || if (!circuit_matches_with_rend_stream(edge_conn, origin_circ)) {
(!edge_conn->rend_data && origin_circ->rend_data) ||
(edge_conn->rend_data && origin_circ->rend_data &&
rend_cmp_service_ids(rend_data_get_address(edge_conn->rend_data),
rend_data_get_address(origin_circ->rend_data)))) {
/* this circ is not for this conn */
return 0; return 0;
} }
} }
@ -2348,8 +2375,7 @@ link_apconn_to_circ(entry_connection_t *apconn, origin_circuit_t *circ,
/* We are attaching a stream to a rendezvous circuit. That means /* We are attaching a stream to a rendezvous circuit. That means
* that an attempt to connect to a hidden service just * that an attempt to connect to a hidden service just
* succeeded. Tell rendclient.c. */ * succeeded. Tell rendclient.c. */
rend_client_note_connection_attempt_ended( hs_client_note_connection_attempt_succeeded(ENTRY_TO_EDGE_CONN(apconn));
ENTRY_TO_EDGE_CONN(apconn)->rend_data);
} }
if (cpath) { /* we were given one; use it */ if (cpath) { /* we were given one; use it */

View File

@ -381,7 +381,8 @@ command_process_create_cell(cell_t *cell, channel_t *chan)
created_cell.handshake_len = len; created_cell.handshake_len = len;
if (onionskin_answer(circ, &created_cell, if (onionskin_answer(circ, &created_cell,
(const char *)keys, rend_circ_nonce)<0) { (const char *)keys, sizeof(keys),
rend_circ_nonce)<0) {
log_warn(LD_OR,"Failed to reply to CREATE_FAST cell. Closing."); log_warn(LD_OR,"Failed to reply to CREATE_FAST cell. Closing.");
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
return; return;

View File

@ -84,6 +84,7 @@
#include "geoip.h" #include "geoip.h"
#include "main.h" #include "main.h"
#include "hs_common.h" #include "hs_common.h"
#include "hs_ident.h"
#include "nodelist.h" #include "nodelist.h"
#include "policies.h" #include "policies.h"
#include "reasons.h" #include "reasons.h"
@ -605,6 +606,7 @@ connection_free_(connection_t *conn)
} }
if (CONN_IS_EDGE(conn)) { if (CONN_IS_EDGE(conn)) {
rend_data_free(TO_EDGE_CONN(conn)->rend_data); rend_data_free(TO_EDGE_CONN(conn)->rend_data);
hs_ident_edge_conn_free(TO_EDGE_CONN(conn)->hs_ident);
} }
if (conn->type == CONN_TYPE_CONTROL) { if (conn->type == CONN_TYPE_CONTROL) {
control_connection_t *control_conn = TO_CONTROL_CONN(conn); control_connection_t *control_conn = TO_CONTROL_CONN(conn);
@ -636,6 +638,7 @@ connection_free_(connection_t *conn)
} }
rend_data_free(dir_conn->rend_data); rend_data_free(dir_conn->rend_data);
hs_ident_dir_conn_free(dir_conn->hs_ident);
if (dir_conn->guard_state) { if (dir_conn->guard_state) {
/* Cancel before freeing, if it's still there. */ /* Cancel before freeing, if it's still there. */
entry_guard_cancel(&dir_conn->guard_state); entry_guard_cancel(&dir_conn->guard_state);

View File

@ -2455,8 +2455,8 @@ connection_ap_get_begincell_flags(entry_connection_t *ap_conn)
* *
* If ap_conn is broken, mark it for close and return -1. Else return 0. * If ap_conn is broken, mark it for close and return -1. Else return 0.
*/ */
int MOCK_IMPL(int,
connection_ap_handshake_send_begin(entry_connection_t *ap_conn) connection_ap_handshake_send_begin,(entry_connection_t *ap_conn))
{ {
char payload[CELL_PAYLOAD_SIZE]; char payload[CELL_PAYLOAD_SIZE];
int payload_len; int payload_len;
@ -3566,8 +3566,12 @@ int
connection_edge_is_rendezvous_stream(const edge_connection_t *conn) connection_edge_is_rendezvous_stream(const edge_connection_t *conn)
{ {
tor_assert(conn); tor_assert(conn);
if (conn->rend_data) /* It should not be possible to set both of these structs */
tor_assert_nonfatal(!(conn->rend_data && conn->hs_ident));
if (conn->rend_data || conn->hs_ident) {
return 1; return 1;
}
return 0; return 0;
} }

View File

@ -33,7 +33,8 @@ int connection_edge_finished_connecting(edge_connection_t *conn);
void connection_ap_about_to_close(entry_connection_t *edge_conn); void connection_ap_about_to_close(entry_connection_t *edge_conn);
void connection_exit_about_to_close(edge_connection_t *edge_conn); void connection_exit_about_to_close(edge_connection_t *edge_conn);
int connection_ap_handshake_send_begin(entry_connection_t *ap_conn); MOCK_DECL(int,
connection_ap_handshake_send_begin,(entry_connection_t *ap_conn));
int connection_ap_handshake_send_resolve(entry_connection_t *ap_conn); int connection_ap_handshake_send_resolve(entry_connection_t *ap_conn);
entry_connection_t *connection_ap_make_link(connection_t *partner, entry_connection_t *connection_ap_make_link(connection_t *partner,

View File

@ -372,7 +372,7 @@ cpuworker_onion_handshake_replyfn(void *work_)
if (onionskin_answer(circ, if (onionskin_answer(circ,
&rpl.created_cell, &rpl.created_cell,
(const char*)rpl.keys, (const char*)rpl.keys, sizeof(rpl.keys),
rpl.rend_auth_material) < 0) { rpl.rend_auth_material) < 0) {
log_warn(LD_OR,"onionskin_answer failed. Closing."); log_warn(LD_OR,"onionskin_answer failed. Closing.");
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);

224
src/or/hs_circuit.c Normal file
View File

@ -0,0 +1,224 @@
/* Copyright (c) 2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file hs_circuit.c
**/
#include "or.h"
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuituse.h"
#include "config.h"
#include "hs_circuit.h"
#include "hs_ident.h"
#include "hs_ntor.h"
/* A circuit is about to become an e2e rendezvous circuit. Check
* <b>circ_purpose</b> and ensure that it's properly set. Return true iff
* circuit purpose is properly set, otherwise return false. */
static int
circuit_purpose_is_correct_for_rend(unsigned int circ_purpose,
int is_service_side)
{
if (is_service_side) {
if (circ_purpose != CIRCUIT_PURPOSE_S_CONNECT_REND) {
log_warn(LD_BUG,
"HS e2e circuit setup with wrong purpose (%d)", circ_purpose);
return 0;
}
}
if (!is_service_side) {
if (circ_purpose != CIRCUIT_PURPOSE_C_REND_READY &&
circ_purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) {
log_warn(LD_BUG,
"Client e2e circuit setup with wrong purpose (%d)", circ_purpose);
return 0;
}
}
return 1;
}
/* Create and return a crypt path for the final hop of a v3 prop224 rendezvous
* circuit. Initialize the crypt path crypto using the output material from the
* ntor key exchange at <b>ntor_key_seed</b>.
*
* If <b>is_service_side</b> is set, we are the hidden service and the final
* hop of the rendezvous circuit is the client on the other side. */
static crypt_path_t *
create_rend_cpath(const uint8_t *ntor_key_seed, size_t seed_len,
int is_service_side)
{
uint8_t keys[HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN];
crypt_path_t *cpath = NULL;
/* Do the key expansion */
if (hs_ntor_circuit_key_expansion(ntor_key_seed, seed_len,
keys, sizeof(keys)) < 0) {
goto err;
}
/* Setup the cpath */
cpath = tor_malloc_zero(sizeof(crypt_path_t));
cpath->magic = CRYPT_PATH_MAGIC;
if (circuit_init_cpath_crypto(cpath, (char*)keys, sizeof(keys),
is_service_side, 1) < 0) {
tor_free(cpath);
goto err;
}
err:
memwipe(keys, 0, sizeof(keys));
return cpath;
}
/* We are a v2 legacy HS client: Create and return a crypt path for the hidden
* service on the other side of the rendezvous circuit <b>circ</b>. Initialize
* the crypt path crypto using the body of the RENDEZVOUS1 cell at
* <b>rend_cell_body</b> (which must be at least DH_KEY_LEN+DIGEST_LEN bytes).
*/
static crypt_path_t *
create_rend_cpath_legacy(origin_circuit_t *circ, const uint8_t *rend_cell_body)
{
crypt_path_t *hop = NULL;
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN];
/* first DH_KEY_LEN bytes are g^y from the service. Finish the dh
* handshake...*/
tor_assert(circ->build_state);
tor_assert(circ->build_state->pending_final_cpath);
hop = circ->build_state->pending_final_cpath;
tor_assert(hop->rend_dh_handshake_state);
if (crypto_dh_compute_secret(LOG_PROTOCOL_WARN, hop->rend_dh_handshake_state,
(char*)rend_cell_body, DH_KEY_LEN,
keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) {
log_warn(LD_GENERAL, "Couldn't complete DH handshake.");
goto err;
}
/* ... and set up cpath. */
if (circuit_init_cpath_crypto(hop,
keys+DIGEST_LEN, sizeof(keys)-DIGEST_LEN,
0, 0) < 0)
goto err;
/* Check whether the digest is right... */
if (tor_memneq(keys, rend_cell_body+DH_KEY_LEN, DIGEST_LEN)) {
log_warn(LD_PROTOCOL, "Incorrect digest of key material.");
goto err;
}
/* clean up the crypto stuff we just made */
crypto_dh_free(hop->rend_dh_handshake_state);
hop->rend_dh_handshake_state = NULL;
goto done;
err:
hop = NULL;
done:
memwipe(keys, 0, sizeof(keys));
return hop;
}
/* Append the final <b>hop</b> to the cpath of the rend <b>circ</b>, and mark
* <b>circ</b> ready for use to transfer HS relay cells. */
static void
finalize_rend_circuit(origin_circuit_t *circ, crypt_path_t *hop,
int is_service_side)
{
tor_assert(circ);
tor_assert(hop);
/* Notify the circuit state machine that we are splicing this circuit */
int new_circ_purpose = is_service_side ?
CIRCUIT_PURPOSE_S_REND_JOINED : CIRCUIT_PURPOSE_C_REND_JOINED;
circuit_change_purpose(TO_CIRCUIT(circ), new_circ_purpose);
/* All is well. Extend the circuit. */
hop->state = CPATH_STATE_OPEN;
/* Set the windows to default. */
hop->package_window = circuit_initial_package_window();
hop->deliver_window = CIRCWINDOW_START;
/* Now that this circuit has finished connecting to its destination,
* make sure circuit_get_open_circ_or_launch is willing to return it
* so we can actually use it. */
circ->hs_circ_has_timed_out = 0;
/* Append the hop to the cpath of this circuit */
onion_append_to_cpath(&circ->cpath, hop);
/* In legacy code, 'pending_final_cpath' points to the final hop we just
* appended to the cpath. We set the original pointer to NULL so that we
* don't double free it. */
if (circ->build_state) {
circ->build_state->pending_final_cpath = NULL;
}
/* Finally, mark circuit as ready to be used for client streams */
if (!is_service_side) {
circuit_try_attaching_streams(circ);
}
}
/* Circuit <b>circ</b> just finished the rend ntor key exchange. Use the key
* exchange output material at <b>ntor_key_seed</b> and setup <b>circ</b> to
* serve as a rendezvous end-to-end circuit between the client and the
* service. If <b>is_service_side</b> is set, then we are the hidden service
* and the other side is the client.
*
* Return 0 if the operation went well; in case of error return -1. */
int
hs_circuit_setup_e2e_rend_circ(origin_circuit_t *circ,
const uint8_t *ntor_key_seed, size_t seed_len,
int is_service_side)
{
if (BUG(!circuit_purpose_is_correct_for_rend(TO_CIRCUIT(circ)->purpose,
is_service_side))) {
return -1;
}
crypt_path_t *hop = create_rend_cpath(ntor_key_seed, seed_len,
is_service_side);
if (!hop) {
log_warn(LD_REND, "Couldn't get v3 %s cpath!",
is_service_side ? "service-side" : "client-side");
return -1;
}
finalize_rend_circuit(circ, hop, is_service_side);
return 0;
}
/* We are a v2 legacy HS client and we just received a RENDEZVOUS1 cell
* <b>rend_cell_body</b> on <b>circ</b>. Finish up the DH key exchange and then
* extend the crypt path of <b>circ</b> so that the hidden service is on the
* other side. */
int
hs_circuit_setup_e2e_rend_circ_legacy_client(origin_circuit_t *circ,
const uint8_t *rend_cell_body)
{
if (BUG(!circuit_purpose_is_correct_for_rend(
TO_CIRCUIT(circ)->purpose, 0))) {
return -1;
}
crypt_path_t *hop = create_rend_cpath_legacy(circ, rend_cell_body);
if (!hop) {
log_warn(LD_GENERAL, "Couldn't get v2 cpath.");
return -1;
}
finalize_rend_circuit(circ, hop, 0);
return 0;
}

24
src/or/hs_circuit.h Normal file
View File

@ -0,0 +1,24 @@
/* Copyright (c) 2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file hs_circuit.h
* \brief Header file containing circuit data for the whole HS subsytem.
**/
#ifndef TOR_HS_CIRCUIT_H
#define TOR_HS_CIRCUIT_H
#include "or.h"
/* e2e circuit API. */
int hs_circuit_setup_e2e_rend_circ(origin_circuit_t *circ,
const uint8_t *ntor_key_seed,
size_t seed_len,
int is_service_side);
int hs_circuit_setup_e2e_rend_circ_legacy_client(origin_circuit_t *circ,
const uint8_t *rend_cell_body);
#endif /* TOR_HS_CIRCUIT_H */

47
src/or/hs_client.c Normal file
View File

@ -0,0 +1,47 @@
/* Copyright (c) 2016-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file hs_service.c
* \brief Implement next generation hidden service client functionality
**/
#include "or.h"
#include "hs_circuit.h"
#include "connection_edge.h"
#include "rendclient.h"
#include "hs_client.h"
/** A prop224 v3 HS circuit successfully connected to the hidden
* service. Update the stream state at <b>hs_conn_ident</b> appropriately. */
static void
hs_client_attempt_succeeded(const hs_ident_edge_conn_t *hs_conn_ident)
{
(void) hs_conn_ident;
/* TODO: When implementing client side */
return;
}
/** A circuit just finished connecting to a hidden service that the stream
* <b>conn</b> has been waiting for. Let the HS subsystem know about this. */
void
hs_client_note_connection_attempt_succeeded(const edge_connection_t *conn)
{
tor_assert(connection_edge_is_rendezvous_stream(conn));
if (BUG(conn->rend_data && conn->hs_ident)) {
log_warn(LD_BUG, "Stream had both rend_data and hs_ident..."
"Prioritizing hs_ident");
}
if (conn->hs_ident) { /* It's v3: pass it to the prop224 handler */
hs_client_attempt_succeeded(conn->hs_ident);
return;
} else if (conn->rend_data) { /* It's v2: pass it to the legacy handler */
rend_client_note_connection_attempt_ended(conn->rend_data);
return;
}
}

16
src/or/hs_client.h Normal file
View File

@ -0,0 +1,16 @@
/* Copyright (c) 2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file hs_client.h
* \brief Header file containing client data for the HS subsytem.
**/
#ifndef TOR_HS_CLIENT_H
#define TOR_HS_CLIENT_H
void hs_client_note_connection_attempt_succeeded(
const edge_connection_t *conn);
#endif /* TOR_HS_CLIENT_H */

View File

@ -49,6 +49,12 @@
/* The time period rotation offset as seen in prop224 section [TIME-PERIODS] */ /* The time period rotation offset as seen in prop224 section [TIME-PERIODS] */
#define HS_TIME_PERIOD_ROTATION_OFFSET (12 * 60) /* minutes */ #define HS_TIME_PERIOD_ROTATION_OFFSET (12 * 60) /* minutes */
/* Type of authentication key used by an introduction point. */
typedef enum {
HS_AUTH_KEY_TYPE_LEGACY = 1,
HS_AUTH_KEY_TYPE_ED25519 = 2,
} hs_auth_key_type_t;
int hs_check_service_private_dir(const char *username, const char *path, int hs_check_service_private_dir(const char *username, const char *path,
unsigned int dir_group_readable, unsigned int dir_group_readable,
unsigned int create); unsigned int create);

81
src/or/hs_ident.c Normal file
View File

@ -0,0 +1,81 @@
/* Copyright (c) 2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file hs_ident.c
* \brief Contains circuit and connection identifier code for the whole HS
* subsytem.
**/
#include "hs_ident.h"
/* Return a newly allocated circuit identifier. The given public key is copied
* identity_pk into the identifier. */
hs_ident_circuit_t *
hs_ident_circuit_new(const ed25519_public_key_t *identity_pk,
hs_ident_circuit_type_t circuit_type)
{
tor_assert(circuit_type == HS_IDENT_CIRCUIT_INTRO ||
circuit_type == HS_IDENT_CIRCUIT_RENDEZVOUS);
hs_ident_circuit_t *ident = tor_malloc_zero(sizeof(*ident));
ed25519_pubkey_copy(&ident->identity_pk, identity_pk);
ident->circuit_type = circuit_type;
return ident;
}
/* Free the given circuit identifier. */
void
hs_ident_circuit_free(hs_ident_circuit_t *ident)
{
if (ident == NULL) {
return;
}
if (ident->auth_key_type == HS_AUTH_KEY_TYPE_LEGACY) {
crypto_pk_free(ident->auth_rsa_pk);
}
memwipe(ident, 0, sizeof(hs_ident_circuit_t));
tor_free(ident);
}
/* For a given directory connection identifier src, return a newly allocated
* copy of it. This can't fail. */
hs_ident_dir_conn_t *
hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src)
{
hs_ident_dir_conn_t *ident = tor_malloc_zero(sizeof(*ident));
memcpy(ident, src, sizeof(*ident));
return ident;
}
/* Free the given directory connection identifier. */
void
hs_ident_dir_conn_free(hs_ident_dir_conn_t *ident)
{
if (ident == NULL) {
return;
}
memwipe(ident, 0, sizeof(hs_ident_dir_conn_t));
tor_free(ident);
}
/* Return a newly allocated edge connection identifier. The given public key
* identity_pk is copied into the identifier. */
hs_ident_edge_conn_t *
hs_ident_edge_conn_new(const ed25519_public_key_t *identity_pk)
{
hs_ident_edge_conn_t *ident = tor_malloc_zero(sizeof(*ident));
ed25519_pubkey_copy(&ident->identity_pk, identity_pk);
return ident;
}
/* Free the given edge connection identifier. */
void
hs_ident_edge_conn_free(hs_ident_edge_conn_t *ident)
{
if (ident == NULL) {
return;
}
memwipe(ident, 0, sizeof(hs_ident_edge_conn_t));
tor_free(ident);
}

124
src/or/hs_ident.h Normal file
View File

@ -0,0 +1,124 @@
/* Copyright (c) 2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file hs_ident.h
* \brief Header file containing circuit and connection identifier data for
* the whole HS subsytem.
*
* \details
* This interface is used to uniquely identify a hidden service on a circuit
* or connection using the service identity public key. Once the circuit or
* connection subsystem calls in the hidden service one, we use those
* identifiers to lookup the corresponding objects like service, intro point
* and descriptor.
*
* Furthermore, the circuit identifier holds cryptographic material needed for
* the e2e encryption on the rendezvous circuit which is set once the
* rendezvous circuit has opened and ready to be used.
**/
#ifndef TOR_HS_IDENT_H
#define TOR_HS_IDENT_H
#include "crypto.h"
#include "crypto_ed25519.h"
#include "hs_common.h"
/* Length of the rendezvous cookie that is used to connect circuits at the
* rendezvous point. */
#define HS_REND_COOKIE_LEN DIGEST_LEN
/* Type of circuit an hs_ident_t object is associated with. */
typedef enum {
HS_IDENT_CIRCUIT_INTRO = 1,
HS_IDENT_CIRCUIT_RENDEZVOUS = 2,
} hs_ident_circuit_type_t;
/* Client and service side circuit identifier that is used for hidden service
* circuit establishment. Not all fields contain data, it depends on the
* circuit purpose. This is attached to an origin_circuit_t. All fields are
* used by both client and service. */
typedef struct hs_ident_circuit_t {
/* (All circuit) The public key used to uniquely identify the service. It is
* the one found in the onion address. */
ed25519_public_key_t identity_pk;
/* (All circuit) The type of circuit this identifier is attached to.
* Accessors of the fields in this object assert non fatal on this circuit
* type. In other words, if a rendezvous field is being accessed, the
* circuit type MUST BE of type HS_IDENT_CIRCUIT_RENDEZVOUS. This value is
* set when an object is initialized in its constructor. */
hs_ident_circuit_type_t circuit_type;
/* (Only intro point circuit) Which type of authentication key this
* circuit identifier is using. */
hs_auth_key_type_t auth_key_type;
/* (Only intro point circuit) Introduction point authentication key. In
* legacy mode, we use an RSA key else an ed25519 public key. */
crypto_pk_t *auth_rsa_pk;
ed25519_public_key_t auth_ed25519_pk;
/* (Only rendezvous circuit) Rendezvous cookie sent from the client to the
* service with an INTRODUCE1 cell and used by the service in an
* RENDEZVOUS1 cell. */
uint8_t rendezvous_cookie[HS_REND_COOKIE_LEN];
/* (Only rendezvous circuit) The HANDSHAKE_INFO needed in the RENDEZVOUS1
* cell of the service. The construction is as follows:
* SERVER_PK [32 bytes]
* AUTH_MAC [32 bytes]
*/
uint8_t rendezvous_handshake_info[CURVE25519_PUBKEY_LEN + DIGEST256_LEN];
/* (Only rendezvous circuit) The NTOR_KEY_SEED needed for key derivation for
* the e2e encryption with the client on the circuit. */
uint8_t rendezvous_ntor_key_seed[DIGEST256_LEN];
/* (Only rendezvous circuit) Number of streams associated with this
* rendezvous circuit. We track this because there is a check on a maximum
* value. */
uint64_t num_rdv_streams;
} hs_ident_circuit_t;
/* Client and service side directory connection identifier used for a
* directory connection to identify which service is being queried. This is
* attached to a dir_connection_t. */
typedef struct hs_ident_dir_conn_t {
/* The public key used to uniquely identify the service. It is the one found
* in the onion address. */
ed25519_public_key_t identity_pk;
/* XXX: Client authorization. */
} hs_ident_dir_conn_t;
/* Client and service side edge connection identifier used for an edge
* connection to identify which service is being queried. This is attached to
* a edge_connection_t. */
typedef struct hs_ident_edge_conn_t {
/* The public key used to uniquely identify the service. It is the one found
* in the onion address. */
ed25519_public_key_t identity_pk;
/* XXX: Client authorization. */
} hs_ident_edge_conn_t;
/* Circuit identifier API. */
hs_ident_circuit_t *hs_ident_circuit_new(
const ed25519_public_key_t *identity_pk,
hs_ident_circuit_type_t circuit_type);
void hs_ident_circuit_free(hs_ident_circuit_t *ident);
/* Directory connection identifier API. */
hs_ident_dir_conn_t *hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src);
void hs_ident_dir_conn_free(hs_ident_dir_conn_t *ident);
/* Edge connection identifier API. */
hs_ident_edge_conn_t *hs_ident_edge_conn_new(
const ed25519_public_key_t *identity_pk);
void hs_ident_edge_conn_free(hs_ident_edge_conn_t *ident);
#endif /* TOR_HS_IDENT_H */

View File

@ -578,49 +578,41 @@ hs_ntor_client_rendezvous2_mac_is_good(
/* Input length to KDF for key expansion */ /* Input length to KDF for key expansion */
#define NTOR_KEY_EXPANSION_KDF_INPUT_LEN (DIGEST256_LEN + M_HSEXPAND_LEN) #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 /** Given the rendezvous key seed in <b>ntor_key_seed</b> (of size
* circuit key expansion as specified by section '4.2.1. Key expansion' and * DIGEST256_LEN), do the circuit key expansion as specified by section
* return a hs_ntor_rend_circuit_keys_t structure with the computed keys. */ * '4.2.1. Key expansion' and place the keys in <b>keys_out</b> (which must be
hs_ntor_rend_circuit_keys_t * * of size HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN).
hs_ntor_circuit_key_expansion( *
const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys) * 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 *ptr;
uint8_t kdf_input[NTOR_KEY_EXPANSION_KDF_INPUT_LEN]; uint8_t kdf_input[NTOR_KEY_EXPANSION_KDF_INPUT_LEN];
uint8_t keys[NTOR_KEY_EXPANSION_KDF_OUTPUT_LEN];
crypto_xof_t *xof; crypto_xof_t *xof;
hs_ntor_rend_circuit_keys_t *rend_circuit_keys = NULL;
/* 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 */ /* Let's build the input to the KDF */
ptr = kdf_input; ptr = kdf_input;
APPEND(ptr, hs_ntor_rend_cell_keys->ntor_key_seed, DIGEST256_LEN); APPEND(ptr, ntor_key_seed, DIGEST256_LEN);
APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND)); APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND));
tor_assert(ptr == kdf_input + sizeof(kdf_input)); tor_assert(ptr == kdf_input + sizeof(kdf_input));
/* Generate the keys */ /* Generate the keys */
xof = crypto_xof_new(); xof = crypto_xof_new();
crypto_xof_add_bytes(xof, kdf_input, sizeof(kdf_input)); crypto_xof_add_bytes(xof, kdf_input, sizeof(kdf_input));
crypto_xof_squeeze_bytes(xof, keys, sizeof(keys)); crypto_xof_squeeze_bytes(xof, keys_out, HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN);
crypto_xof_free(xof); crypto_xof_free(xof);
/* Generate keys structure and assign keys to it */ return 0;
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;
} }

View File

@ -6,6 +6,10 @@
#include "or.h" #include "or.h"
/* Output length of KDF for key expansion */
#define HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN \
(DIGEST256_LEN*2 + CIPHER256_KEY_LEN*2)
/* Key material needed to encode/decode INTRODUCE1 cells */ /* Key material needed to encode/decode INTRODUCE1 cells */
typedef struct { typedef struct {
/* Key used for encryption of encrypted INTRODUCE1 blob */ /* Key used for encryption of encrypted INTRODUCE1 blob */
@ -23,21 +27,6 @@ typedef struct {
uint8_t ntor_key_seed[DIGEST256_LEN]; uint8_t ntor_key_seed[DIGEST256_LEN];
} hs_ntor_rend_cell_keys_t; } 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( int hs_ntor_client_get_introduce1_keys(
const ed25519_public_key_t *intro_auth_pubkey, const ed25519_public_key_t *intro_auth_pubkey,
const curve25519_public_key_t *intro_enc_pubkey, const curve25519_public_key_t *intro_enc_pubkey,
@ -66,8 +55,9 @@ int hs_ntor_service_get_rendezvous1_keys(
const curve25519_public_key_t *client_ephemeral_enc_pubkey, const curve25519_public_key_t *client_ephemeral_enc_pubkey,
hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out); hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out);
hs_ntor_rend_circuit_keys_t *hs_ntor_circuit_key_expansion( int hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed,
const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys); size_t seed_len,
uint8_t *keys_out, size_t keys_out_len);
int hs_ntor_client_rendezvous2_mac_is_good( int hs_ntor_client_rendezvous2_mac_is_good(
const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys, const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys,

View File

@ -50,16 +50,19 @@ LIBTOR_A_SOURCES = \
src/or/dnsserv.c \ src/or/dnsserv.c \
src/or/fp_pair.c \ src/or/fp_pair.c \
src/or/geoip.c \ src/or/geoip.c \
src/or/hs_intropoint.c \ src/or/hs_cache.c \
src/or/hs_circuitmap.c \ src/or/hs_circuitmap.c \
src/or/hs_common.c \
src/or/hs_circuit.c \
src/or/hs_descriptor.c \
src/or/hs_ident.c \
src/or/hs_intropoint.c \
src/or/hs_ntor.c \ src/or/hs_ntor.c \
src/or/hs_service.c \ src/or/hs_service.c \
src/or/hs_client.c \
src/or/entrynodes.c \ src/or/entrynodes.c \
src/or/ext_orport.c \ src/or/ext_orport.c \
src/or/hibernate.c \ src/or/hibernate.c \
src/or/hs_cache.c \
src/or/hs_common.c \
src/or/hs_descriptor.c \
src/or/keypin.c \ src/or/keypin.c \
src/or/main.c \ src/or/main.c \
src/or/microdesc.c \ src/or/microdesc.c \
@ -181,11 +184,14 @@ ORHEADERS = \
src/or/hibernate.h \ src/or/hibernate.h \
src/or/hs_cache.h \ src/or/hs_cache.h \
src/or/hs_common.h \ src/or/hs_common.h \
src/or/hs_circuit.h \
src/or/hs_descriptor.h \ src/or/hs_descriptor.h \
src/or/hs_ident.h \
src/or/hs_intropoint.h \ src/or/hs_intropoint.h \
src/or/hs_circuitmap.h \ src/or/hs_circuitmap.h \
src/or/hs_ntor.h \ src/or/hs_ntor.h \
src/or/hs_service.h \ src/or/hs_service.h \
src/or/hs_client.h \
src/or/keypin.h \ src/or/keypin.h \
src/or/main.h \ src/or/main.h \
src/or/microdesc.h \ src/or/microdesc.h \

View File

@ -846,6 +846,11 @@ rend_data_v2_t *TO_REND_DATA_V2(const rend_data_t *d)
return DOWNCAST(rend_data_v2_t, d); return DOWNCAST(rend_data_v2_t, d);
} }
/* Stub because we can't include hs_ident.h. */
typedef struct hs_ident_edge_conn_t hs_ident_edge_conn_t;
typedef struct hs_ident_dir_conn_t hs_ident_dir_conn_t;
typedef struct hs_ident_circuit_t hs_ident_circuit_t;
/** Time interval for tracking replays of DH public keys received in /** Time interval for tracking replays of DH public keys received in
* INTRODUCE2 cells. Used only to avoid launching multiple * INTRODUCE2 cells. Used only to avoid launching multiple
* simultaneous attempts to connect to the same rendezvous point. */ * simultaneous attempts to connect to the same rendezvous point. */
@ -1633,6 +1638,11 @@ typedef struct edge_connection_t {
* an exit)? */ * an exit)? */
rend_data_t *rend_data; rend_data_t *rend_data;
/* Hidden service connection identifier for edge connections. Used by the HS
* client-side code to identify client SOCKS connections and by the
* service-side code to match HS circuits with their streams. */
hs_ident_edge_conn_t *hs_ident;
uint32_t address_ttl; /**< TTL for address-to-addr mapping on exit uint32_t address_ttl; /**< TTL for address-to-addr mapping on exit
* connection. Exit connections only. */ * connection. Exit connections only. */
uint32_t begincell_flags; /** Flags sent or received in the BEGIN cell uint32_t begincell_flags; /** Flags sent or received in the BEGIN cell
@ -1783,6 +1793,11 @@ typedef struct dir_connection_t {
/** What rendezvous service are we querying for? */ /** What rendezvous service are we querying for? */
rend_data_t *rend_data; rend_data_t *rend_data;
/* Hidden service connection identifier for dir connections: Used by HS
client-side code to fetch HS descriptors, and by the service-side code to
upload descriptors. */
hs_ident_dir_conn_t *hs_ident;
/** If this is a one-hop connection, tracks the state of the directory guard /** If this is a one-hop connection, tracks the state of the directory guard
* for this connection (if any). */ * for this connection (if any). */
struct circuit_guard_state_t *guard_state; struct circuit_guard_state_t *guard_state;
@ -3186,6 +3201,10 @@ typedef struct origin_circuit_t {
/** Holds all rendezvous data on either client or service side. */ /** Holds all rendezvous data on either client or service side. */
rend_data_t *rend_data; rend_data_t *rend_data;
/** Holds hidden service identifier on either client or service side. This
* is for both introduction and rendezvous circuit. */
hs_ident_circuit_t *hs_ident;
/** Holds the data that the entry guard system uses to track the /** Holds the data that the entry guard system uses to track the
* status of the guard this circuit is using, and thereby to determine * status of the guard this circuit is using, and thereby to determine
* whether this circuit can be used. */ * whether this circuit can be used. */

View File

@ -17,6 +17,7 @@
#include "connection_edge.h" #include "connection_edge.h"
#include "directory.h" #include "directory.h"
#include "hs_common.h" #include "hs_common.h"
#include "hs_circuit.h"
#include "main.h" #include "main.h"
#include "networkstatus.h" #include "networkstatus.h"
#include "nodelist.h" #include "nodelist.h"
@ -1150,9 +1151,6 @@ int
rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request, rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
size_t request_len) size_t request_len)
{ {
crypt_path_t *hop;
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN];
if ((circ->base_.purpose != CIRCUIT_PURPOSE_C_REND_READY && if ((circ->base_.purpose != CIRCUIT_PURPOSE_C_REND_READY &&
circ->base_.purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) circ->base_.purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED)
|| !circ->build_state->pending_final_cpath) { || !circ->build_state->pending_final_cpath) {
@ -1170,55 +1168,13 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
log_info(LD_REND,"Got RENDEZVOUS2 cell from hidden service."); log_info(LD_REND,"Got RENDEZVOUS2 cell from hidden service.");
/* first DH_KEY_LEN bytes are g^y from the service. Finish the dh if (hs_circuit_setup_e2e_rend_circ_legacy_client(circ, request) < 0) {
* handshake...*/ log_warn(LD_GENERAL, "Failed to setup circ");
tor_assert(circ->build_state);
tor_assert(circ->build_state->pending_final_cpath);
hop = circ->build_state->pending_final_cpath;
tor_assert(hop->rend_dh_handshake_state);
if (crypto_dh_compute_secret(LOG_PROTOCOL_WARN,
hop->rend_dh_handshake_state, (char*)request,
DH_KEY_LEN,
keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) {
log_warn(LD_GENERAL, "Couldn't complete DH handshake.");
goto err; goto err;
} }
/* ... and set up cpath. */
if (circuit_init_cpath_crypto(hop, keys+DIGEST_LEN, 0)<0)
goto err;
/* Check whether the digest is right... */
if (tor_memneq(keys, request+DH_KEY_LEN, DIGEST_LEN)) {
log_warn(LD_PROTOCOL, "Incorrect digest of key material.");
goto err;
}
crypto_dh_free(hop->rend_dh_handshake_state);
hop->rend_dh_handshake_state = NULL;
/* All is well. Extend the circuit. */
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_REND_JOINED);
hop->state = CPATH_STATE_OPEN;
/* set the windows to default. these are the windows
* that the client thinks the service has.
*/
hop->package_window = circuit_initial_package_window();
hop->deliver_window = CIRCWINDOW_START;
/* Now that this circuit has finished connecting to its destination,
* make sure circuit_get_open_circ_or_launch is willing to return it
* so we can actually use it. */
circ->hs_circ_has_timed_out = 0;
onion_append_to_cpath(&circ->cpath, hop);
circ->build_state->pending_final_cpath = NULL; /* prevent double-free */
circuit_try_attaching_streams(circ);
memwipe(keys, 0, sizeof(keys));
return 0; return 0;
err: err:
memwipe(keys, 0, sizeof(keys));
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
return -1; return -1;
} }

View File

@ -2195,7 +2195,9 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
cpath->rend_dh_handshake_state = dh; cpath->rend_dh_handshake_state = dh;
dh = NULL; dh = NULL;
if (circuit_init_cpath_crypto(cpath,keys+DIGEST_LEN,1)<0) if (circuit_init_cpath_crypto(cpath,
keys+DIGEST_LEN, sizeof(keys)-DIGEST_LEN,
1, 0)<0)
goto err; goto err;
memcpy(cpath->rend_circ_nonce, keys, DIGEST_LEN); memcpy(cpath->rend_circ_nonce, keys, DIGEST_LEN);

View File

@ -115,6 +115,7 @@ src_test_test_SOURCES = \
src/test/test_extorport.c \ src/test/test_extorport.c \
src/test/test_hs.c \ src/test/test_hs.c \
src/test/test_hs_service.c \ src/test/test_hs_service.c \
src/test/test_hs_client.c \
src/test/test_hs_intropoint.c \ src/test/test_hs_intropoint.c \
src/test/test_handles.c \ src/test/test_handles.c \
src/test/test_hs_cache.c \ src/test/test_hs_cache.c \
@ -270,6 +271,7 @@ noinst_HEADERS+= \
src/test/test.h \ src/test/test.h \
src/test/test_helpers.h \ src/test/test_helpers.h \
src/test/test_dir_common.h \ src/test/test_dir_common.h \
src/test/test_connection.h \
src/test/test_descriptors.inc \ src/test/test_descriptors.inc \
src/test/example_extrainfo.inc \ src/test/example_extrainfo.inc \
src/test/failing_routerdescs.inc \ src/test/failing_routerdescs.inc \

View File

@ -71,3 +71,19 @@ create_descriptor(rend_service_descriptor_t **generated, char **service_id,
crypto_pk_free(pk2); crypto_pk_free(pk2);
} }
rend_data_t *
mock_rend_data(const char *onion_address)
{
rend_data_v2_t *v2_data = tor_malloc_zero(sizeof(*v2_data));
rend_data_t *rend_query = &v2_data->base_;
rend_query->version = 2;
strlcpy(v2_data->onion_address, onion_address,
sizeof(v2_data->onion_address));
v2_data->auth_type = REND_NO_AUTH;
rend_query->hsdirs_fp = smartlist_new();
smartlist_add(rend_query->hsdirs_fp, tor_memdup("aaaaaaaaaaaaaaaaaaaaaaaa",
DIGEST_LEN));
return rend_query;
}

View File

@ -10,6 +10,7 @@ void generate_desc(int time_diff, rend_encoded_v2_service_descriptor_t **desc,
char **service_id, int intro_points); char **service_id, int intro_points);
void create_descriptor(rend_service_descriptor_t **generated, void create_descriptor(rend_service_descriptor_t **generated,
char **service_id, int intro_points); char **service_id, int intro_points);
rend_data_t *mock_rend_data(const char *onion_address);
#endif #endif

View File

@ -1215,6 +1215,7 @@ struct testgroup_t testgroups[] = {
{ "hs_cache/", hs_cache }, { "hs_cache/", hs_cache },
{ "hs_descriptor/", hs_descriptor }, { "hs_descriptor/", hs_descriptor },
{ "hs_service/", hs_service_tests }, { "hs_service/", hs_service_tests },
{ "hs_client/", hs_client_tests },
{ "hs_intropoint/", hs_intropoint_tests }, { "hs_intropoint/", hs_intropoint_tests },
{ "introduce/", introduce_tests }, { "introduce/", introduce_tests },
{ "keypin/", keypin_tests }, { "keypin/", keypin_tests },

View File

@ -209,6 +209,7 @@ extern struct testcase_t hs_tests[];
extern struct testcase_t hs_cache[]; extern struct testcase_t hs_cache[];
extern struct testcase_t hs_descriptor[]; extern struct testcase_t hs_descriptor[];
extern struct testcase_t hs_service_tests[]; extern struct testcase_t hs_service_tests[];
extern struct testcase_t hs_client_tests[];
extern struct testcase_t hs_intropoint_tests[]; extern struct testcase_t hs_intropoint_tests[];
extern struct testcase_t introduce_tests[]; extern struct testcase_t introduce_tests[];
extern struct testcase_t keypin_tests[]; extern struct testcase_t keypin_tests[];

View File

@ -17,9 +17,8 @@
#include "rendcache.h" #include "rendcache.h"
#include "directory.h" #include "directory.h"
static void test_conn_lookup_addr_helper(const char *address, #include "test_connection.h"
int family, #include "test_helpers.h"
tor_addr_t *addr);
static void * test_conn_get_basic_setup(const struct testcase_t *tc); static void * test_conn_get_basic_setup(const struct testcase_t *tc);
static int test_conn_get_basic_teardown(const struct testcase_t *tc, static int test_conn_get_basic_teardown(const struct testcase_t *tc,
@ -62,48 +61,7 @@ static int test_conn_get_rsrc_teardown(const struct testcase_t *tc,
#define TEST_CONN_UNATTACHED_STATE (AP_CONN_STATE_CIRCUIT_WAIT) #define TEST_CONN_UNATTACHED_STATE (AP_CONN_STATE_CIRCUIT_WAIT)
#define TEST_CONN_ATTACHED_STATE (AP_CONN_STATE_CONNECT_WAIT) #define TEST_CONN_ATTACHED_STATE (AP_CONN_STATE_CONNECT_WAIT)
#define TEST_CONN_FD_INIT 50 void
static int mock_connection_connect_sockaddr_called = 0;
static int fake_socket_number = TEST_CONN_FD_INIT;
static int
mock_connection_connect_sockaddr(connection_t *conn,
const struct sockaddr *sa,
socklen_t sa_len,
const struct sockaddr *bindaddr,
socklen_t bindaddr_len,
int *socket_error)
{
(void)sa_len;
(void)bindaddr;
(void)bindaddr_len;
tor_assert(conn);
tor_assert(sa);
tor_assert(socket_error);
mock_connection_connect_sockaddr_called++;
conn->s = fake_socket_number++;
tt_assert(SOCKET_OK(conn->s));
/* We really should call tor_libevent_initialize() here. Because we don't,
* we are relying on other parts of the code not checking if the_event_base
* (and therefore event->ev_base) is NULL. */
tt_assert(connection_add_connecting(conn) == 0);
done:
/* Fake "connected" status */
return 1;
}
static int
fake_close_socket(evutil_socket_t sock)
{
(void)sock;
return 0;
}
static void
test_conn_lookup_addr_helper(const char *address, int family, tor_addr_t *addr) test_conn_lookup_addr_helper(const char *address, int family, tor_addr_t *addr)
{ {
int rv = 0; int rv = 0;
@ -122,51 +80,6 @@ test_conn_lookup_addr_helper(const char *address, int family, tor_addr_t *addr)
tor_addr_make_null(addr, TEST_CONN_FAMILY); tor_addr_make_null(addr, TEST_CONN_FAMILY);
} }
static connection_t *
test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose)
{
connection_t *conn = NULL;
tor_addr_t addr;
int socket_err = 0;
int in_progress = 0;
MOCK(connection_connect_sockaddr,
mock_connection_connect_sockaddr);
MOCK(tor_close_socket, fake_close_socket);
init_connection_lists();
conn = connection_new(type, TEST_CONN_FAMILY);
tt_assert(conn);
test_conn_lookup_addr_helper(TEST_CONN_ADDRESS, TEST_CONN_FAMILY, &addr);
tt_assert(!tor_addr_is_null(&addr));
tor_addr_copy_tight(&conn->addr, &addr);
conn->port = TEST_CONN_PORT;
mock_connection_connect_sockaddr_called = 0;
in_progress = connection_connect(conn, TEST_CONN_ADDRESS_PORT, &addr,
TEST_CONN_PORT, &socket_err);
tt_assert(mock_connection_connect_sockaddr_called == 1);
tt_assert(!socket_err);
tt_assert(in_progress == 0 || in_progress == 1);
/* fake some of the attributes so the connection looks OK */
conn->state = state;
conn->purpose = purpose;
assert_connection_ok(conn, time(NULL));
UNMOCK(connection_connect_sockaddr);
UNMOCK(tor_close_socket);
return conn;
/* On failure */
done:
UNMOCK(connection_connect_sockaddr);
UNMOCK(tor_close_socket);
return NULL;
}
static void * static void *
test_conn_get_basic_setup(const struct testcase_t *tc) test_conn_get_basic_setup(const struct testcase_t *tc)
{ {

View File

@ -0,0 +1,13 @@
/* Copyright (c) 2014-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/** Some constants used by test_connection and helpers */
#define TEST_CONN_FAMILY (AF_INET)
#define TEST_CONN_ADDRESS "127.0.0.1"
#define TEST_CONN_PORT (12345)
#define TEST_CONN_ADDRESS_PORT "127.0.0.1:12345"
#define TEST_CONN_FD_INIT 50
void test_conn_lookup_addr_helper(const char *address,
int family, tor_addr_t *addr);

View File

@ -7,9 +7,14 @@
*/ */
#define ROUTERLIST_PRIVATE #define ROUTERLIST_PRIVATE
#define CONNECTION_PRIVATE
#define MAIN_PRIVATE
#include "orconfig.h" #include "orconfig.h"
#include "or.h" #include "or.h"
#include "connection.h"
#include "main.h"
#include "relay.h" #include "relay.h"
#include "routerlist.h" #include "routerlist.h"
#include "nodelist.h" #include "nodelist.h"
@ -17,6 +22,7 @@
#include "test.h" #include "test.h"
#include "test_helpers.h" #include "test_helpers.h"
#include "test_connection.h"
#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS #ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS
DISABLE_GCC_WARNING(overlength-strings) DISABLE_GCC_WARNING(overlength-strings)
@ -143,3 +149,93 @@ mock_tor_addr_lookup__fail_on_bad_addrs(const char *name,
return tor_addr_lookup__real(name, family, out); return tor_addr_lookup__real(name, family, out);
} }
/*********** Helper funcs for making new connections/streams *****************/
/* Helper for test_conn_get_connection() */
static int
fake_close_socket(evutil_socket_t sock)
{
(void)sock;
return 0;
}
static int mock_connection_connect_sockaddr_called = 0;
static int fake_socket_number = TEST_CONN_FD_INIT;
/* Helper for test_conn_get_connection() */
static int
mock_connection_connect_sockaddr(connection_t *conn,
const struct sockaddr *sa,
socklen_t sa_len,
const struct sockaddr *bindaddr,
socklen_t bindaddr_len,
int *socket_error)
{
(void)sa_len;
(void)bindaddr;
(void)bindaddr_len;
tor_assert(conn);
tor_assert(sa);
tor_assert(socket_error);
mock_connection_connect_sockaddr_called++;
conn->s = fake_socket_number++;
tt_assert(SOCKET_OK(conn->s));
/* We really should call tor_libevent_initialize() here. Because we don't,
* we are relying on other parts of the code not checking if the_event_base
* (and therefore event->ev_base) is NULL. */
tt_assert(connection_add_connecting(conn) == 0);
done:
/* Fake "connected" status */
return 1;
}
/** Create and return a new connection/stream */
connection_t *
test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose)
{
connection_t *conn = NULL;
tor_addr_t addr;
int socket_err = 0;
int in_progress = 0;
MOCK(connection_connect_sockaddr,
mock_connection_connect_sockaddr);
MOCK(tor_close_socket, fake_close_socket);
init_connection_lists();
conn = connection_new(type, TEST_CONN_FAMILY);
tt_assert(conn);
test_conn_lookup_addr_helper(TEST_CONN_ADDRESS, TEST_CONN_FAMILY, &addr);
tt_assert(!tor_addr_is_null(&addr));
tor_addr_copy_tight(&conn->addr, &addr);
conn->port = TEST_CONN_PORT;
mock_connection_connect_sockaddr_called = 0;
in_progress = connection_connect(conn, TEST_CONN_ADDRESS_PORT, &addr,
TEST_CONN_PORT, &socket_err);
tt_assert(mock_connection_connect_sockaddr_called == 1);
tt_assert(!socket_err);
tt_assert(in_progress == 0 || in_progress == 1);
/* fake some of the attributes so the connection looks OK */
conn->state = state;
conn->purpose = purpose;
assert_connection_ok(conn, time(NULL));
UNMOCK(connection_connect_sockaddr);
UNMOCK(tor_close_socket);
return conn;
/* On failure */
done:
UNMOCK(connection_connect_sockaddr);
UNMOCK(tor_close_socket);
return NULL;
}

View File

@ -1,9 +1,11 @@
/* Copyright (c) 2014-2017, The Tor Project, Inc. */ /* Copyright (c) 2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */ /* See LICENSE for licensing information */
#ifndef TOR_TEST_HELPERS_H #ifndef TOR_TEST_HELPERS_H
#define TOR_TEST_HELPERS_H #define TOR_TEST_HELPERS_H
#include "or.h"
const char *get_yesterday_date_str(void); const char *get_yesterday_date_str(void);
circuit_t * dummy_origin_circuit_new(int num_cells); circuit_t * dummy_origin_circuit_new(int num_cells);
@ -20,7 +22,10 @@ void connection_write_to_buf_mock(const char *string, size_t len,
int mock_tor_addr_lookup__fail_on_bad_addrs(const char *name, int mock_tor_addr_lookup__fail_on_bad_addrs(const char *name,
uint16_t family, tor_addr_t *out); uint16_t family, tor_addr_t *out);
connection_t *test_conn_get_connection(uint8_t state,
uint8_t type, uint8_t purpose);
extern const char TEST_DESCRIPTORS[]; extern const char TEST_DESCRIPTORS[];
#endif #endif /* TOR_TEST_HELPERS_H */

286
src/test/test_hs_client.c Normal file
View File

@ -0,0 +1,286 @@
/* Copyright (c) 2016-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file test_hs_client.c
* \brief Test prop224 HS client functionality.
*/
#define CRYPTO_PRIVATE
#define MAIN_PRIVATE
#define TOR_CHANNEL_INTERNAL_
#define CIRCUITBUILD_PRIVATE
#define CIRCUITLIST_PRIVATE
#define CONNECTION_PRIVATE
#include "test.h"
#include "test_helpers.h"
#include "log_test_helpers.h"
#include "rend_test_helpers.h"
#include "config.h"
#include "crypto.h"
#include "channeltls.h"
#include "hs_circuit.h"
#include "hs_ident.h"
#include "circuitlist.h"
#include "circuitbuild.h"
#include "connection.h"
#include "connection_edge.h"
static int
mock_connection_ap_handshake_send_begin(entry_connection_t *ap_conn)
{
(void) ap_conn;
return 0;
}
/* Test helper function: Setup a circuit and a stream with the same hidden
* service destination, and put them in <b>circ_out</b> and
* <b>conn_out</b>. Make the stream wait for circuits to be established to the
* hidden service. */
static int
helper_get_circ_and_stream_for_test(origin_circuit_t **circ_out,
connection_t **conn_out,
int is_legacy)
{
int retval;
channel_tls_t *n_chan=NULL;
rend_data_t *conn_rend_data = NULL;
origin_circuit_t *or_circ = NULL;
connection_t *conn = NULL;
ed25519_public_key_t service_pk;
/* Make a dummy connection stream and make it wait for our circuit */
conn = test_conn_get_connection(AP_CONN_STATE_CIRCUIT_WAIT,
CONN_TYPE_AP /* ??? */,
0);
if (is_legacy) {
/* Legacy: Setup rend_data of stream */
char service_id[REND_SERVICE_ID_LEN_BASE32+1] = {0};
TO_EDGE_CONN(conn)->rend_data = mock_rend_data(service_id);
conn_rend_data = TO_EDGE_CONN(conn)->rend_data;
} else {
/* prop224: Setup hs conn identifier on the stream */
ed25519_secret_key_t sk;
tt_int_op(0, OP_EQ, ed25519_secret_key_generate(&sk, 0));
tt_int_op(0, OP_EQ, ed25519_public_key_generate(&service_pk, &sk));
/* Setup hs_conn_identifier of stream */
TO_EDGE_CONN(conn)->hs_ident = hs_ident_edge_conn_new(&service_pk);
}
/* Make it wait for circuit */
connection_ap_mark_as_pending_circuit(TO_ENTRY_CONN(conn));
/* This is needed to silence a BUG warning from
connection_edge_update_circuit_isolation() */
TO_ENTRY_CONN(conn)->original_dest_address =
tor_strdup(TO_ENTRY_CONN(conn)->socks_request->address);
/****************************************************/
/* Now make dummy circuit */
or_circ = origin_circuit_new();
or_circ->base_.purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED;
or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
or_circ->build_state->is_internal = 1;
if (is_legacy) {
/* Legacy: Setup rend data and final cpath */
or_circ->build_state->pending_final_cpath =
tor_malloc_zero(sizeof(crypt_path_t));
or_circ->build_state->pending_final_cpath->magic = CRYPT_PATH_MAGIC;
or_circ->build_state->pending_final_cpath->rend_dh_handshake_state =
crypto_dh_new(DH_TYPE_REND);
tt_assert(
or_circ->build_state->pending_final_cpath->rend_dh_handshake_state);
retval = crypto_dh_generate_public(
or_circ->build_state->pending_final_cpath->rend_dh_handshake_state);
tt_int_op(retval, ==, 0);
or_circ->rend_data = rend_data_dup(conn_rend_data);
} else {
/* prop224: Setup hs ident on the circuit */
or_circ->hs_ident = hs_ident_circuit_new(&service_pk,
HS_IDENT_CIRCUIT_RENDEZVOUS);
}
TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN;
/* fake n_chan */
n_chan = tor_malloc_zero(sizeof(channel_tls_t));
n_chan->base_.global_identifier = 1;
or_circ->base_.n_chan = &(n_chan->base_);
*circ_out = or_circ;
*conn_out = conn;
return 0;
done:
/* something failed */
return -1;
}
/* Test: Ensure that setting up legacy e2e rendezvous circuits works
* correctly. */
static void
test_e2e_rend_circuit_setup_legacy(void *arg)
{
int retval;
origin_circuit_t *or_circ = NULL;
connection_t *conn = NULL;
(void) arg;
/** In this test we create a v2 legacy HS stream and a circuit with the same
* hidden service destination. We make the stream wait for circuits to be
* established to the hidden service, and then we complete the circuit using
* the hs_circuit_setup_e2e_rend_circ_legacy_client() function. We then
* check that the end-to-end cpath was setup correctly and that the stream
* was attached to the circuit as expected. */
MOCK(connection_ap_handshake_send_begin,
mock_connection_ap_handshake_send_begin);
/* Setup */
retval = helper_get_circ_and_stream_for_test( &or_circ, &conn, 1);
tt_int_op(retval, OP_EQ, 0);
tt_assert(or_circ);
tt_assert(conn);
/* Check number of hops */
retval = cpath_get_n_hops(&or_circ->cpath);
tt_int_op(retval, ==, 0);
/* Check that our stream is not attached on any circuits */
tt_assert(!TO_EDGE_CONN(conn)->on_circuit);
/********************************************** */
/* Make a good RENDEZVOUS1 cell body because it needs to pass key exchange
* digest verification... */
uint8_t rend_cell_body[DH_KEY_LEN+DIGEST_LEN] = {2};
{
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN];
crypto_dh_t *dh_state =
or_circ->build_state->pending_final_cpath->rend_dh_handshake_state;
/* compute and overwrite digest of cell body with the right value */
retval = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh_state,
(char*)rend_cell_body, DH_KEY_LEN,
keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN);
tt_int_op(retval, OP_GT, 0);
memcpy(rend_cell_body+DH_KEY_LEN, keys, DIGEST_LEN);
}
/* Setup the circuit */
retval = hs_circuit_setup_e2e_rend_circ_legacy_client(or_circ,
rend_cell_body);
tt_int_op(retval, OP_EQ, 0);
/**********************************************/
/* See that a hop was added to the circuit's cpath */
retval = cpath_get_n_hops(&or_circ->cpath);
tt_int_op(retval, OP_EQ, 1);
/* Check the digest algo */
tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->f_digest),
OP_EQ, DIGEST_SHA1);
tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->b_digest),
OP_EQ, DIGEST_SHA1);
tt_assert(or_circ->cpath->f_crypto);
tt_assert(or_circ->cpath->b_crypto);
/* Ensure that circ purpose was changed */
tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED);
/* Test that stream got attached */
tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ));
done:
connection_free_(conn);
tor_free(TO_CIRCUIT(or_circ)->n_chan);
circuit_free(TO_CIRCUIT(or_circ));
}
/* Test: Ensure that setting up v3 rendezvous circuits works correctly. */
static void
test_e2e_rend_circuit_setup(void *arg)
{
uint8_t ntor_key_seed[DIGEST256_LEN] = {0};
origin_circuit_t *or_circ;
int retval;
connection_t *conn = NULL;
(void) arg;
/** In this test we create a prop224 v3 HS stream and a circuit with the same
* hidden service destination. We make the stream wait for circuits to be
* established to the hidden service, and then we complete the circuit using
* the hs_circuit_setup_e2e_rend_circ() function. We then check that the
* end-to-end cpath was setup correctly and that the stream was attached to
* the circuit as expected. */
MOCK(connection_ap_handshake_send_begin,
mock_connection_ap_handshake_send_begin);
/* Setup */
retval = helper_get_circ_and_stream_for_test( &or_circ, &conn, 0);
tt_int_op(retval, OP_EQ, 0);
tt_assert(or_circ);
tt_assert(conn);
/* Check number of hops: There should be no hops yet to this circ */
retval = cpath_get_n_hops(&or_circ->cpath);
tt_int_op(retval, ==, 0);
tt_assert(!or_circ->cpath);
/* Check that our stream is not attached on any circuits */
tt_assert(!TO_EDGE_CONN(conn)->on_circuit);
/**********************************************/
/* Setup the circuit */
retval = hs_circuit_setup_e2e_rend_circ(or_circ,
ntor_key_seed, sizeof(ntor_key_seed),
0);
tt_int_op(retval, OP_EQ, 0);
/**********************************************/
/* See that a hop was added to the circuit's cpath */
retval = cpath_get_n_hops(&or_circ->cpath);
tt_int_op(retval, OP_EQ, 1);
/* Check that the crypt path has prop224 algorithm parameters */
tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->f_digest),
OP_EQ, DIGEST_SHA3_256);
tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->b_digest),
OP_EQ, DIGEST_SHA3_256);
tt_assert(or_circ->cpath->f_crypto);
tt_assert(or_circ->cpath->b_crypto);
/* Ensure that circ purpose was changed */
tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED);
/* Test that stream got attached */
tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ));
done:
connection_free_(conn);
tor_free(TO_CIRCUIT(or_circ)->n_chan);
circuit_free(TO_CIRCUIT(or_circ));
}
struct testcase_t hs_client_tests[] = {
{ "e2e_rend_circuit_setup_legacy", test_e2e_rend_circuit_setup_legacy,
TT_FORK, NULL, NULL },
{ "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup,
TT_FORK, NULL, NULL },
END_OF_TESTCASES
};

View File

@ -6,20 +6,34 @@
* \brief Test hidden service functionality. * \brief Test hidden service functionality.
*/ */
#define CIRCUITBUILD_PRIVATE
#define CIRCUITLIST_PRIVATE
#define CONNECTION_PRIVATE
#define CRYPTO_PRIVATE
#define HS_COMMON_PRIVATE #define HS_COMMON_PRIVATE
#define HS_SERVICE_PRIVATE
#define HS_INTROPOINT_PRIVATE #define HS_INTROPOINT_PRIVATE
#define MAIN_PRIVATE
#define TOR_CHANNEL_INTERNAL_
#include "test.h" #include "test.h"
#include "log_test_helpers.h" #include "log_test_helpers.h"
#include "crypto.h" #include "rend_test_helpers.h"
#include "hs/cell_establish_intro.h" #include "or.h"
#include "channeltls.h"
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuituse.h"
#include "config.h"
#include "connection.h"
#include "hs_circuit.h"
#include "hs_common.h" #include "hs_common.h"
#include "hs_service.h" #include "hs_ident.h"
#include "hs_intropoint.h" #include "hs_intropoint.h"
#include "hs_ntor.h" #include "hs_ntor.h"
#include "hs_service.h"
#include "main.h"
#include "rendservice.h"
/** We simulate the creation of an outgoing ESTABLISH_INTRO cell, and then we /** We simulate the creation of an outgoing ESTABLISH_INTRO cell, and then we
* parse it from the receiver side. */ * parse it from the receiver side. */
@ -235,6 +249,72 @@ test_time_period(void *arg)
; ;
} }
/* Test: Ensure that setting up rendezvous circuits works correctly. */
static void
test_e2e_rend_circuit_setup(void *arg)
{
ed25519_public_key_t service_pk;
origin_circuit_t *or_circ;
int retval;
/** In this test we create a v3 prop224 service-side rendezvous circuit.
* We simulate an HS ntor key exchange with a client, and check that
* the circuit was setup correctly and is ready to accept rendezvous data */
(void) arg;
/* Now make dummy circuit */
{
or_circ = origin_circuit_new();
or_circ->base_.purpose = CIRCUIT_PURPOSE_S_CONNECT_REND;
or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
or_circ->build_state->is_internal = 1;
/* prop224: Setup hs conn identifier on the stream */
ed25519_secret_key_t sk;
tt_int_op(0, OP_EQ, ed25519_secret_key_generate(&sk, 0));
tt_int_op(0, OP_EQ, ed25519_public_key_generate(&service_pk, &sk));
or_circ->hs_ident = hs_ident_circuit_new(&service_pk,
HS_IDENT_CIRCUIT_RENDEZVOUS);
TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN;
}
/* Check number of hops */
retval = cpath_get_n_hops(&or_circ->cpath);
tt_int_op(retval, OP_EQ, 0);
/* Setup the circuit: do the ntor key exchange */
{
uint8_t ntor_key_seed[DIGEST256_LEN] = {2};
retval = hs_circuit_setup_e2e_rend_circ(or_circ,
ntor_key_seed, sizeof(ntor_key_seed),
1);
tt_int_op(retval, OP_EQ, 0);
}
/* See that a hop was added to the circuit's cpath */
retval = cpath_get_n_hops(&or_circ->cpath);
tt_int_op(retval, OP_EQ, 1);
/* Check the digest algo */
tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->f_digest),
OP_EQ, DIGEST_SHA3_256);
tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->b_digest),
OP_EQ, DIGEST_SHA3_256);
tt_assert(or_circ->cpath->f_crypto);
tt_assert(or_circ->cpath->b_crypto);
/* Ensure that circ purpose was changed */
tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_S_REND_JOINED);
done:
circuit_free(TO_CIRCUIT(or_circ));
}
struct testcase_t hs_service_tests[] = { struct testcase_t hs_service_tests[] = {
{ "gen_establish_intro_cell", test_gen_establish_intro_cell, TT_FORK, { "gen_establish_intro_cell", test_gen_establish_intro_cell, TT_FORK,
NULL, NULL }, NULL, NULL },
@ -244,6 +324,8 @@ struct testcase_t hs_service_tests[] = {
NULL, NULL }, NULL, NULL },
{ "time_period", test_time_period, TT_FORK, { "time_period", test_time_period, TT_FORK,
NULL, NULL }, NULL, NULL },
{ "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, TT_FORK,
NULL, NULL },
END_OF_TESTCASES END_OF_TESTCASES
}; };

View File

@ -21,22 +21,6 @@ static const int TIME_IN_THE_PAST = -(REND_CACHE_MAX_AGE + \
REND_CACHE_MAX_SKEW + 60); REND_CACHE_MAX_SKEW + 60);
static const int TIME_IN_THE_FUTURE = REND_CACHE_MAX_SKEW + 60; static const int TIME_IN_THE_FUTURE = REND_CACHE_MAX_SKEW + 60;
static rend_data_t *
mock_rend_data(const char *onion_address)
{
rend_data_v2_t *v2_data = tor_malloc_zero(sizeof(*v2_data));
rend_data_t *rend_query = &v2_data->base_;
rend_query->version = 2;
strlcpy(v2_data->onion_address, onion_address,
sizeof(v2_data->onion_address));
v2_data->auth_type = REND_NO_AUTH;
rend_query->hsdirs_fp = smartlist_new();
smartlist_add(rend_query->hsdirs_fp, tor_memdup("aaaaaaaaaaaaaaaaaaaaaaaa",
DIGEST_LEN));
return rend_query;
}
static void static void
test_rend_cache_lookup_entry(void *data) test_rend_cache_lookup_entry(void *data)
{ {