Browse Source

prop224: Add unittests handling v3 ESTABLISH_INTRO cells.

Test for both v2 and v3 ESTABLISH_INTRO handling.
George Kadianakis 7 years ago
parent
commit
297213825b

+ 5 - 5
src/common/crypto_ed25519.c

@@ -267,11 +267,11 @@ ed25519_sign(ed25519_signature_t *signature_out,
  * Like ed25519_sign(), but also prefix <b>msg</b> with <b>prefix_str</b>
  * before signing. <b>prefix_str</b> must be a NUL-terminated string.
  */
-int
-ed25519_sign_prefixed(ed25519_signature_t *signature_out,
-                      const uint8_t *msg, size_t msg_len,
-                      const char *prefix_str,
-                      const ed25519_keypair_t *keypair)
+MOCK_IMPL(int,
+ed25519_sign_prefixed,(ed25519_signature_t *signature_out,
+                       const uint8_t *msg, size_t msg_len,
+                       const char *prefix_str,
+                       const ed25519_keypair_t *keypair))
 {
   int retval;
   size_t prefixed_msg_len;

+ 6 - 5
src/common/crypto_ed25519.h

@@ -55,11 +55,12 @@ int ed25519_checksig(const ed25519_signature_t *signature,
                      const uint8_t *msg, size_t len,
                      const ed25519_public_key_t *pubkey);
 
-int
-ed25519_sign_prefixed(ed25519_signature_t *signature_out,
-                      const uint8_t *msg, size_t len,
-                      const char *prefix_str,
-                      const ed25519_keypair_t *keypair);
+MOCK_DECL(int,
+ed25519_sign_prefixed,(ed25519_signature_t *signature_out,
+                       const uint8_t *msg, size_t len,
+                       const char *prefix_str,
+                       const ed25519_keypair_t *keypair));
+
 int
 ed25519_checksig_prefixed(const ed25519_signature_t *signature,
                           const uint8_t *msg, size_t len,

+ 2 - 0
src/test/include.am

@@ -96,6 +96,8 @@ src_test_test_SOURCES = \
 	src/test/test_guardfraction.c \
 	src/test/test_extorport.c \
 	src/test/test_hs.c \
+	src/test/test_hs_service.c \
+	src/test/test_hs_intropoint.c \
 	src/test/test_handles.c \
 	src/test/test_hs_cache.c \
 	src/test/test_hs_descriptor.c \

+ 3 - 1
src/test/test.c

@@ -1204,9 +1204,11 @@ struct testgroup_t testgroups[] = {
   { "entrynodes/", entrynodes_tests },
   { "guardfraction/", guardfraction_tests },
   { "extorport/", extorport_tests },
-  { "hs/", hs_tests },
+  { "legacy_hs/", hs_tests },
   { "hs_cache/", hs_cache },
   { "hs_descriptor/", hs_descriptor },
+  { "hs_service/", hs_service_tests },
+  { "hs_intropoint/", hs_intropoint_tests },
   { "introduce/", introduce_tests },
   { "keypin/", keypin_tests },
   { "link-handshake/", link_handshake_tests },

+ 2 - 0
src/test/test.h

@@ -201,6 +201,8 @@ extern struct testcase_t extorport_tests[];
 extern struct testcase_t hs_tests[];
 extern struct testcase_t hs_cache[];
 extern struct testcase_t hs_descriptor[];
+extern struct testcase_t hs_service_tests[];
+extern struct testcase_t hs_intropoint_tests[];
 extern struct testcase_t introduce_tests[];
 extern struct testcase_t keypin_tests[];
 extern struct testcase_t link_handshake_tests[];

+ 360 - 0
src/test/test_hs_intropoint.c

@@ -0,0 +1,360 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_hs_service.c
+ * \brief Test hidden service functionality.
+ */
+
+#define HS_SERVICE_PRIVATE
+#define HS_INTROPOINT_PRIVATE
+#define RENDSERVICE_PRIVATE
+#define CIRCUITLIST_PRIVATE
+
+#include "test.h"
+#include "crypto.h"
+
+#include "or.h"
+#include "ht.h"
+
+#include "hs/cell_establish_intro.h"
+#include "hs_service.h"
+#include "hs_circuitmap.h"
+#include "hs_intropoint.h"
+
+#include "circuitlist.h"
+#include "circuituse.h"
+#include "rendservice.h"
+
+/* Mock function to avoid networking in unittests */
+static int
+mock_send_intro_established_cell(or_circuit_t *circ)
+{
+  (void) circ;
+  return 0;
+}
+
+/* Try sending an ESTABLISH_INTRO cell on a circuit that is already an intro
+ * point. Should fail. */
+static void
+test_establish_intro_wrong_purpose(void *arg)
+{
+  int retval;
+  hs_cell_establish_intro_t *establish_intro_cell = NULL;
+  or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
+  uint8_t cell_body[RELAY_PAYLOAD_SIZE];
+  ssize_t cell_len = 0;
+  char circuit_key_material[DIGEST_LEN] = {0};
+
+  (void)arg;
+
+  /* Get the auth key of the intro point */
+  crypto_rand(circuit_key_material, sizeof(circuit_key_material));
+  memcpy(intro_circ->rend_circ_nonce, circuit_key_material, DIGEST_LEN);
+
+  /* Set a bad circuit purpose!! :) */
+  circuit_change_purpose(TO_CIRCUIT(intro_circ), CIRCUIT_PURPOSE_INTRO_POINT);
+
+  /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
+     attempt to parse it. */
+  establish_intro_cell = generate_establish_intro_cell(circuit_key_material,
+                                           sizeof(circuit_key_material));
+  tt_assert(establish_intro_cell);
+  cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body),
+                                         establish_intro_cell);
+  tt_int_op(cell_len, >, 0);
+
+  /* Receive the cell */
+  retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len);
+  tt_int_op(retval, ==, -1);
+
+ done:
+  hs_cell_establish_intro_free(establish_intro_cell);
+  circuit_free(TO_CIRCUIT(intro_circ));
+}
+
+/* Prepare a circuit for accepting an ESTABLISH_INTRO cell */
+static void
+helper_prepare_circ_for_intro(or_circuit_t *circ, char *circuit_key_material)
+{
+  /* Prepare the circuit for the incoming ESTABLISH_INTRO */
+  circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR);
+  memcpy(circ->rend_circ_nonce, circuit_key_material, DIGEST_LEN);
+}
+
+/* Send an empty ESTABLISH_INTRO cell. Should fail. */
+static void
+test_establish_intro_wrong_keytype(void *arg)
+{
+  int retval;
+  or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
+  char circuit_key_material[DIGEST_LEN] = {0};
+
+  (void)arg;
+
+  /* Get the auth key of the intro point */
+  crypto_rand(circuit_key_material, sizeof(circuit_key_material));
+  helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+
+  /* Receive the cell. Should fail. */
+  retval = hs_intro_received_establish_intro(intro_circ, (uint8_t*)"", 0);
+  tt_int_op(retval, ==, -1);
+
+ done:
+  circuit_free(TO_CIRCUIT(intro_circ));
+}
+
+/* Send an ESTABLISH_INTRO cell with an unknown auth key type. Should fail. */
+static void
+test_establish_intro_wrong_keytype2(void *arg)
+{
+  int retval;
+  hs_cell_establish_intro_t *establish_intro_cell = NULL;
+  or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
+  uint8_t cell_body[RELAY_PAYLOAD_SIZE];
+  ssize_t cell_len = 0;
+  char circuit_key_material[DIGEST_LEN] = {0};
+
+  (void)arg;
+
+  /* Get the auth key of the intro point */
+  crypto_rand(circuit_key_material, sizeof(circuit_key_material));
+  helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+
+  /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
+     attempt to parse it. */
+  establish_intro_cell = generate_establish_intro_cell(circuit_key_material,
+                                           sizeof(circuit_key_material));
+  tt_assert(establish_intro_cell);
+  cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body),
+                                         establish_intro_cell);
+  tt_int_op(cell_len, >, 0);
+
+  /* Mutate the auth key type! :) */
+  cell_body[0] = 42;
+
+  /* Receive the cell. Should fail. */
+  retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len);
+  tt_int_op(retval, ==, -1);
+
+ done:
+  hs_cell_establish_intro_free(establish_intro_cell);
+  circuit_free(TO_CIRCUIT(intro_circ));
+}
+
+/* Send a legit ESTABLISH_INTRO cell but slightly change the signature. Should
+ * fail. */
+static void
+test_establish_intro_wrong_sig(void *arg)
+{
+  int retval;
+  hs_cell_establish_intro_t *establish_intro_cell = NULL;
+  or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
+  uint8_t cell_body[RELAY_PAYLOAD_SIZE];
+  ssize_t cell_len = 0;
+  char circuit_key_material[DIGEST_LEN] = {0};
+
+  (void)arg;
+
+  /* Get the auth key of the intro point */
+  crypto_rand(circuit_key_material, sizeof(circuit_key_material));
+  helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+
+  /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
+     attempt to parse it. */
+  establish_intro_cell = generate_establish_intro_cell(circuit_key_material,
+                                           sizeof(circuit_key_material));
+  tt_assert(establish_intro_cell);
+  cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body),
+                                         establish_intro_cell);
+  tt_int_op(cell_len, >, 0);
+
+  /* Mutate the last byte (signature)! :) */
+  cell_body[cell_len-1]++;
+
+  /* Receive the cell. Should fail. */
+  retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len);
+  tt_int_op(retval, ==, -1);
+
+ done:
+  hs_cell_establish_intro_free(establish_intro_cell);
+  circuit_free(TO_CIRCUIT(intro_circ));
+}
+
+/* Helper function: Send a well-formed v3 ESTABLISH_INTRO cell to
+ * <b>intro_circ</b>. Return the cell. */
+static hs_cell_establish_intro_t *
+helper_establish_intro_v3(or_circuit_t *intro_circ)
+{
+  int retval;
+  hs_cell_establish_intro_t *establish_intro_cell = NULL;
+  uint8_t cell_body[RELAY_PAYLOAD_SIZE];
+  ssize_t cell_len = 0;
+  char circuit_key_material[DIGEST_LEN] = {0};
+
+  tt_assert(intro_circ);
+
+  /* Prepare the circuit for the incoming ESTABLISH_INTRO */
+  crypto_rand(circuit_key_material, sizeof(circuit_key_material));
+  helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+
+  /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
+     attempt to parse it. */
+  establish_intro_cell = generate_establish_intro_cell(circuit_key_material,
+                                           sizeof(circuit_key_material));
+  tt_assert(establish_intro_cell);
+  cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body),
+                                         establish_intro_cell);
+  tt_int_op(cell_len, >, 0);
+
+  /* Receive the cell */
+  retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len);
+  tt_int_op(retval, ==, 0);
+
+ done:
+  return establish_intro_cell;
+}
+
+/* Helper function: Send a well-formed v2 ESTABLISH_INTRO cell to
+ * <b>intro_circ</b>. Return the public key advertised in the cell. */
+static crypto_pk_t *
+helper_establish_intro_v2(or_circuit_t *intro_circ)
+{
+  crypto_pk_t *key1 = NULL;
+  int retval;
+  uint8_t cell_body[RELAY_PAYLOAD_SIZE];
+  ssize_t cell_len = 0;
+  char circuit_key_material[DIGEST_LEN] = {0};
+
+  tt_assert(intro_circ);
+
+  /* Prepare the circuit for the incoming ESTABLISH_INTRO */
+  crypto_rand(circuit_key_material, sizeof(circuit_key_material));
+  helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+
+  /* Send legacy establish_intro */
+  key1 = pk_generate(0);
+
+  /* Use old circuit_key_material why not */
+  cell_len = encode_establish_intro_cell_legacy((char*)cell_body,
+                                                key1,
+                                                circuit_key_material);
+  tt_int_op(cell_len, >, 0);
+
+  /* Receive legacy establish_intro */
+  retval = hs_intro_received_establish_intro(intro_circ,
+                                       cell_body, cell_len);
+  tt_int_op(retval, ==, 0);
+
+ done:
+  return key1;
+}
+
+/** Successfuly register a v2 intro point and a v3 intro point. Ensure that HS
+ *  circuitmap is maintained properly. */
+static void
+test_intro_point_registration(void *arg)
+{
+  int retval;
+  hs_circuitmap_ht *the_hs_circuitmap = NULL;
+
+  or_circuit_t *intro_circ = NULL;
+  hs_cell_establish_intro_t *establish_intro_cell = NULL;
+  ed25519_public_key_t auth_key;
+
+  crypto_pk_t *legacy_auth_key = NULL;
+  or_circuit_t *legacy_intro_circ = NULL;
+
+  or_circuit_t *returned_intro_circ = NULL;
+
+  (void) arg;
+
+  MOCK(hs_intro_send_intro_established_cell, mock_send_intro_established_cell);
+
+  hs_circuitmap_init();
+
+  /* Check that the circuitmap is currently empty */
+  {
+    the_hs_circuitmap = get_hs_circuitmap();
+    tt_assert(the_hs_circuitmap);
+    tt_int_op(0, ==, HT_SIZE(the_hs_circuitmap));
+    /* Do a circuitmap query in any case */
+    returned_intro_circ = hs_circuitmap_get_intro_circ_v3(&auth_key);
+    tt_ptr_op(returned_intro_circ, ==, NULL);
+  }
+
+  /* Create a v3 intro point */
+  {
+    intro_circ = or_circuit_new(0, NULL);
+    tt_assert(intro_circ);
+    establish_intro_cell = helper_establish_intro_v3(intro_circ);
+
+    /* Check that the intro point was registered on the HS circuitmap */
+    the_hs_circuitmap = get_hs_circuitmap();
+    tt_assert(the_hs_circuitmap);
+    tt_int_op(1, ==, HT_SIZE(the_hs_circuitmap));
+    get_auth_key_from_establish_intro_cell(&auth_key, establish_intro_cell);
+    returned_intro_circ = hs_circuitmap_get_intro_circ_v3(&auth_key);
+    tt_ptr_op(intro_circ, ==, returned_intro_circ);
+  }
+
+  /* Create a v2 intro point */
+  {
+    char key_digest[DIGEST_LEN];
+
+    legacy_intro_circ = or_circuit_new(1, NULL);
+    tt_assert(legacy_intro_circ);
+    legacy_auth_key = helper_establish_intro_v2(legacy_intro_circ);
+    tt_assert(legacy_auth_key);
+
+    /* Check that the circuitmap now has two elements */
+    the_hs_circuitmap = get_hs_circuitmap();
+    tt_assert(the_hs_circuitmap);
+    tt_int_op(2, ==, HT_SIZE(the_hs_circuitmap));
+
+    /* Check that the new element is our legacy intro circuit. */
+    retval = crypto_pk_get_digest(legacy_auth_key, key_digest);
+    tt_int_op(retval, ==, 0);
+    returned_intro_circ= hs_circuitmap_get_intro_circ_v2((uint8_t*)key_digest);
+    tt_ptr_op(legacy_intro_circ, ==, returned_intro_circ);
+  }
+
+  /* XXX Continue test and try to register a second v3 intro point with the
+   * same auth key. Make sure that old intro circuit gets closed. */
+
+ done:
+  crypto_pk_free(legacy_auth_key);
+  circuit_free(TO_CIRCUIT(intro_circ));
+  circuit_free(TO_CIRCUIT(legacy_intro_circ));
+  hs_cell_establish_intro_free(establish_intro_cell);
+
+  { /* Test circuitmap free_all function. */
+    the_hs_circuitmap = get_hs_circuitmap();
+    tt_assert(the_hs_circuitmap);
+    hs_circuitmap_free_all();
+    the_hs_circuitmap = get_hs_circuitmap();
+    tt_assert(!the_hs_circuitmap);
+  }
+
+  UNMOCK(hs_intro_send_intro_established_cell);
+}
+
+struct testcase_t hs_intropoint_tests[] = {
+  { "intro_point_registration",
+    test_intro_point_registration, TT_FORK, NULL, NULL },
+
+  { "receive_establish_intro_wrong_keytype",
+    test_establish_intro_wrong_keytype, TT_FORK, NULL, NULL },
+
+  { "receive_establish_intro_wrong_keytype2",
+    test_establish_intro_wrong_keytype2, TT_FORK, NULL, NULL },
+
+  { "receive_establish_intro_wrong_purpose",
+    test_establish_intro_wrong_purpose, TT_FORK, NULL, NULL },
+
+  { "receive_establish_intro_wrong_sig",
+    test_establish_intro_wrong_sig, TT_FORK, NULL, NULL },
+
+  END_OF_TESTCASES
+};
+

