diff options
author | George Kadianakis <desnacked@riseup.net> | 2017-08-29 18:28:11 +0300 |
---|---|---|
committer | George Kadianakis <desnacked@riseup.net> | 2017-09-15 12:46:26 +0300 |
commit | e9b4624cc589d830d7a78128649f7945ac808737 (patch) | |
tree | 27fbe464cbf0a8cb4824fd8f647c3271afcbbe1d | |
parent | 29aa400908ff89c72fd9fbd723793058d73ce35f (diff) | |
download | tor-e9b4624cc589d830d7a78128649f7945ac808737.tar.gz tor-e9b4624cc589d830d7a78128649f7945ac808737.zip |
test: Add HS v3 client-side test for picking intro points
This commit adds a pretty advanced test for the client-side making sure that
picking intro is done properly.
This unittest also reveals a memleak on the client_pick_intro() function which
is fixed by the subsequent commit.
Signed-off-by: David Goulet <dgoulet@torproject.org>
-rw-r--r-- | src/or/hs_client.c | 4 | ||||
-rw-r--r-- | src/or/hs_client.h | 8 | ||||
-rw-r--r-- | src/test/test_hs_client.c | 176 |
3 files changed, 185 insertions, 3 deletions
diff --git a/src/or/hs_client.c b/src/or/hs_client.c index be5ece068c..19359d260e 100644 --- a/src/or/hs_client.c +++ b/src/or/hs_client.c @@ -521,7 +521,7 @@ client_rendezvous_circ_has_opened(origin_circuit_t *circ) * to a newly allocated extend_info_t object fully initialized. Return NULL if * we can't convert it for which chances are that we are missing or malformed * link specifiers. */ -static extend_info_t * +STATIC extend_info_t * desc_intro_point_to_extend_info(const hs_desc_intro_point_t *ip) { extend_info_t *ei; @@ -594,7 +594,7 @@ intro_point_is_usable(const ed25519_public_key_t *service_pk, /* Using a descriptor desc, return a newly allocated extend_info_t object of a * randomly picked introduction point from its list. Return NULL if none are * usable. */ -static extend_info_t * +STATIC extend_info_t * client_get_random_intro(const ed25519_public_key_t *service_pk) { extend_info_t *ei = NULL, *ei_excluded = NULL; diff --git a/src/or/hs_client.h b/src/or/hs_client.h index 3ea2b8cdf9..d50d346217 100644 --- a/src/or/hs_client.h +++ b/src/or/hs_client.h @@ -71,7 +71,13 @@ void hs_client_free_all(void); STATIC routerstatus_t * pick_hsdir_v3(const ed25519_public_key_t *onion_identity_pk); -#endif +STATIC extend_info_t * +client_get_random_intro(const ed25519_public_key_t *service_pk); + +STATIC extend_info_t * +desc_intro_point_to_extend_info(const hs_desc_intro_point_t *ip); + +#endif /* HS_CLIENT_PRIVATE */ #endif /* TOR_HS_CLIENT_H */ diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c index af5f5cb576..125a14e5d2 100644 --- a/src/test/test_hs_client.c +++ b/src/test/test_hs_client.c @@ -8,6 +8,7 @@ #define CRYPTO_PRIVATE #define MAIN_PRIVATE +#define HS_CLIENT_PRIVATE #define TOR_CHANNEL_INTERNAL_ #define CIRCUITBUILD_PRIVATE #define CIRCUITLIST_PRIVATE @@ -17,17 +18,22 @@ #include "test_helpers.h" #include "log_test_helpers.h" #include "rend_test_helpers.h" +#include "hs_test_helpers.h" #include "config.h" #include "crypto.h" #include "channeltls.h" +#include "routerset.h" #include "hs_circuit.h" +#include "hs_client.h" #include "hs_ident.h" +#include "hs_cache.h" #include "circuitlist.h" #include "circuitbuild.h" #include "connection.h" #include "connection_edge.h" +#include "networkstatus.h" static int mock_connection_ap_handshake_send_begin(entry_connection_t *ap_conn) @@ -36,6 +42,15 @@ mock_connection_ap_handshake_send_begin(entry_connection_t *ap_conn) return 0; } +static networkstatus_t mock_ns; + +static networkstatus_t * +mock_networkstatus_get_live_consensus(time_t now) +{ + (void) now; + return &mock_ns; +} + /* 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 @@ -276,11 +291,172 @@ test_e2e_rend_circuit_setup(void *arg) circuit_free(TO_CIRCUIT(or_circ)); } +/** Test client logic for picking intro points from a descriptor. Also test how + * ExcludeNodes and intro point failures affect picking intro points. */ +static void +test_client_pick_intro(void *arg) +{ + int ret; + ed25519_keypair_t service_kp; + hs_descriptor_t *desc = NULL; + + MOCK(networkstatus_get_live_consensus, + mock_networkstatus_get_live_consensus); + + (void) arg; + + hs_init(); + + /* Generate service keypair */ + tt_int_op(0, OP_EQ, ed25519_keypair_generate(&service_kp, 0)); + + /* Set time */ + ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC", + &mock_ns.valid_after); + tt_int_op(ret, OP_EQ, 0); + ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC", + &mock_ns.fresh_until); + tt_int_op(ret, OP_EQ, 0); + + update_approx_time(mock_ns.fresh_until-10); + time_t now = approx_time(); + + /* Test logic: + * + * 1) Add our desc with intro points to the HS cache. + * + * 2) Mark all descriptor intro points except _the chosen one_ as + * failed. Then query the desc to get a random intro: check that we got + * _the chosen one_. Then fail the chosen one as well, and see that no + * intros are returned. + * + * 3) Then clean the intro state cache and get an intro point. + * + * 4) Try fetching an intro with the wrong service key: shouldn't work + * + * 5) Set StrictNodes and put all our intro points in ExcludeNodes: see that + * nothing is returned. + */ + + /* 1) Add desc to HS cache */ + { + char *encoded = NULL; + desc = hs_helper_build_hs_desc_with_ip(&service_kp); + ret = hs_desc_encode_descriptor(desc, &service_kp, &encoded); + tt_int_op(ret, OP_EQ, 0); + tt_assert(encoded); + + /* store it */ + hs_cache_store_as_client(encoded, &service_kp.pubkey); + + /* fetch it to make sure it works */ + const hs_descriptor_t *fetched_desc = + hs_cache_lookup_as_client(&service_kp.pubkey); + tt_assert(fetched_desc); + tt_mem_op(fetched_desc->subcredential, OP_EQ, desc->subcredential, + DIGEST256_LEN); + tt_assert(!tor_mem_is_zero((char*)fetched_desc->subcredential, + DIGEST256_LEN)); + tor_free(encoded); + } + + /* 2) Mark all intro points except _the chosen one_ as failed. Then query the + * desc and get a random intro: check that we got _the chosen one_. */ + { + /* Pick the chosen intro point and get its ei */ + hs_desc_intro_point_t *chosen_intro_point = + smartlist_get(desc->encrypted_data.intro_points, 0); + extend_info_t *chosen_intro_ei = + desc_intro_point_to_extend_info(chosen_intro_point); + tt_assert(chosen_intro_point); + tt_assert(chosen_intro_ei); + + /* Now mark all other intro points as failed */ + SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points, + hs_desc_intro_point_t *, ip) { + /* Skip the chosen intro point */ + if (ip == chosen_intro_point) { + continue; + } + ed25519_public_key_t *intro_auth_key = &ip->auth_key_cert->signed_key; + hs_cache_client_intro_state_note(&service_kp.pubkey, + intro_auth_key, + INTRO_POINT_FAILURE_GENERIC); + } SMARTLIST_FOREACH_END(ip); + + /* Try to get a random intro: Should return the chosen one! */ + extend_info_t *ip = client_get_random_intro(&service_kp.pubkey); + tor_assert(ip); + tt_assert(!tor_mem_is_zero((char*)ip->identity_digest, DIGEST_LEN)); + tt_mem_op(ip->identity_digest, OP_EQ, chosen_intro_ei->identity_digest, + DIGEST_LEN); + + extend_info_free(chosen_intro_ei); + extend_info_free(ip); + + /* Now also mark the chosen one as failed: See that we can't get any intro + points anymore. */ + hs_cache_client_intro_state_note(&service_kp.pubkey, + &chosen_intro_point->auth_key_cert->signed_key, + INTRO_POINT_FAILURE_TIMEOUT); + ip = client_get_random_intro(&service_kp.pubkey); + tor_assert(!ip); + } + + /* 3) Clean the intro state cache and get an intro point */ + { + /* Pretend we are 5 mins in the future and order a cleanup of the intro + * state. This should clean up the intro point failures and allow us to get + * an intro. */ + hs_cache_client_intro_state_clean(now + 5*60); + + /* Get an intro. It should work! */ + extend_info_t *ip = client_get_random_intro(&service_kp.pubkey); + tor_assert(ip); + extend_info_free(ip); + } + + /* 4) Try fetching an intro with the wrong service key: shouldn't work */ + { + ed25519_keypair_t dummy_kp; + tt_int_op(0, OP_EQ, ed25519_keypair_generate(&dummy_kp, 0)); + extend_info_t *ip = client_get_random_intro(&dummy_kp.pubkey); + tor_assert(!ip); + } + + /* 5) Set StrictNodes and put all our intro points in ExcludeNodes: see that + * nothing is returned. */ + { + get_options_mutable()->ExcludeNodes = routerset_new(); + get_options_mutable()->StrictNodes = 1; + SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points, + hs_desc_intro_point_t *, ip) { + extend_info_t *intro_ei = desc_intro_point_to_extend_info(ip); + if (intro_ei) { + char *ip_addr = tor_addr_to_str_dup(&intro_ei->addr); + tor_assert(ip_addr); + ret =routerset_parse(get_options_mutable()->ExcludeNodes, ip_addr, ""); + tt_int_op(ret, OP_EQ, 0); + tor_free(ip_addr); + extend_info_free(intro_ei); + } + } SMARTLIST_FOREACH_END(ip); + + extend_info_t *ip = client_get_random_intro(&service_kp.pubkey); + tt_assert(!ip); + } + + done: + hs_descriptor_free(desc); +} + 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 }, + { "client_pick_intro", test_client_pick_intro, + TT_FORK, NULL, NULL }, END_OF_TESTCASES }; |