[tor-commits] [tor/master] test: Add HS v3 client-side test for picking intro points

nickm at torproject.org nickm at torproject.org
Fri Sep 15 13:04:34 UTC 2017


commit e9b4624cc589d830d7a78128649f7945ac808737
Author: George Kadianakis <desnacked at riseup.net>
Date:   Tue Aug 29 18:28:11 2017 +0300

    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 at torproject.org>
---
 src/or/hs_client.c        |   4 +-
 src/or/hs_client.h        |   8 ++-
 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 be5ece068..19359d260 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 3ea2b8cdf..d50d34621 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 af5f5cb57..125a14e5d 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
 };
 





More information about the tor-commits mailing list