+ 112 - 0
src/test/test_hs_service.c

@@ -0,0 +1,112 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_hs_service.c
+ * \brief Test hidden service functionality.
+ */
+
+#define HS_SERVICE_PRIVATE
+#define HS_INTROPOINT_PRIVATE
+
+#include "test.h"
+#include "log_test_helpers.h"
+#include "crypto.h"
+
+#include "hs/cell_establish_intro.h"
+#include "hs_service.h"
+#include "hs_intropoint.h"
+
+/** We simulate the creation of an outgoing ESTABLISH_INTRO cell, and then we
+ *  parse it from the receiver side. */
+static void
+test_gen_establish_intro_cell(void *arg)
+{
+  (void) arg;
+  int retval;
+  char circuit_key_material[DIGEST_LEN] = {0};
+  uint8_t buf[RELAY_PAYLOAD_SIZE];
+  hs_cell_establish_intro_t *cell_out = NULL;
+  hs_cell_establish_intro_t *cell_in = NULL;
+
+  crypto_rand(circuit_key_material, sizeof(circuit_key_material));
+
+  /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
+     attempt to parse it. */
+  {
+    cell_out = generate_establish_intro_cell(circuit_key_material,
+                                             sizeof(circuit_key_material));
+    tt_assert(cell_out);
+
+    retval = get_establish_intro_payload(buf, sizeof(buf), cell_out);
+    tt_int_op(retval, >=, 0);
+  }
+
+  /* Parse it as the receiver */
+  {
+    ssize_t parse_result = hs_cell_establish_intro_parse(&cell_in,
+                                                         buf, sizeof(buf));
+    tt_int_op(parse_result, >=, 0);
+
+    retval = verify_establish_intro_cell(cell_in,
+                                         circuit_key_material,
+                                         sizeof(circuit_key_material));
+    tt_int_op(retval, >=, 0);
+  }
+
+ done:
+  hs_cell_establish_intro_free(cell_out);
+  hs_cell_establish_intro_free(cell_in);
+}
+
+/* Mocked ed25519_sign_prefixed() function that always fails :) */
+static int
+mock_ed25519_sign_prefixed(ed25519_signature_t *signature_out,
+                           const uint8_t *msg, size_t msg_len,
+                           const char *prefix_str,
+                           const ed25519_keypair_t *keypair) {
+  (void) signature_out;
+  (void) msg;
+  (void) msg_len;
+  (void) prefix_str;
+  (void) keypair;
+  return -1;
+}
+
+/** We simulate a failure to create an ESTABLISH_INTRO cell */
+static void
+test_gen_establish_intro_cell_bad(void *arg)
+{
+  (void) arg;
+  hs_cell_establish_intro_t *cell = NULL;
+  char circuit_key_material[DIGEST_LEN] = {0};
+
+  MOCK(ed25519_sign_prefixed, mock_ed25519_sign_prefixed);
+
+  crypto_rand(circuit_key_material, sizeof(circuit_key_material));
+
+  setup_full_capture_of_logs(LOG_WARN);
+  /* Easiest way to make that function fail is to mock the
+     ed25519_sign_prefixed() function and make it fail. */
+  cell = generate_establish_intro_cell(circuit_key_material,
+                                       sizeof(circuit_key_material));
+  expect_log_msg_containing("Unable to gen signature for "
+                            "ESTABLISH_INTRO cell.");
+  teardown_capture_of_logs();
+  tt_assert(!cell);
+
+ done:
+  hs_cell_establish_intro_free(cell);
+  UNMOCK(ed25519_sign_prefixed);
+}
+
+struct testcase_t hs_service_tests[] = {
+  { "gen_establish_intro_cell", test_gen_establish_intro_cell, TT_FORK,
+    NULL, NULL },
+  { "gen_establish_intro_cell_bad", test_gen_establish_intro_cell_bad, TT_FORK,
+    NULL, NULL },
+
+
+  END_OF_TESTCASES
+};
+