test: Test that client picks the right HSDir for service.

This test is important because it tests that upload_descriptor_to_all()
is in synch with pick_hsdir_v3(). That's not the case for the
reachability test which just compares the responsible hsdir sets.
This commit is contained in:
George Kadianakis 2017-09-08 18:43:45 +03:00
parent e0371935a1
commit 0307e7e0e7
3 changed files with 247 additions and 2 deletions

View File

@ -6,6 +6,8 @@
* \brief Implement next generation hidden service client functionality
**/
#define HS_CLIENT_PRIVATE
#include "or.h"
#include "hs_circuit.h"
#include "hs_ident.h"
@ -158,7 +160,7 @@ directory_launch_v3_desc_fetch(const ed25519_public_key_t *onion_identity_pk,
/** Return the HSDir we should use to fetch the descriptor of the hidden
* service with identity key <b>onion_identity_pk</b>. */
static routerstatus_t *
STATIC routerstatus_t *
pick_hsdir_v3(const ed25519_public_key_t *onion_identity_pk)
{
int retval;

View File

@ -48,5 +48,12 @@ int hs_client_reextend_intro_circuit(origin_circuit_t *circ);
void hs_client_free_all(void);
#ifdef HS_CLIENT_PRIVATE
STATIC routerstatus_t *
pick_hsdir_v3(const ed25519_public_key_t *onion_identity_pk);
#endif
#endif /* TOR_HS_CLIENT_H */

View File

@ -7,6 +7,7 @@
*/
#define HS_COMMON_PRIVATE
#define HS_CLIENT_PRIVATE
#define HS_SERVICE_PRIVATE
#define NODELIST_PRIVATE
@ -17,6 +18,7 @@
#include "connection_edge.h"
#include "hs_common.h"
#include "hs_client.h"
#include "hs_service.h"
#include "config.h"
#include "networkstatus.h"
@ -336,6 +338,17 @@ mock_networkstatus_get_latest_consensus(void)
return mock_ns;
}
static networkstatus_t *
mock_networkstatus_get_live_consensus(time_t now)
{
(void) now;
tt_assert(mock_ns);
done:
return mock_ns;
}
/** Test the responsible HSDirs calculation function */
static void
test_responsible_hsdirs(void *arg)
@ -1416,6 +1429,228 @@ test_reachability(void *arg)
}
}
/** Pick an HSDir for service with <b>onion_identity_pk</b> as a client. Put
* its identity digest in <b>hsdir_digest_out</b>. */
static void
helper_client_pick_hsdir(const ed25519_public_key_t *onion_identity_pk,
char *hsdir_digest_out)
{
tt_assert(onion_identity_pk);
routerstatus_t *client_hsdir = pick_hsdir_v3(onion_identity_pk);
tt_assert(client_hsdir);
digest_to_base64(hsdir_digest_out, client_hsdir->identity_digest);
done:
;
}
/** Set the consensus and system time based on <b>between_srv_and_tp</b>. If
* <b>between_srv_and_tp</b> is set, then set the time to be inside the time
* segment between SRV#N and TP#N. */
static time_t
helper_set_consensus_and_system_time(networkstatus_t *ns,
int between_srv_and_tp)
{
time_t real_time;
/* The period between SRV#N and TP#N is from 00:00 to 12:00 UTC. Consensus
* valid_after is what matters here, the rest is just to specify the voting
* period correctly. */
if (between_srv_and_tp) {
parse_rfc1123_time("Wed, 13 Apr 2016 11:00:00 UTC", &ns->valid_after);
parse_rfc1123_time("Wed, 13 Apr 2016 12:00:00 UTC", &ns->fresh_until);
parse_rfc1123_time("Wed, 13 Apr 2016 14:00:00 UTC", &ns->valid_until);
} else {
parse_rfc1123_time("Wed, 13 Apr 2016 13:00:00 UTC", &ns->valid_after);
parse_rfc1123_time("Wed, 13 Apr 2016 14:00:00 UTC", &ns->fresh_until);
parse_rfc1123_time("Wed, 13 Apr 2016 16:00:00 UTC", &ns->valid_until);
}
/* Set system time: pretend to be just 2 minutes before consensus expiry */
real_time = ns->valid_until - 120;
update_approx_time(real_time);
return real_time;
}
/** Helper function that carries out the actual test for
* test_client_service_sync() */
static void
helper_test_hsdir_sync(networkstatus_t *ns,
int service_between_srv_and_tp,
int client_between_srv_and_tp,
int client_fetches_next_desc)
{
hs_service_descriptor_t *desc;
int retval;
/** Test logic:
* 1) Initialize service time: consensus and system time.
* 1.1) Initialize service hash ring
* 2) Initialize service and publish descriptors.
* 3) Initialize client time: consensus and system time.
* 3.1) Initialize client hash ring
* 4) Try to fetch descriptor as client, and CHECK that the HSDir picked by
* the client was also picked by service.
*/
cleanup_nodelist();
smartlist_clear(ns->routerstatus_list);
/* 1) Initialize service time: consensus and real time */
time_t now = helper_set_consensus_and_system_time(ns,
service_between_srv_and_tp);
helper_initialize_big_hash_ring(ns);
/* 2) Initialize service */
hs_service_t *service = helper_init_service(now);
desc = client_fetches_next_desc ? service->desc_next : service->desc_current;
/* Now let's upload our desc to all hsdirs */
upload_descriptor_to_all(service, desc);
/* Check that previous hsdirs were populated */
tt_int_op(smartlist_len(desc->previous_hsdirs), OP_EQ, 6);
/* 3) Initialize client time */
now = helper_set_consensus_and_system_time(ns, client_between_srv_and_tp);
cleanup_nodelist();
smartlist_clear(ns->routerstatus_list);
helper_initialize_big_hash_ring(ns);
/* 4) Fetch desc as client */
char client_hsdir_b64_digest[BASE64_DIGEST_LEN+1] = {0};
helper_client_pick_hsdir(&service->keys.identity_pk,
client_hsdir_b64_digest);
/* CHECK: Go through the hsdirs chosen by the service and make sure that it
* contains the one picked by the client! */
retval = smartlist_contains_string(desc->previous_hsdirs,
client_hsdir_b64_digest);
tt_int_op(retval, OP_EQ, 1);
done:
/* At the end: free all services and initialize the subsystem again, we will
* need it for next scenario. */
hs_service_free_all();
hs_service_init();
}
/** This test ensures that client and service will pick the same HSDirs, under
* various timing scenarios:
* a) Scenario where both client and service are in the time segment between
* SRV#N and TP#N:
* b) Scenario where both client and service are in the time segment between
* TP#N and SRV#N+1.
* c) Scenario where service is between SRV#N and TP#N, but client is between
* TP#N and SRV#N+1.
* d) Scenario where service is between TP#N and SRV#N+1, but client is
* between SRV#N and TP#N.
*
* This test is important because it tests that upload_descriptor_to_all() is
* in synch with pick_hsdir_v3(). That's not the case for the
* test_reachability() test which only compares the responsible hsdir sets.
*/
static void
test_client_service_hsdir_set_sync(void *arg)
{
networkstatus_t *ns = NULL;
(void) arg;
MOCK(networkstatus_get_latest_consensus,
mock_networkstatus_get_latest_consensus);
MOCK(networkstatus_get_live_consensus,
mock_networkstatus_get_live_consensus);
MOCK(get_or_state,
get_or_state_replacement);
MOCK(hs_desc_encode_descriptor,
mock_hs_desc_encode_descriptor);
MOCK(directory_initiate_request,
mock_directory_initiate_request);
hs_init();
/* Initialize a big hash ring: we want it to be big so that client and
* service cannot accidentally select the same HSDirs */
ns = networkstatus_get_latest_consensus();
tt_assert(ns);
/** Now test the various synch scenarios. See the helper function for more
details: */
/* a) Scenario where both client and service are in the time segment between
* SRV#N and TP#N. At this time the client fetches the first HS desc:
*
* +------------------------------------------------------------------+
* | |
* | 00:00 12:00 00:00 12:00 00:00 12:00 |
* | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
* | |
* | $==========|-----------$===========|----------$===========| |
* | ^ ^ |
* | S C |
* +------------------------------------------------------------------+
*/
helper_test_hsdir_sync(ns, 1, 1, 0);
/* b) Scenario where both client and service are in the time segment between
* TP#N and SRV#N+1. At this time the client fetches the second HS
* desc:
*
* +------------------------------------------------------------------+
* | |
* | 00:00 12:00 00:00 12:00 00:00 12:00 |
* | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
* | |
* | $==========|-----------$===========|-----------$===========| |
* | ^ ^ |
* | S C |
* +------------------------------------------------------------------+
*/
helper_test_hsdir_sync(ns, 0, 0, 1);
/* c) Scenario where service is between SRV#N and TP#N, but client is
* between TP#N and SRV#N+1. Client is forward in time so it fetches the
* second HS desc.
*
* +------------------------------------------------------------------+
* | |
* | 00:00 12:00 00:00 12:00 00:00 12:00 |
* | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
* | |
* | $==========|-----------$===========|-----------$===========| |
* | ^ ^ |
* | S C |
* +------------------------------------------------------------------+
*/
helper_test_hsdir_sync(ns, 1, 0, 1);
/* d) Scenario where service is between TP#N and SRV#N+1, but client is
* between SRV#N and TP#N. Client is backwards in time so it fetches the
* first HS desc.
*
* +------------------------------------------------------------------+
* | |
* | 00:00 12:00 00:00 12:00 00:00 12:00 |
* | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
* | |
* | $==========|-----------$===========|-----------$===========| |
* | ^ ^ |
* | C S |
* +------------------------------------------------------------------+
*/
helper_test_hsdir_sync(ns, 0, 1, 0);
done:
SMARTLIST_FOREACH(ns->routerstatus_list,
routerstatus_t *, rs, routerstatus_free(rs));
smartlist_clear(ns->routerstatus_list);
networkstatus_vote_free(ns);
nodelist_free_all();
hs_free_all();
}
struct testcase_t hs_common_tests[] = {
{ "build_address", test_build_address, TT_FORK,
NULL, NULL },
@ -1439,7 +1674,8 @@ struct testcase_t hs_common_tests[] = {
NULL, NULL },
{ "reachability", test_reachability, TT_FORK,
NULL, NULL },
{ "client_service_hsdir_set_sync", test_client_service_hsdir_set_sync,
TT_FORK, NULL, NULL },
END_OF_TESTCASES
};