aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/ed25519_exts_ref.py6
-rw-r--r--src/test/ed25519_vectors.inc32
-rw-r--r--src/test/hs_test_helpers.c30
-rw-r--r--src/test/hs_test_helpers.h3
-rw-r--r--src/test/include.am3
-rw-r--r--src/test/test.c3
-rw-r--r--src/test/test.h3
-rw-r--r--src/test/test_hs_cache.c8
-rw-r--r--src/test/test_hs_cell.c130
-rw-r--r--src/test/test_hs_common.c507
-rw-r--r--src/test/test_hs_config.c2
-rw-r--r--src/test/test_hs_descriptor.c18
-rw-r--r--src/test/test_hs_intropoint.c297
-rw-r--r--src/test/test_hs_ntor.c114
-rw-r--r--src/test/test_hs_service.c1361
-rw-r--r--src/test/test_shared_random.c120
16 files changed, 2167 insertions, 470 deletions
diff --git a/src/test/ed25519_exts_ref.py b/src/test/ed25519_exts_ref.py
index 1898256540..f84d3002d3 100644
--- a/src/test/ed25519_exts_ref.py
+++ b/src/test/ed25519_exts_ref.py
@@ -32,8 +32,7 @@ def curve25519ToEd25519(c, sign):
return encodepoint([x,y])
def blindESK(esk, param):
- h = H("Derive temporary signing key" + param)
- mult = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2))
+ mult = 2**(b-2) + sum(2**i * bit(param,i) for i in range(3,b-2))
s = decodeint(esk[:32])
s_prime = (s * mult) % ell
k = esk[32:]
@@ -42,8 +41,7 @@ def blindESK(esk, param):
return encodeint(s_prime) + k_prime
def blindPK(pk, param):
- h = H("Derive temporary signing key" + param)
- mult = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2))
+ mult = 2**(b-2) + sum(2**i * bit(param,i) for i in range(3,b-2))
P = decodepoint(pk)
return encodepoint(scalarmult(P, mult))
diff --git a/src/test/ed25519_vectors.inc b/src/test/ed25519_vectors.inc
index 760bafb971..60c863beba 100644
--- a/src/test/ed25519_vectors.inc
+++ b/src/test/ed25519_vectors.inc
@@ -91,21 +91,21 @@ static const char *ED25519_BLINDING_PARAMS[] = {
* blinding parameter.
*/
static const char *ED25519_BLINDED_SECRET_KEYS[] = {
- "014e83abadb2ca9a27e0ffe23920333d817729f48700e97656ec2823d694050e171d43"
+ "293c3acff4e902f6f63ddc5d5caa2a57e771db4f24de65d4c28df3232f47fa01171d43"
"f24e3f53e70ec7ac280044ac77d4942dee5d6807118a59bdf3ee647e89",
- "fad8cca0b4335847795288b1452508752b253e64e6c7c78d4a02dbbd7d46aa0eb8ceff"
+ "38b88f9f9440358da544504ee152fb475528f7c51c285bd1c68b14ade8e29a07b8ceff"
"20dfcf53eb52b891fc078c934efbf0353af7242e7dc51bb32a093afa29",
- "116eb0ae0a4a91763365bdf86db427b00862db448487808788cc339ac10e5e089217f5"
+ "4d03ce16a3f3249846aac9de0a0075061495c3b027248eeee47da4ddbaf9e0049217f5"
"2e92797462bd890fc274672e05c98f2c82970d640084781334aae0f940",
- "bd1fbb0ee5acddc4adbcf5f33e95d9445f40326ce579fdd764a24483a9ccb20f509ece"
+ "51d7db01aaa0d937a9fd7c8c7381445a14d8fa61f43347af5460d7cd8fda9904509ece"
"e77082ce088f7c19d5a00e955eeef8df6fa41686abc1030c2d76807733",
- "237f5345cefe8573ce9fa7e216381a1172796c9e3f70668ab503b1352952530fb57b95"
+ "1f76cab834e222bd2546efa7e073425680ab88df186ff41327d3e40770129b00b57b95"
"a440570659a440a3e4771465022a8e67af86bdf2d0990c54e7bb87ff9a",
- "ba8ff23bc4ad2b739e1ccffc9fbc7837053ea81cdfdb15073f56411cfbae1d0ec492fc"
+ "c23588c23ee76093419d07b27c6df5922a03ac58f96c53671456a7d1bdbf560ec492fc"
"87d5ec2a1b185ca5a40541fdef0b1e128fd5c2380c888bfa924711bcab",
- "0fa68f969de038c7a90a4a74ee6167c77582006f2dedecc1956501ba6b6fb10391b476"
+ "3ed249c6932d076e1a2f6916975914b14e8c739da00992358b8f37d3e790650691b476"
"8f8e556d78f4bdcb9a13b6f6066fe81d3134ae965dc48cd0785b3af2b8",
- "deaa3456d1c21944d5dcd361a646858c6cf9336b0a6851d925717eb1ae186902053d9c"
+ "288cbfd923cb286d48c084555b5bdd06c05e92fb81acdb45271367f57515380e053d9c"
"00c81e1331c06ab50087be8cfc7dc11691b132614474f1aa9c2503cccd",
};
@@ -115,14 +115,14 @@ static const char *ED25519_BLINDED_SECRET_KEYS[] = {
* blinding parameter.
*/
static const char *ED25519_BLINDED_PUBLIC_KEYS[] = {
- "722d6da6348e618967ef782e71061e27163a8b35f21856475d9d2023f65b6495",
- "1dffa0586da6cbfcff2024eedf4fc6c818242d9a82dbbe635d6da1b975a1160d",
- "5ed81f98fed5a6acda4ea6da2c34fab0ab359d950c510c256473f1f33ff438b4",
- "6e6f92a54fb282120c46d9603df41135f025bc1f58f283809d04be96aeb04040",
- "cda236f28edc4c7e02d18007b8dab49d669265b0f7aefb1824d7cc8e73a2cd63",
- "367b03b17b67ca7329b89a520bdab91782402a41cd67264e34b5541a4b3f875b",
- "8d486b03ac4e3b486b7a1d563706c7fdac75aee789a7cf6f22789eedeff61a31",
- "9f297ff0aa2ceda91c5ab1b6446f12533d145940de6d850dc323417afde0cb78",
+ "1fc1fa4465bd9d4956fdbdc9d3acb3c7019bb8d5606b951c2e1dfe0b42eaeb41",
+ "1cbbd4a88ce8f165447f159d9f628ada18674158c4f7c5ead44ce8eb0fa6eb7e",
+ "c5419ad133ffde7e0ac882055d942f582054132b092de377d587435722deb028",
+ "3e08d0dc291066272e313014bfac4d39ad84aa93c038478a58011f431648105f",
+ "59381f06acb6bf1389ba305f70874eed3e0f2ab57cdb7bc69ed59a9b8899ff4d",
+ "2b946a484344eb1c17c89dd8b04196a84f3b7222c876a07a4cece85f676f87d9",
+ "c6b585129b135f8769df2eba987e76e089e80ba3a2a6729134d3b28008ac098e",
+ "0eefdc795b59cabbc194c6174e34ba9451e8355108520554ec285acabebb34ac",
};
/**
diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c
index 3f0d6a9413..2753d29078 100644
--- a/src/test/hs_test_helpers.c
+++ b/src/test/hs_test_helpers.c
@@ -6,6 +6,7 @@
#include "test.h"
#include "torcert.h"
+#include "hs_common.h"
#include "hs_test_helpers.h"
hs_desc_intro_point_t *
@@ -15,8 +16,7 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
int ret;
ed25519_keypair_t auth_kp;
hs_desc_intro_point_t *intro_point = NULL;
- hs_desc_intro_point_t *ip = tor_malloc_zero(sizeof(*ip));
- ip->link_specifiers = smartlist_new();
+ hs_desc_intro_point_t *ip = hs_desc_intro_point_new();
{
hs_desc_link_specifier_t *ls = tor_malloc_zero(sizeof(*ls));
@@ -94,8 +94,7 @@ static hs_descriptor_t *
hs_helper_build_hs_desc_impl(unsigned int no_ip,
const ed25519_keypair_t *signing_kp)
{
- int ret;
- time_t now = time(NULL);
+ time_t now = approx_time();
ed25519_keypair_t blinded_kp;
hs_descriptor_t *descp = NULL, *desc = tor_malloc_zero(sizeof(*desc));
@@ -105,8 +104,9 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
memcpy(&desc->plaintext_data.signing_pubkey, &signing_kp->pubkey,
sizeof(ed25519_public_key_t));
- ret = ed25519_keypair_generate(&blinded_kp, 0);
- tt_int_op(ret, ==, 0);
+ uint64_t current_time_period = hs_get_time_period_num(approx_time());
+ hs_build_blinded_keypair(signing_kp, NULL, 0,
+ current_time_period, &blinded_kp);
/* Copy only the public key into the descriptor. */
memcpy(&desc->plaintext_data.blinded_pubkey, &blinded_kp.pubkey,
sizeof(ed25519_public_key_t));
@@ -119,6 +119,9 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
desc->plaintext_data.revision_counter = 42;
desc->plaintext_data.lifetime_sec = 3 * 60 * 60;
+ hs_get_subcredential(&signing_kp->pubkey, &blinded_kp.pubkey,
+ desc->subcredential);
+
/* Setup encrypted data section. */
desc->encrypted_data.create2_ntor = 1;
desc->encrypted_data.intro_auth_types = smartlist_new();
@@ -142,6 +145,21 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
return descp;
}
+/** Helper function to get the HS subcredential using the identity keypair of
+ * an HS. Used to decrypt descriptors in unittests. */
+void
+hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp,
+ uint8_t *subcred_out)
+{
+ ed25519_keypair_t blinded_kp;
+ uint64_t current_time_period = hs_get_time_period_num(approx_time());
+ hs_build_blinded_keypair(signing_kp, NULL, 0,
+ current_time_period, &blinded_kp);
+
+ hs_get_subcredential(&signing_kp->pubkey, &blinded_kp.pubkey,
+ subcred_out);
+}
+
/* Build a descriptor with introduction points. */
hs_descriptor_t *
hs_helper_build_hs_desc_with_ip(const ed25519_keypair_t *signing_kp)
diff --git a/src/test/hs_test_helpers.h b/src/test/hs_test_helpers.h
index a7fedab136..05f5aa7b64 100644
--- a/src/test/hs_test_helpers.h
+++ b/src/test/hs_test_helpers.h
@@ -17,6 +17,9 @@ hs_descriptor_t *hs_helper_build_hs_desc_with_ip(
const ed25519_keypair_t *signing_kp);
void hs_helper_desc_equal(const hs_descriptor_t *desc1,
const hs_descriptor_t *desc2);
+void
+hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp,
+ uint8_t *subcred_out);
#endif /* TOR_HS_TEST_HELPERS_H */
diff --git a/src/test/include.am b/src/test/include.am
index 230a722017..7a9ecca907 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -115,7 +115,10 @@ src_test_test_SOURCES = \
src/test/test_guardfraction.c \
src/test/test_extorport.c \
src/test/test_hs.c \
+ src/test/test_hs_common.c \
src/test/test_hs_config.c \
+ src/test/test_hs_cell.c \
+ src/test/test_hs_ntor.c \
src/test/test_hs_service.c \
src/test/test_hs_client.c \
src/test/test_hs_intropoint.c \
diff --git a/src/test/test.c b/src/test/test.c
index c5c394900c..994a97ab00 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -1214,8 +1214,11 @@ struct testgroup_t testgroups[] = {
{ "extorport/", extorport_tests },
{ "legacy_hs/", hs_tests },
{ "hs_cache/", hs_cache },
+ { "hs_cell/", hs_cell_tests },
+ { "hs_common/", hs_common_tests },
{ "hs_config/", hs_config_tests },
{ "hs_descriptor/", hs_descriptor },
+ { "hs_ntor/", hs_ntor_tests },
{ "hs_service/", hs_service_tests },
{ "hs_client/", hs_client_tests },
{ "hs_intropoint/", hs_intropoint_tests },
diff --git a/src/test/test.h b/src/test/test.h
index 9b2a0b842f..448d253fb5 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -207,8 +207,11 @@ extern struct testcase_t guardfraction_tests[];
extern struct testcase_t extorport_tests[];
extern struct testcase_t hs_tests[];
extern struct testcase_t hs_cache[];
+extern struct testcase_t hs_cell_tests[];
+extern struct testcase_t hs_common_tests[];
extern struct testcase_t hs_config_tests[];
extern struct testcase_t hs_descriptor[];
+extern struct testcase_t hs_ntor_tests[];
extern struct testcase_t hs_service_tests[];
extern struct testcase_t hs_client_tests[];
extern struct testcase_t hs_intropoint_tests[];
diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c
index 40f50b322a..6c2addef9a 100644
--- a/src/test/test_hs_cache.c
+++ b/src/test/test_hs_cache.c
@@ -342,6 +342,7 @@ test_hsdir_revision_counter_check(void *arg)
hs_descriptor_t *published_desc = NULL;
char *published_desc_str = NULL;
+ uint8_t subcredential[DIGEST256_LEN];
char *received_desc_str = NULL;
hs_descriptor_t *received_desc = NULL;
@@ -378,9 +379,11 @@ test_hsdir_revision_counter_check(void *arg)
const ed25519_public_key_t *blinded_key;
blinded_key = &published_desc->plaintext_data.blinded_pubkey;
+ hs_get_subcredential(&signing_kp.pubkey, blinded_key, subcredential);
received_desc_str = helper_fetch_desc_from_hsdir(blinded_key);
- retval = hs_desc_decode_descriptor(received_desc_str,NULL, &received_desc);
+ retval = hs_desc_decode_descriptor(received_desc_str,
+ subcredential, &received_desc);
tt_int_op(retval, ==, 0);
tt_assert(received_desc);
@@ -412,7 +415,8 @@ test_hsdir_revision_counter_check(void *arg)
blinded_key = &published_desc->plaintext_data.blinded_pubkey;
received_desc_str = helper_fetch_desc_from_hsdir(blinded_key);
- retval = hs_desc_decode_descriptor(received_desc_str,NULL, &received_desc);
+ retval = hs_desc_decode_descriptor(received_desc_str,
+ subcredential, &received_desc);
tt_int_op(retval, ==, 0);
tt_assert(received_desc);
diff --git a/src/test/test_hs_cell.c b/src/test/test_hs_cell.c
new file mode 100644
index 0000000000..1b3c788a67
--- /dev/null
+++ b/src/test/test_hs_cell.c
@@ -0,0 +1,130 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_hs_cell.c
+ * \brief Test hidden service cell functionality.
+ */
+
+#define HS_INTROPOINT_PRIVATE
+#define HS_SERVICE_PRIVATE
+
+#include "test.h"
+#include "test_helpers.h"
+#include "log_test_helpers.h"
+
+#include "crypto_ed25519.h"
+#include "hs_cell.h"
+#include "hs_intropoint.h"
+#include "hs_service.h"
+
+/* Trunnel. */
+#include "hs/cell_establish_intro.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;
+ ssize_t ret;
+ char circ_nonce[DIGEST_LEN] = {0};
+ uint8_t buf[RELAY_PAYLOAD_SIZE];
+ trn_cell_establish_intro_t *cell_in = NULL;
+
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+
+ /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
+ attempt to parse it. */
+ {
+ /* We only need the auth key pair here. */
+ hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0);
+ /* Auth key pair is generated in the constructor so we are all set for
+ * using this IP object. */
+ ret = hs_cell_build_establish_intro(circ_nonce, ip, buf);
+ service_intro_point_free(ip);
+ tt_u64_op(ret, OP_GT, 0);
+ }
+
+ /* Check the contents of the cell */
+ {
+ /* First byte is the auth key type: make sure its correct */
+ tt_int_op(buf[0], OP_EQ, HS_INTRO_AUTH_KEY_TYPE_ED25519);
+ /* Next two bytes is auth key len */
+ tt_int_op(ntohs(get_uint16(buf+1)), OP_EQ, ED25519_PUBKEY_LEN);
+ /* Skip to the number of extensions: no extensions */
+ tt_int_op(buf[35], OP_EQ, 0);
+ /* Skip to the sig len. Make sure it's the size of an ed25519 sig */
+ tt_int_op(ntohs(get_uint16(buf+35+1+32)), OP_EQ, ED25519_SIG_LEN);
+ }
+
+ /* Parse it as the receiver */
+ {
+ ret = trn_cell_establish_intro_parse(&cell_in, buf, sizeof(buf));
+ tt_u64_op(ret, OP_GT, 0);
+
+ ret = verify_establish_intro_cell(cell_in,
+ (const uint8_t *) circ_nonce,
+ sizeof(circ_nonce));
+ tt_u64_op(ret, OP_EQ, 0);
+ }
+
+ done:
+ trn_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;
+ ssize_t cell_len = 0;
+ trn_cell_establish_intro_t *cell = NULL;
+ char circ_nonce[DIGEST_LEN] = {0};
+ hs_service_intro_point_t *ip = NULL;
+
+ MOCK(ed25519_sign_prefixed, mock_ed25519_sign_prefixed);
+
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+
+ 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 = trn_cell_establish_intro_new();
+ tt_assert(cell);
+ ip = service_intro_point_new(NULL, 0);
+ cell_len = hs_cell_build_establish_intro(circ_nonce, ip, NULL);
+ service_intro_point_free(ip);
+ expect_log_msg_containing("Unable to make signature for "
+ "ESTABLISH_INTRO cell.");
+ teardown_capture_of_logs();
+ tt_i64_op(cell_len, OP_EQ, -1);
+
+ done:
+ trn_cell_establish_intro_free(cell);
+ UNMOCK(ed25519_sign_prefixed);
+}
+
+struct testcase_t hs_cell_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
+};
+
diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c
new file mode 100644
index 0000000000..d79d80bfab
--- /dev/null
+++ b/src/test/test_hs_common.c
@@ -0,0 +1,507 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_hs_common.c
+ * \brief Test hidden service common functionalities.
+ */
+
+#define HS_COMMON_PRIVATE
+#define HS_SERVICE_PRIVATE
+
+#include "test.h"
+#include "test_helpers.h"
+#include "log_test_helpers.h"
+#include "hs_test_helpers.h"
+
+#include "hs_common.h"
+#include "hs_service.h"
+#include "config.h"
+#include "networkstatus.h"
+#include "nodelist.h"
+
+/** Test the validation of HS v3 addresses */
+static void
+test_validate_address(void *arg)
+{
+ int ret;
+
+ (void) arg;
+
+ /* Address too short and too long. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_address_is_valid("blah");
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg_containing("has an invalid length");
+ teardown_capture_of_logs();
+
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_address_is_valid(
+ "p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnadb");
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg_containing("has an invalid length");
+ teardown_capture_of_logs();
+
+ /* Invalid checksum (taken from prop224) */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_address_is_valid(
+ "l5satjgud6gucryazcyvyvhuxhr74u6ygigiuyixe3a6ysis67ororad");
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg_containing("invalid checksum");
+ teardown_capture_of_logs();
+
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_address_is_valid(
+ "btojiu7nu5y5iwut64eufevogqdw4wmqzugnoluw232r4t3ecsfv37ad");
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg_containing("invalid checksum");
+ teardown_capture_of_logs();
+
+ /* Non base32 decodable string. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_address_is_valid(
+ "????????????????????????????????????????????????????????");
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg_containing("can't be decoded");
+ teardown_capture_of_logs();
+
+ /* Valid address. */
+ ret = hs_address_is_valid(
+ "p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnad");
+ tt_int_op(ret, OP_EQ, 1);
+
+ done:
+ ;
+}
+
+static int
+mock_write_str_to_file(const char *path, const char *str, int bin)
+{
+ (void)bin;
+ tt_str_op(path, OP_EQ, "/double/five/squared");
+ tt_str_op(str, OP_EQ,
+ "ijbeeqscijbeeqscijbeeqscijbeeqscijbeeqscijbeeqscijbezhid.onion\n");
+
+ done:
+ return 0;
+}
+
+/** Test building HS v3 onion addresses */
+static void
+test_build_address(void *arg)
+{
+ int ret;
+ char onion_addr[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ ed25519_public_key_t pubkey;
+ hs_service_t *service = NULL;
+
+ (void) arg;
+
+ MOCK(write_str_to_file, mock_write_str_to_file);
+
+ /* The following has been created with hs_build_address.py script that
+ * follows proposal 224 specification to build an onion address. */
+ static const char *test_addr =
+ "ijbeeqscijbeeqscijbeeqscijbeeqscijbeeqscijbeeqscijbezhid";
+
+ /* Let's try to build the same onion address that the script can do. Key is
+ * a long set of very random \x42 :). */
+ memset(&pubkey, '\x42', sizeof(pubkey));
+ hs_build_address(&pubkey, HS_VERSION_THREE, onion_addr);
+ tt_str_op(test_addr, OP_EQ, onion_addr);
+ /* Validate that address. */
+ ret = hs_address_is_valid(onion_addr);
+ tt_int_op(ret, OP_EQ, 1);
+
+ service = tor_malloc_zero(sizeof(hs_service_t));
+ memcpy(service->onion_address, onion_addr, sizeof(service->onion_address));
+ tor_asprintf(&service->config.directory_path, "/double/five");
+ ret = write_address_to_file(service, "squared");
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ hs_service_free(service);
+}
+
+/** Test that our HS time period calculation functions work properly */
+static void
+test_time_period(void *arg)
+{
+ (void) arg;
+ uint64_t tn;
+ int retval;
+ time_t fake_time, correct_time, start_time;
+
+ /* Let's do the example in prop224 section [TIME-PERIODS] */
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 11:00:00 UTC",
+ &fake_time);
+ tt_int_op(retval, ==, 0);
+
+ /* Check that the time period number is right */
+ tn = hs_get_time_period_num(fake_time);
+ tt_u64_op(tn, ==, 16903);
+
+ /* Increase current time to 11:59:59 UTC and check that the time period
+ number is still the same */
+ fake_time += 3599;
+ tn = hs_get_time_period_num(fake_time);
+ tt_u64_op(tn, ==, 16903);
+
+ { /* Check start time of next time period */
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 12:00:00 UTC",
+ &correct_time);
+ tt_int_op(retval, ==, 0);
+
+ start_time = hs_get_start_time_of_next_time_period(fake_time);
+ tt_int_op(start_time, OP_EQ, correct_time);
+ }
+
+ /* Now take time to 12:00:00 UTC and check that the time period rotated */
+ fake_time += 1;
+ tn = hs_get_time_period_num(fake_time);
+ tt_u64_op(tn, ==, 16904);
+
+ /* Now also check our hs_get_next_time_period_num() function */
+ tn = hs_get_next_time_period_num(fake_time);
+ tt_u64_op(tn, ==, 16905);
+
+ { /* Check start time of next time period again */
+ retval = parse_rfc1123_time("Wed, 14 Apr 2016 12:00:00 UTC",
+ &correct_time);
+ tt_int_op(retval, ==, 0);
+
+ start_time = hs_get_start_time_of_next_time_period(fake_time);
+ tt_int_op(start_time, OP_EQ, correct_time);
+ }
+
+ /* Now do another sanity check: The time period number at the start of the
+ * next time period, must be the same time period number as the one returned
+ * from hs_get_next_time_period_num() */
+ {
+ time_t next_tp_start = hs_get_start_time_of_next_time_period(fake_time);
+ tt_int_op(hs_get_time_period_num(next_tp_start), OP_EQ,
+ hs_get_next_time_period_num(fake_time));
+ }
+
+ done:
+ ;
+}
+
+/** Test that we can correctly find the start time of the next time period */
+static void
+test_start_time_of_next_time_period(void *arg)
+{
+ (void) arg;
+ int retval;
+ time_t fake_time;
+ char tbuf[ISO_TIME_LEN + 1];
+ time_t next_tp_start_time;
+
+ /* Do some basic tests */
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 11:00:00 UTC",
+ &fake_time);
+ tt_int_op(retval, ==, 0);
+ next_tp_start_time = hs_get_start_time_of_next_time_period(fake_time);
+ /* Compare it with the correct result */
+ format_iso_time(tbuf, next_tp_start_time);
+ tt_str_op("2016-04-13 12:00:00", OP_EQ, tbuf);
+
+ /* Another test with an edge-case time (start of TP) */
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 12:00:00 UTC",
+ &fake_time);
+ tt_int_op(retval, ==, 0);
+ next_tp_start_time = hs_get_start_time_of_next_time_period(fake_time);
+ format_iso_time(tbuf, next_tp_start_time);
+ tt_str_op("2016-04-14 12:00:00", OP_EQ, tbuf);
+
+ {
+ /* Now pretend we are on a testing network and alter the voting schedule to
+ be every 10 seconds. This means that a time period has length 10*24
+ seconds (4 minutes). It also means that we apply a rotational offset of
+ 120 seconds to the time period, so that it starts at 00:02:00 instead of
+ 00:00:00. */
+ or_options_t *options = get_options_mutable();
+ options->TestingTorNetwork = 1;
+ options->V3AuthVotingInterval = 10;
+ options->TestingV3AuthInitialVotingInterval = 10;
+
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 00:00:00 UTC",
+ &fake_time);
+ tt_int_op(retval, ==, 0);
+ next_tp_start_time = hs_get_start_time_of_next_time_period(fake_time);
+ /* Compare it with the correct result */
+ format_iso_time(tbuf, next_tp_start_time);
+ tt_str_op("2016-04-13 00:02:00", OP_EQ, tbuf);
+
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 00:02:00 UTC",
+ &fake_time);
+ tt_int_op(retval, ==, 0);
+ next_tp_start_time = hs_get_start_time_of_next_time_period(fake_time);
+ /* Compare it with the correct result */
+ format_iso_time(tbuf, next_tp_start_time);
+ tt_str_op("2016-04-13 00:06:00", OP_EQ, tbuf);
+ }
+
+ done:
+ ;
+}
+
+/** Test that our HS overlap period functions work properly. */
+static void
+test_desc_overlap_period(void *arg)
+{
+ (void) arg;
+ int retval;
+ time_t now = time(NULL);
+ networkstatus_t *dummy_consensus = NULL;
+
+ /* First try with a consensus just inside the overlap period */
+ dummy_consensus = tor_malloc_zero(sizeof(networkstatus_t));
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 00:00:00 UTC",
+ &dummy_consensus->valid_after);
+ tt_int_op(retval, ==, 0);
+
+ retval = hs_overlap_mode_is_active(dummy_consensus, now);
+ tt_int_op(retval, ==, 1);
+
+ /* Now increase the valid_after so that it goes to 11:00:00 UTC. Overlap
+ period is still active. */
+ dummy_consensus->valid_after += 3600*11;
+ retval = hs_overlap_mode_is_active(dummy_consensus, now);
+ tt_int_op(retval, ==, 1);
+
+ /* Now increase the valid_after so that it goes to 11:59:59 UTC. Overlap
+ period is still active. */
+ dummy_consensus->valid_after += 3599;
+ retval = hs_overlap_mode_is_active(dummy_consensus, now);
+ tt_int_op(retval, ==, 1);
+
+ /* Now increase the valid_after so that it drifts to noon, and check that
+ overlap mode is not active anymore. */
+ dummy_consensus->valid_after += 1;
+ retval = hs_overlap_mode_is_active(dummy_consensus, now);
+ tt_int_op(retval, ==, 0);
+
+ /* Check that overlap mode is also inactive at 23:59:59 UTC */
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 23:59:59 UTC",
+ &dummy_consensus->valid_after);
+ tt_int_op(retval, ==, 0);
+ retval = hs_overlap_mode_is_active(dummy_consensus, now);
+ tt_int_op(retval, ==, 0);
+
+ done:
+ tor_free(dummy_consensus);
+}
+
+/* Test the overlap period functions on a testnet with altered voting
+ * schedule */
+static void
+test_desc_overlap_period_testnet(void *arg)
+{
+ int retval;
+ time_t now = approx_time();
+ networkstatus_t *dummy_consensus = NULL;
+ or_options_t *options = get_options_mutable();
+
+ (void) arg;
+
+ /* Set the testnet option and a 10-second voting interval */
+ options->TestingTorNetwork = 1;
+ options->V3AuthVotingInterval = 10;
+ options->TestingV3AuthInitialVotingInterval = 10;
+
+ dummy_consensus = tor_malloc_zero(sizeof(networkstatus_t));
+
+ /* A 10-second voting interval means that the lengths of an SRV run and of a
+ * time period are both 10*24 seconds (4 minutes). The SRV gets published at
+ * 00:00:00 and the TP starts at 00:02:00 (rotation offset: 2 mins). Those
+ * two minutes between SRV publish and TP start is the overlap period
+ * window. Let's test it: */
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 00:00:00 UTC",
+ &dummy_consensus->valid_after);
+ tt_int_op(retval, ==, 0);
+ retval = hs_overlap_mode_is_active(dummy_consensus, now);
+ tt_int_op(retval, ==, 1);
+
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 00:01:59 UTC",
+ &dummy_consensus->valid_after);
+ tt_int_op(retval, ==, 0);
+ retval = hs_overlap_mode_is_active(dummy_consensus, now);
+ tt_int_op(retval, ==, 1);
+
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 00:02:00 UTC",
+ &dummy_consensus->valid_after);
+ tt_int_op(retval, ==, 0);
+ retval = hs_overlap_mode_is_active(dummy_consensus, now);
+ tt_int_op(retval, ==, 0);
+
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 00:04:00 UTC",
+ &dummy_consensus->valid_after);
+ tt_int_op(retval, ==, 0);
+ retval = hs_overlap_mode_is_active(dummy_consensus, now);
+ tt_int_op(retval, ==, 1);
+
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 00:05:59 UTC",
+ &dummy_consensus->valid_after);
+ tt_int_op(retval, ==, 0);
+ retval = hs_overlap_mode_is_active(dummy_consensus, now);
+ tt_int_op(retval, ==, 1);
+
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 00:06:00 UTC",
+ &dummy_consensus->valid_after);
+ tt_int_op(retval, ==, 0);
+ retval = hs_overlap_mode_is_active(dummy_consensus, now);
+ tt_int_op(retval, ==, 0);
+
+ done:
+ tor_free(dummy_consensus);
+}
+
+static networkstatus_t *mock_ns = NULL;
+
+static networkstatus_t *
+mock_networkstatus_get_latest_consensus(void)
+{
+ time_t now = approx_time();
+
+ /* If initialized, return it */
+ if (mock_ns) {
+ return mock_ns;
+ }
+
+ /* Initialize fake consensus */
+ mock_ns = tor_malloc_zero(sizeof(networkstatus_t));
+
+ /* This consensus is live */
+ mock_ns->valid_after = now-1;
+ mock_ns->fresh_until = now+1;
+ mock_ns->valid_until = now+2;
+ /* Create routerstatus list */
+ mock_ns->routerstatus_list = smartlist_new();
+
+ return mock_ns;
+}
+
+/** Test the responsible HSDirs calculation function */
+static void
+test_responsible_hsdirs(void *arg)
+{
+ time_t now = approx_time();
+ smartlist_t *responsible_dirs = smartlist_new();
+ networkstatus_t *ns = NULL;
+ routerstatus_t *rs = tor_malloc_zero(sizeof(routerstatus_t));
+
+ (void) arg;
+
+ hs_init();
+
+ MOCK(networkstatus_get_latest_consensus,
+ mock_networkstatus_get_latest_consensus);
+
+ ns = networkstatus_get_latest_consensus();
+
+ { /* First router: HSdir */
+ tor_addr_t ipv4_addr;
+ memset(rs->identity_digest, 'A', DIGEST_LEN);
+ rs->is_hs_dir = 1;
+ rs->supports_v3_hsdir = 1;
+ routerinfo_t ri;
+ memset(&ri, 0 ,sizeof(routerinfo_t));
+ tor_addr_parse(&ipv4_addr, "127.0.0.1");
+ ri.addr = tor_addr_to_ipv4h(&ipv4_addr);
+ ri.nickname = tor_strdup("fatal");
+ ri.protocol_list = (char *) "HSDir=1-2 LinkAuth=3";
+ memset(ri.cache_info.identity_digest, 'A', DIGEST_LEN);
+ tt_assert(nodelist_set_routerinfo(&ri, NULL));
+ node_t *node = node_get_mutable_by_id(ri.cache_info.identity_digest);
+ memset(node->hsdir_index->current, 'Z',
+ sizeof(node->hsdir_index->current));
+ smartlist_add(ns->routerstatus_list, rs);
+ }
+
+ ed25519_public_key_t blinded_pk;
+ uint64_t time_period_num = hs_get_time_period_num(now);
+ hs_get_responsible_hsdirs(&blinded_pk, time_period_num,
+ 0, 0, responsible_dirs);
+ tt_int_op(smartlist_len(responsible_dirs), OP_EQ, 1);
+
+ /** TODO: Build a bigger network and do more tests here */
+
+ done:
+ routerstatus_free(rs);
+ smartlist_free(responsible_dirs);
+ smartlist_clear(ns->routerstatus_list);
+ networkstatus_vote_free(mock_ns);
+}
+
+/** Test disaster SRV computation and caching */
+static void
+test_disaster_srv(void *arg)
+{
+ uint8_t *cached_disaster_srv_one = NULL;
+ uint8_t *cached_disaster_srv_two = NULL;
+ uint8_t srv_one[DIGEST256_LEN] = {0};
+ uint8_t srv_two[DIGEST256_LEN] = {0};
+ uint8_t srv_three[DIGEST256_LEN] = {0};
+ uint8_t srv_four[DIGEST256_LEN] = {0};
+ uint8_t srv_five[DIGEST256_LEN] = {0};
+
+ (void) arg;
+
+ /* Get the cached SRVs: we gonna use them later for verification */
+ cached_disaster_srv_one = get_first_cached_disaster_srv();
+ cached_disaster_srv_two = get_second_cached_disaster_srv();
+
+ /* Compute some srvs */
+ get_disaster_srv(1, srv_one);
+ get_disaster_srv(2, srv_two);
+
+ /* Check that the cached ones where updated */
+ tt_mem_op(cached_disaster_srv_one, OP_EQ, srv_one, DIGEST256_LEN);
+ tt_mem_op(cached_disaster_srv_two, OP_EQ, srv_two, DIGEST256_LEN);
+
+ /* Ask for an SRV that has already been computed */
+ get_disaster_srv(2, srv_two);
+ /* and check that the cache entries have not changed */
+ tt_mem_op(cached_disaster_srv_one, OP_EQ, srv_one, DIGEST256_LEN);
+ tt_mem_op(cached_disaster_srv_two, OP_EQ, srv_two, DIGEST256_LEN);
+
+ /* Ask for a new SRV */
+ get_disaster_srv(3, srv_three);
+ tt_mem_op(cached_disaster_srv_one, OP_EQ, srv_three, DIGEST256_LEN);
+ tt_mem_op(cached_disaster_srv_two, OP_EQ, srv_two, DIGEST256_LEN);
+
+ /* Ask for another SRV: none of the original SRVs should now be cached */
+ get_disaster_srv(4, srv_four);
+ tt_mem_op(cached_disaster_srv_one, OP_EQ, srv_three, DIGEST256_LEN);
+ tt_mem_op(cached_disaster_srv_two, OP_EQ, srv_four, DIGEST256_LEN);
+
+ /* Ask for yet another SRV */
+ get_disaster_srv(5, srv_five);
+ tt_mem_op(cached_disaster_srv_one, OP_EQ, srv_five, DIGEST256_LEN);
+ tt_mem_op(cached_disaster_srv_two, OP_EQ, srv_four, DIGEST256_LEN);
+
+ done:
+ ;
+}
+
+struct testcase_t hs_common_tests[] = {
+ { "build_address", test_build_address, TT_FORK,
+ NULL, NULL },
+ { "validate_address", test_validate_address, TT_FORK,
+ NULL, NULL },
+ { "time_period", test_time_period, TT_FORK,
+ NULL, NULL },
+ { "start_time_of_next_time_period", test_start_time_of_next_time_period,
+ TT_FORK, NULL, NULL },
+ { "desc_overlap_period", test_desc_overlap_period, TT_FORK,
+ NULL, NULL },
+ { "desc_overlap_period_testnet", test_desc_overlap_period_testnet, TT_FORK,
+ NULL, NULL },
+ { "desc_responsible_hsdirs", test_responsible_hsdirs, TT_FORK,
+ NULL, NULL },
+ { "disaster_srv", test_disaster_srv, TT_FORK, NULL, NULL },
+
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_hs_config.c b/src/test/test_hs_config.c
index d14e3dd8ae..a76be301d3 100644
--- a/src/test/test_hs_config.c
+++ b/src/test/test_hs_config.c
@@ -453,7 +453,7 @@ test_staging_service_v3(void *arg)
/* Ok, we have a service in our map! Registration went well. */
tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1);
/* Make sure we don't have a magic v2 service out of this. */
- tt_int_op(num_rend_services(), OP_EQ, 0);
+ tt_int_op(rend_num_services(), OP_EQ, 0);
done:
hs_free_all();
diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c
index b1abe381d4..77bdd4be5e 100644
--- a/src/test/test_hs_descriptor.c
+++ b/src/test/test_hs_descriptor.c
@@ -296,6 +296,7 @@ test_decode_descriptor(void *arg)
hs_descriptor_t *desc = NULL;
hs_descriptor_t *decoded = NULL;
hs_descriptor_t *desc_no_ip = NULL;
+ uint8_t subcredential[DIGEST256_LEN];
(void) arg;
@@ -303,15 +304,18 @@ test_decode_descriptor(void *arg)
tt_int_op(ret, ==, 0);
desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
+ hs_helper_get_subcred_from_identity_keypair(&signing_kp,
+ subcredential);
+
/* Give some bad stuff to the decoding function. */
- ret = hs_desc_decode_descriptor("hladfjlkjadf", NULL, &decoded);
+ ret = hs_desc_decode_descriptor("hladfjlkjadf", subcredential, &decoded);
tt_int_op(ret, OP_EQ, -1);
ret = hs_desc_encode_descriptor(desc, &signing_kp, &encoded);
tt_int_op(ret, ==, 0);
tt_assert(encoded);
- ret = hs_desc_decode_descriptor(encoded, NULL, &decoded);
+ ret = hs_desc_decode_descriptor(encoded, subcredential, &decoded);
tt_int_op(ret, ==, 0);
tt_assert(decoded);
@@ -322,6 +326,8 @@ test_decode_descriptor(void *arg)
ed25519_keypair_t signing_kp_no_ip;
ret = ed25519_keypair_generate(&signing_kp_no_ip, 0);
tt_int_op(ret, ==, 0);
+ hs_helper_get_subcred_from_identity_keypair(&signing_kp_no_ip,
+ subcredential);
desc_no_ip = hs_helper_build_hs_desc_no_ip(&signing_kp_no_ip);
tt_assert(desc_no_ip);
tor_free(encoded);
@@ -329,7 +335,7 @@ test_decode_descriptor(void *arg)
tt_int_op(ret, ==, 0);
tt_assert(encoded);
hs_descriptor_free(decoded);
- ret = hs_desc_decode_descriptor(encoded, NULL, &decoded);
+ ret = hs_desc_decode_descriptor(encoded, subcredential, &decoded);
tt_int_op(ret, ==, 0);
tt_assert(decoded);
}
@@ -427,7 +433,7 @@ test_decode_invalid_intro_point(void *arg)
const char *junk = "this is not a descriptor";
ip = decode_introduction_point(desc, junk);
tt_assert(!ip);
- desc_intro_point_free(ip);
+ hs_desc_intro_point_free(ip);
ip = NULL;
}
@@ -445,7 +451,7 @@ test_decode_invalid_intro_point(void *arg)
tt_assert(!ip);
tor_free(encoded_ip);
smartlist_free(lines);
- desc_intro_point_free(ip);
+ hs_desc_intro_point_free(ip);
ip = NULL;
}
@@ -545,7 +551,7 @@ test_decode_invalid_intro_point(void *arg)
done:
hs_descriptor_free(desc);
- desc_intro_point_free(ip);
+ hs_desc_intro_point_free(ip);
}
static void
diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c
index c6197875b5..09af10904b 100644
--- a/src/test/test_hs_intropoint.c
+++ b/src/test/test_hs_intropoint.c
@@ -17,21 +17,66 @@
#include "log_test_helpers.h"
#include "or.h"
+#include "circuitlist.h"
+#include "circuituse.h"
#include "ht.h"
+#include "relay.h"
+#include "rendservice.h"
+
+#include "hs_cell.h"
+#include "hs_circuitmap.h"
+#include "hs_common.h"
+#include "hs_intropoint.h"
+#include "hs_service.h"
/* Trunnel. */
#include "hs/cell_establish_intro.h"
#include "hs/cell_introduce1.h"
#include "hs/cell_common.h"
-#include "hs_service.h"
-#include "hs_common.h"
-#include "hs_circuitmap.h"
-#include "hs_intropoint.h"
-#include "circuitlist.h"
-#include "circuituse.h"
-#include "rendservice.h"
-#include "relay.h"
+static size_t
+new_establish_intro_cell(const char *circ_nonce,
+ trn_cell_establish_intro_t **cell_out)
+{
+ ssize_t cell_len = 0;
+ uint8_t buf[RELAY_PAYLOAD_SIZE] = {0};
+ trn_cell_establish_intro_t *cell = NULL;
+ hs_service_intro_point_t *ip = NULL;
+
+ /* Auth key pair is generated in the constructor so we are all set for
+ * using this IP object. */
+ ip = service_intro_point_new(NULL, 0);
+ tt_assert(ip);
+ cell_len = hs_cell_build_establish_intro(circ_nonce, ip, buf);
+ tt_u64_op(cell_len, OP_GT, 0);
+
+ cell_len = trn_cell_establish_intro_parse(&cell, buf, sizeof(buf));
+ tt_int_op(cell_len, OP_GT, 0);
+ tt_assert(cell);
+ *cell_out = cell;
+
+ done:
+ service_intro_point_free(ip);
+ return cell_len;
+}
+
+static ssize_t
+new_establish_intro_encoded_cell(const char *circ_nonce, uint8_t *cell_out)
+{
+ ssize_t cell_len = 0;
+ hs_service_intro_point_t *ip = NULL;
+
+ /* Auth key pair is generated in the constructor so we are all set for
+ * using this IP object. */
+ ip = service_intro_point_new(NULL, 0);
+ tt_assert(ip);
+ cell_len = hs_cell_build_establish_intro(circ_nonce, ip, cell_out);
+ tt_u64_op(cell_len, OP_GT, 0);
+
+ done:
+ service_intro_point_free(ip);
+ return cell_len;
+}
/* Mock function to avoid networking in unittests */
static int
@@ -122,29 +167,24 @@ static void
test_establish_intro_wrong_purpose(void *arg)
{
int retval;
- trn_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;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ char circ_nonce[DIGEST_LEN] = {0};
+ uint8_t cell_body[RELAY_PAYLOAD_SIZE];
+ or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
(void)arg;
/* Get the auth key of the intro point */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- memcpy(intro_circ->rend_circ_nonce, circuit_key_material, DIGEST_LEN);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ memcpy(intro_circ->rend_circ_nonce, circ_nonce, 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);
+ cell_len = new_establish_intro_encoded_cell(circ_nonce, cell_body);
+ tt_u64_op(cell_len, OP_GT, 0);
/* Receive the cell. Should fail. */
setup_full_capture_of_logs(LOG_INFO);
@@ -154,18 +194,16 @@ test_establish_intro_wrong_purpose(void *arg)
tt_int_op(retval, ==, -1);
done:
- trn_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,
- uint8_t *circuit_key_material)
+helper_prepare_circ_for_intro(or_circuit_t *circ, const char *circ_nonce)
{
/* 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);
+ memcpy(circ->rend_circ_nonce, circ_nonce, DIGEST_LEN);
}
/* Send an empty ESTABLISH_INTRO cell. Should fail. */
@@ -174,17 +212,17 @@ test_establish_intro_wrong_keytype(void *arg)
{
int retval;
or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ char circ_nonce[DIGEST_LEN] = {0};
- (void)arg;
+ (void) arg;
/* Get the auth key of the intro point */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* Receive the cell. Should fail. */
setup_full_capture_of_logs(LOG_INFO);
- retval = hs_intro_received_establish_intro(intro_circ, (uint8_t*)"", 0);
+ retval = hs_intro_received_establish_intro(intro_circ, (uint8_t *) "", 0);
expect_log_msg_containing("Empty ESTABLISH_INTRO cell.");
teardown_capture_of_logs();
tt_int_op(retval, ==, -1);
@@ -198,26 +236,21 @@ static void
test_establish_intro_wrong_keytype2(void *arg)
{
int retval;
- trn_cell_establish_intro_t *establish_intro_cell = NULL;
- or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
+ char circ_nonce[DIGEST_LEN] = {0};
uint8_t cell_body[RELAY_PAYLOAD_SIZE];
ssize_t cell_len = 0;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
- (void)arg;
+ (void) arg;
/* Get the auth key of the intro point */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* 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);
+ * attempt to parse it. */
+ cell_len = new_establish_intro_encoded_cell(circ_nonce, cell_body);
+ tt_u64_op(cell_len, OP_GT, 0);
/* Mutate the auth key type! :) */
cell_body[0] = 42;
@@ -230,7 +263,6 @@ test_establish_intro_wrong_keytype2(void *arg)
tt_int_op(retval, ==, -1);
done:
- trn_cell_establish_intro_free(establish_intro_cell);
circuit_free(TO_CIRCUIT(intro_circ));
}
@@ -239,26 +271,27 @@ static void
test_establish_intro_wrong_mac(void *arg)
{
int retval;
- trn_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];
+ char circ_nonce[DIGEST_LEN] = {0};
ssize_t cell_len = 0;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ uint8_t cell_body[RELAY_PAYLOAD_SIZE];
+ trn_cell_establish_intro_t *cell = NULL;
+ or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
- (void)arg;
+ (void) arg;
/* Get the auth key of the intro point */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* 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);
+ * attempt to parse it. */
+ cell_len = new_establish_intro_cell(circ_nonce, &cell);
+ tt_u64_op(cell_len, OP_GT, 0);
+ tt_assert(cell);
+
/* Mangle one byte of the MAC. */
uint8_t *handshake_ptr =
- trn_cell_establish_intro_getarray_handshake_mac(establish_intro_cell);
+ trn_cell_establish_intro_getarray_handshake_mac(cell);
handshake_ptr[TRUNNEL_SHA3_256_LEN - 1]++;
/* We need to resign the payload with that change. */
{
@@ -269,26 +302,26 @@ test_establish_intro_wrong_mac(void *arg)
retval = ed25519_keypair_generate(&key_struct, 0);
tt_int_op(retval, OP_EQ, 0);
uint8_t *auth_key_ptr =
- trn_cell_establish_intro_getarray_auth_key(establish_intro_cell);
+ trn_cell_establish_intro_getarray_auth_key(cell);
memcpy(auth_key_ptr, key_struct.pubkey.pubkey, ED25519_PUBKEY_LEN);
/* Encode payload so we can sign it. */
- cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body),
- establish_intro_cell);
- tt_int_op(cell_len, >, 0);
+ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body),
+ cell);
+ tt_int_op(cell_len, OP_GT, 0);
retval = ed25519_sign_prefixed(&sig, cell_body,
cell_len -
- (ED25519_SIG_LEN +
- sizeof(establish_intro_cell->sig_len)),
+ (ED25519_SIG_LEN + sizeof(cell->sig_len)),
ESTABLISH_INTRO_SIG_PREFIX, &key_struct);
tt_int_op(retval, OP_EQ, 0);
/* And write the signature to the cell */
uint8_t *sig_ptr =
- trn_cell_establish_intro_getarray_sig(establish_intro_cell);
- memcpy(sig_ptr, sig.sig, establish_intro_cell->sig_len);
+ trn_cell_establish_intro_getarray_sig(cell);
+ memcpy(sig_ptr, sig.sig, cell->sig_len);
/* Re-encode with the new signature. */
- cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body),
- establish_intro_cell);
+ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body),
+ cell);
+ tt_int_op(cell_len, OP_GT, 0);
}
/* Receive the cell. Should fail because our MAC is wrong. */
@@ -299,7 +332,7 @@ test_establish_intro_wrong_mac(void *arg)
tt_int_op(retval, ==, -1);
done:
- trn_cell_establish_intro_free(establish_intro_cell);
+ trn_cell_establish_intro_free(cell);
circuit_free(TO_CIRCUIT(intro_circ));
}
@@ -309,32 +342,32 @@ static void
test_establish_intro_wrong_auth_key_len(void *arg)
{
int retval;
- trn_cell_establish_intro_t *establish_intro_cell = NULL;
- or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
+ char circ_nonce[DIGEST_LEN] = {0};
uint8_t cell_body[RELAY_PAYLOAD_SIZE];
ssize_t cell_len = 0;
size_t bad_auth_key_len = ED25519_PUBKEY_LEN - 1;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ trn_cell_establish_intro_t *cell = NULL;
+ or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
- (void)arg;
+ (void) arg;
/* Get the auth key of the intro point */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* 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);
+ * attempt to parse it. */
+ cell_len = new_establish_intro_cell(circ_nonce, &cell);
+ tt_u64_op(cell_len, OP_GT, 0);
+ tt_assert(cell);
+
/* Mangle the auth key length. */
- trn_cell_establish_intro_set_auth_key_len(establish_intro_cell,
- bad_auth_key_len);
- trn_cell_establish_intro_setlen_auth_key(establish_intro_cell,
- bad_auth_key_len);
- cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body),
- establish_intro_cell);
- tt_int_op(cell_len, >, 0);
+ trn_cell_establish_intro_set_auth_key_len(cell, bad_auth_key_len);
+ trn_cell_establish_intro_setlen_auth_key(cell, bad_auth_key_len);
+ /* Encode cell. */
+ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body),
+ cell);
+ tt_int_op(cell_len, OP_GT, 0);
/* Receive the cell. Should fail. */
setup_full_capture_of_logs(LOG_INFO);
@@ -344,7 +377,7 @@ test_establish_intro_wrong_auth_key_len(void *arg)
tt_int_op(retval, ==, -1);
done:
- trn_cell_establish_intro_free(establish_intro_cell);
+ trn_cell_establish_intro_free(cell);
circuit_free(TO_CIRCUIT(intro_circ));
}
@@ -354,30 +387,32 @@ static void
test_establish_intro_wrong_sig_len(void *arg)
{
int retval;
- trn_cell_establish_intro_t *establish_intro_cell = NULL;
- or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
+ char circ_nonce[DIGEST_LEN] = {0};
uint8_t cell_body[RELAY_PAYLOAD_SIZE];
ssize_t cell_len = 0;
size_t bad_sig_len = ED25519_SIG_LEN - 1;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ trn_cell_establish_intro_t *cell = NULL;
+ or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
- (void)arg;
+ (void) arg;
/* Get the auth key of the intro point */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* 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);
+ * attempt to parse it. */
+ cell_len = new_establish_intro_cell(circ_nonce, &cell);
+ tt_u64_op(cell_len, OP_GT, 0);
+ tt_assert(cell);
+
/* Mangle the signature length. */
- trn_cell_establish_intro_set_sig_len(establish_intro_cell, bad_sig_len);
- trn_cell_establish_intro_setlen_sig(establish_intro_cell, bad_sig_len);
- cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body),
- establish_intro_cell);
- tt_int_op(cell_len, >, 0);
+ trn_cell_establish_intro_set_sig_len(cell, bad_sig_len);
+ trn_cell_establish_intro_setlen_sig(cell, bad_sig_len);
+ /* Encode cell. */
+ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body),
+ cell);
+ tt_int_op(cell_len, OP_GT, 0);
/* Receive the cell. Should fail. */
setup_full_capture_of_logs(LOG_INFO);
@@ -387,7 +422,7 @@ test_establish_intro_wrong_sig_len(void *arg)
tt_int_op(retval, ==, -1);
done:
- trn_cell_establish_intro_free(establish_intro_cell);
+ trn_cell_establish_intro_free(cell);
circuit_free(TO_CIRCUIT(intro_circ));
}
@@ -397,29 +432,24 @@ static void
test_establish_intro_wrong_sig(void *arg)
{
int retval;
- trn_cell_establish_intro_t *establish_intro_cell = NULL;
- or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
+ char circ_nonce[DIGEST_LEN] = {0};
uint8_t cell_body[RELAY_PAYLOAD_SIZE];
ssize_t cell_len = 0;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
- (void)arg;
+ (void) arg;
/* Get the auth key of the intro point */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* 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);
+ cell_len = new_establish_intro_encoded_cell(circ_nonce, cell_body);
+ tt_u64_op(cell_len, OP_GT, 0);
/* Mutate the last byte (signature)! :) */
- cell_body[cell_len-1]++;
+ cell_body[cell_len - 1]++;
/* Receive the cell. Should fail. */
setup_full_capture_of_logs(LOG_INFO);
@@ -429,7 +459,6 @@ test_establish_intro_wrong_sig(void *arg)
tt_int_op(retval, ==, -1);
done:
- trn_cell_establish_intro_free(establish_intro_cell);
circuit_free(TO_CIRCUIT(intro_circ));
}
@@ -439,32 +468,32 @@ static trn_cell_establish_intro_t *
helper_establish_intro_v3(or_circuit_t *intro_circ)
{
int retval;
- trn_cell_establish_intro_t *establish_intro_cell = NULL;
+ char circ_nonce[DIGEST_LEN] = {0};
uint8_t cell_body[RELAY_PAYLOAD_SIZE];
ssize_t cell_len = 0;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ trn_cell_establish_intro_t *cell = NULL;
tt_assert(intro_circ);
/* Prepare the circuit for the incoming ESTABLISH_INTRO */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* 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);
+ * attempt to parse it. */
+ cell_len = new_establish_intro_cell(circ_nonce, &cell);
+ tt_u64_op(cell_len, OP_GT, 0);
+ tt_assert(cell);
+ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body),
+ cell);
+ tt_int_op(cell_len, OP_GT, 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;
+ return cell;
}
/* Helper function: Send a well-formed v2 ESTABLISH_INTRO cell to
@@ -476,22 +505,22 @@ helper_establish_intro_v2(or_circuit_t *intro_circ)
int retval;
uint8_t cell_body[RELAY_PAYLOAD_SIZE];
ssize_t cell_len = 0;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ char circ_nonce[DIGEST_LEN] = {0};
tt_assert(intro_circ);
/* Prepare the circuit for the incoming ESTABLISH_INTRO */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* 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,
- sizeof(cell_body),
- key1,
- (char *) circuit_key_material);
+ /* Use old circ_nonce why not */
+ cell_len = rend_service_encode_establish_intro_cell(
+ (char*)cell_body,
+ sizeof(cell_body), key1,
+ circ_nonce);
tt_int_op(cell_len, >, 0);
/* Receive legacy establish_intro */
diff --git a/src/test/test_hs_ntor.c b/src/test/test_hs_ntor.c
new file mode 100644
index 0000000000..2544997106
--- /dev/null
+++ b/src/test/test_hs_ntor.c
@@ -0,0 +1,114 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_hs_ntor.c
+ * \brief Test hidden service ntor functionality.
+ */
+
+#include "test.h"
+#include "test_helpers.h"
+#include "log_test_helpers.h"
+
+#include "hs_ntor.h"
+
+/* Test the HS ntor handshake. Simulate the sending of an encrypted INTRODUCE1
+ * cell, and verify the proper derivation of decryption keys on the other end.
+ * Then simulate the sending of an authenticated RENDEZVOUS1 cell and verify
+ * the proper verification on the other end. */
+static void
+test_hs_ntor(void *arg)
+{
+ int retval;
+
+ uint8_t subcredential[DIGEST256_LEN];
+
+ ed25519_keypair_t service_intro_auth_keypair;
+ curve25519_keypair_t service_intro_enc_keypair;
+ curve25519_keypair_t service_ephemeral_rend_keypair;
+
+ curve25519_keypair_t client_ephemeral_enc_keypair;
+
+ hs_ntor_intro_cell_keys_t client_hs_ntor_intro_cell_keys;
+ hs_ntor_intro_cell_keys_t service_hs_ntor_intro_cell_keys;
+
+ hs_ntor_rend_cell_keys_t service_hs_ntor_rend_cell_keys;
+ hs_ntor_rend_cell_keys_t client_hs_ntor_rend_cell_keys;
+
+ (void) arg;
+
+ /* Generate fake data for this unittest */
+ {
+ /* Generate fake subcredential */
+ memset(subcredential, 'Z', DIGEST256_LEN);
+
+ /* service */
+ curve25519_keypair_generate(&service_intro_enc_keypair, 0);
+ ed25519_keypair_generate(&service_intro_auth_keypair, 0);
+ curve25519_keypair_generate(&service_ephemeral_rend_keypair, 0);
+ /* client */
+ curve25519_keypair_generate(&client_ephemeral_enc_keypair, 0);
+ }
+
+ /* Client: Simulate the sending of an encrypted INTRODUCE1 cell */
+ retval =
+ hs_ntor_client_get_introduce1_keys(&service_intro_auth_keypair.pubkey,
+ &service_intro_enc_keypair.pubkey,
+ &client_ephemeral_enc_keypair,
+ subcredential,
+ &client_hs_ntor_intro_cell_keys);
+ tt_int_op(retval, ==, 0);
+
+ /* Service: Simulate the decryption of the received INTRODUCE1 */
+ retval =
+ hs_ntor_service_get_introduce1_keys(&service_intro_auth_keypair.pubkey,
+ &service_intro_enc_keypair,
+ &client_ephemeral_enc_keypair.pubkey,
+ subcredential,
+ &service_hs_ntor_intro_cell_keys);
+ tt_int_op(retval, ==, 0);
+
+ /* Test that the INTRODUCE1 encryption/mac keys match! */
+ tt_mem_op(client_hs_ntor_intro_cell_keys.enc_key, OP_EQ,
+ service_hs_ntor_intro_cell_keys.enc_key,
+ CIPHER256_KEY_LEN);
+ tt_mem_op(client_hs_ntor_intro_cell_keys.mac_key, OP_EQ,
+ service_hs_ntor_intro_cell_keys.mac_key,
+ DIGEST256_LEN);
+
+ /* Service: Simulate creation of RENDEZVOUS1 key material. */
+ retval =
+ hs_ntor_service_get_rendezvous1_keys(&service_intro_auth_keypair.pubkey,
+ &service_intro_enc_keypair,
+ &service_ephemeral_rend_keypair,
+ &client_ephemeral_enc_keypair.pubkey,
+ &service_hs_ntor_rend_cell_keys);
+ tt_int_op(retval, ==, 0);
+
+ /* Client: Simulate the verification of a received RENDEZVOUS1 cell */
+ retval =
+ hs_ntor_client_get_rendezvous1_keys(&service_intro_auth_keypair.pubkey,
+ &client_ephemeral_enc_keypair,
+ &service_intro_enc_keypair.pubkey,
+ &service_ephemeral_rend_keypair.pubkey,
+ &client_hs_ntor_rend_cell_keys);
+ tt_int_op(retval, ==, 0);
+
+ /* Test that the RENDEZVOUS1 key material match! */
+ tt_mem_op(client_hs_ntor_rend_cell_keys.rend_cell_auth_mac, OP_EQ,
+ service_hs_ntor_rend_cell_keys.rend_cell_auth_mac,
+ DIGEST256_LEN);
+ tt_mem_op(client_hs_ntor_rend_cell_keys.ntor_key_seed, OP_EQ,
+ service_hs_ntor_rend_cell_keys.ntor_key_seed,
+ DIGEST256_LEN);
+ done:
+ ;
+}
+
+struct testcase_t hs_ntor_tests[] = {
+ { "hs_ntor", test_hs_ntor, TT_FORK,
+ NULL, NULL },
+
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c
index 4a7cb81140..aea2c8fbfb 100644
--- a/src/test/test_hs_service.c
+++ b/src/test/test_hs_service.c
@@ -15,341 +15,114 @@
#define HS_SERVICE_PRIVATE
#define HS_INTROPOINT_PRIVATE
#define MAIN_PRIVATE
+#define NETWORKSTATUS_PRIVATE
+#define STATEFILE_PRIVATE
#define TOR_CHANNEL_INTERNAL_
#include "test.h"
#include "test_helpers.h"
#include "log_test_helpers.h"
#include "rend_test_helpers.h"
+#include "hs_test_helpers.h"
#include "or.h"
-#include "channeltls.h"
+#include "config.h"
#include "circuitbuild.h"
#include "circuitlist.h"
#include "circuituse.h"
-#include "config.h"
-#include "connection.h"
#include "crypto.h"
-#include "hs_circuit.h"
+#include "networkstatus.h"
+#include "nodelist.h"
+#include "relay.h"
+
#include "hs_common.h"
#include "hs_config.h"
+#include "hs_circuit.h"
#include "hs_ident.h"
#include "hs_intropoint.h"
#include "hs_ntor.h"
#include "hs_service.h"
#include "main.h"
#include "rendservice.h"
+#include "statefile.h"
/* Trunnel */
#include "hs/cell_establish_intro.h"
-/* Helper: from a set of options in conf, configure a service which will add
- * it to the staging list of the HS subsytem. */
-static int
-helper_config_service(const char *conf)
-{
- int ret = 0;
- or_options_t *options = NULL;
- tt_assert(conf);
- options = helper_parse_options(conf);
- tt_assert(options);
- ret = hs_config_service_all(options, 0);
- done:
- or_options_free(options);
- return ret;
-}
+static networkstatus_t mock_ns;
-/** 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)
+static networkstatus_t *
+mock_networkstatus_get_live_consensus(time_t now)
{
- (void) arg;
- ssize_t retval;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
- uint8_t buf[RELAY_PAYLOAD_SIZE];
- trn_cell_establish_intro_t *cell_out = NULL;
- trn_cell_establish_intro_t *cell_in = NULL;
-
- crypto_rand((char *) 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 = trn_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:
- trn_cell_establish_intro_free(cell_out);
- trn_cell_establish_intro_free(cell_in);
+ (void) now;
+ return &mock_ns;
}
-/* 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;
-}
+static or_state_t *dummy_state = NULL;
-/** We simulate a failure to create an ESTABLISH_INTRO cell */
-static void
-test_gen_establish_intro_cell_bad(void *arg)
+/* Mock function to get fake or state (used for rev counters) */
+static or_state_t *
+get_or_state_replacement(void)
{
- (void) arg;
- trn_cell_establish_intro_t *cell = NULL;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
-
- MOCK(ed25519_sign_prefixed, mock_ed25519_sign_prefixed);
-
- crypto_rand((char *) 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:
- trn_cell_establish_intro_free(cell);
- UNMOCK(ed25519_sign_prefixed);
+ return dummy_state;
}
-/** Test the HS ntor handshake. Simulate the sending of an encrypted INTRODUCE1
- * cell, and verify the proper derivation of decryption keys on the other end.
- * Then simulate the sending of an authenticated RENDEZVOUS1 cell and verify
- * the proper verification on the other end. */
+/* Mock function because we are not trying to test the close circuit that does
+ * an awful lot of checks on the circuit object. */
static void
-test_hs_ntor(void *arg)
+mock_circuit_mark_for_close(circuit_t *circ, int reason, int line,
+ const char *file)
{
- int retval;
-
- uint8_t subcredential[DIGEST256_LEN];
-
- ed25519_keypair_t service_intro_auth_keypair;
- curve25519_keypair_t service_intro_enc_keypair;
- curve25519_keypair_t service_ephemeral_rend_keypair;
-
- curve25519_keypair_t client_ephemeral_enc_keypair;
-
- hs_ntor_intro_cell_keys_t client_hs_ntor_intro_cell_keys;
- hs_ntor_intro_cell_keys_t service_hs_ntor_intro_cell_keys;
-
- hs_ntor_rend_cell_keys_t service_hs_ntor_rend_cell_keys;
- hs_ntor_rend_cell_keys_t client_hs_ntor_rend_cell_keys;
-
- (void) arg;
-
- /* Generate fake data for this unittest */
- {
- /* Generate fake subcredential */
- memset(subcredential, 'Z', DIGEST256_LEN);
-
- /* service */
- curve25519_keypair_generate(&service_intro_enc_keypair, 0);
- ed25519_keypair_generate(&service_intro_auth_keypair, 0);
- curve25519_keypair_generate(&service_ephemeral_rend_keypair, 0);
- /* client */
- curve25519_keypair_generate(&client_ephemeral_enc_keypair, 0);
- }
-
- /* Client: Simulate the sending of an encrypted INTRODUCE1 cell */
- retval =
- hs_ntor_client_get_introduce1_keys(&service_intro_auth_keypair.pubkey,
- &service_intro_enc_keypair.pubkey,
- &client_ephemeral_enc_keypair,
- subcredential,
- &client_hs_ntor_intro_cell_keys);
- tt_int_op(retval, ==, 0);
-
- /* Service: Simulate the decryption of the received INTRODUCE1 */
- retval =
- hs_ntor_service_get_introduce1_keys(&service_intro_auth_keypair.pubkey,
- &service_intro_enc_keypair,
- &client_ephemeral_enc_keypair.pubkey,
- subcredential,
- &service_hs_ntor_intro_cell_keys);
- tt_int_op(retval, ==, 0);
-
- /* Test that the INTRODUCE1 encryption/mac keys match! */
- tt_mem_op(client_hs_ntor_intro_cell_keys.enc_key, OP_EQ,
- service_hs_ntor_intro_cell_keys.enc_key,
- CIPHER256_KEY_LEN);
- tt_mem_op(client_hs_ntor_intro_cell_keys.mac_key, OP_EQ,
- service_hs_ntor_intro_cell_keys.mac_key,
- DIGEST256_LEN);
-
- /* Service: Simulate creation of RENDEZVOUS1 key material. */
- retval =
- hs_ntor_service_get_rendezvous1_keys(&service_intro_auth_keypair.pubkey,
- &service_intro_enc_keypair,
- &service_ephemeral_rend_keypair,
- &client_ephemeral_enc_keypair.pubkey,
- &service_hs_ntor_rend_cell_keys);
- tt_int_op(retval, ==, 0);
-
- /* Client: Simulate the verification of a received RENDEZVOUS1 cell */
- retval =
- hs_ntor_client_get_rendezvous1_keys(&service_intro_auth_keypair.pubkey,
- &client_ephemeral_enc_keypair,
- &service_intro_enc_keypair.pubkey,
- &service_ephemeral_rend_keypair.pubkey,
- &client_hs_ntor_rend_cell_keys);
- tt_int_op(retval, ==, 0);
-
- /* Test that the RENDEZVOUS1 key material match! */
- tt_mem_op(client_hs_ntor_rend_cell_keys.rend_cell_auth_mac, OP_EQ,
- service_hs_ntor_rend_cell_keys.rend_cell_auth_mac,
- DIGEST256_LEN);
- tt_mem_op(client_hs_ntor_rend_cell_keys.ntor_key_seed, OP_EQ,
- service_hs_ntor_rend_cell_keys.ntor_key_seed,
- DIGEST256_LEN);
- done:
- ;
+ (void) circ;
+ (void) reason;
+ (void) line;
+ (void) file;
+ return;
}
-static void
-test_validate_address(void *arg)
+static int
+mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ,
+ uint8_t relay_command, const char *payload,
+ size_t payload_len,
+ crypt_path_t *cpath_layer,
+ const char *filename, int lineno)
{
- int ret;
-
- (void) arg;
-
- /* Address too short and too long. */
- setup_full_capture_of_logs(LOG_WARN);
- ret = hs_address_is_valid("blah");
- tt_int_op(ret, OP_EQ, 0);
- expect_log_msg_containing("has an invalid length");
- teardown_capture_of_logs();
-
- setup_full_capture_of_logs(LOG_WARN);
- ret = hs_address_is_valid(
- "p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnadb");
- tt_int_op(ret, OP_EQ, 0);
- expect_log_msg_containing("has an invalid length");
- teardown_capture_of_logs();
-
- /* Invalid checksum (taken from prop224) */
- setup_full_capture_of_logs(LOG_WARN);
- ret = hs_address_is_valid(
- "l5satjgud6gucryazcyvyvhuxhr74u6ygigiuyixe3a6ysis67ororad");
- tt_int_op(ret, OP_EQ, 0);
- expect_log_msg_containing("invalid checksum");
- teardown_capture_of_logs();
-
- setup_full_capture_of_logs(LOG_WARN);
- ret = hs_address_is_valid(
- "btojiu7nu5y5iwut64eufevogqdw4wmqzugnoluw232r4t3ecsfv37ad");
- tt_int_op(ret, OP_EQ, 0);
- expect_log_msg_containing("invalid checksum");
- teardown_capture_of_logs();
-
- /* Non base32 decodable string. */
- setup_full_capture_of_logs(LOG_WARN);
- ret = hs_address_is_valid(
- "????????????????????????????????????????????????????????");
- tt_int_op(ret, OP_EQ, 0);
- expect_log_msg_containing("can't be decoded");
- teardown_capture_of_logs();
-
- /* Valid address. */
- ret = hs_address_is_valid(
- "p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnad");
- tt_int_op(ret, OP_EQ, 1);
-
- done:
- ;
+ (void) stream_id;
+ (void) circ;
+ (void) relay_command;
+ (void) payload;
+ (void) payload_len;
+ (void) cpath_layer;
+ (void) filename;
+ (void) lineno;
+ return 0;
}
-static void
-test_build_address(void *arg)
+/* Mock function that always return true so we can test the descriptor
+ * creation of the next time period deterministically. */
+static int
+mock_hs_overlap_mode_is_active_true(const networkstatus_t *consensus,
+ time_t now)
{
- int ret;
- char onion_addr[HS_SERVICE_ADDR_LEN_BASE32 + 1];
- ed25519_public_key_t pubkey;
-
- (void) arg;
-
- /* The following has been created with hs_build_address.py script that
- * follows proposal 224 specification to build an onion address. */
- static const char *test_addr =
- "ijbeeqscijbeeqscijbeeqscijbeeqscijbeeqscijbeeqscijbezhid";
-
- /* Let's try to build the same onion address that the script can do. Key is
- * a long set of very random \x42 :). */
- memset(&pubkey, '\x42', sizeof(pubkey));
- hs_build_address(&pubkey, HS_VERSION_THREE, onion_addr);
- tt_str_op(test_addr, OP_EQ, onion_addr);
- /* Validate that address. */
- ret = hs_address_is_valid(onion_addr);
- tt_int_op(ret, OP_EQ, 1);
-
- done:
- ;
+ (void) consensus;
+ (void) now;
+ return 1;
}
-/** Test that our HS time period calculation functions work properly */
-static void
-test_time_period(void *arg)
+/* Helper: from a set of options in conf, configure a service which will add
+ * it to the staging list of the HS subsytem. */
+static int
+helper_config_service(const char *conf)
{
- (void) arg;
- uint64_t tn;
- int retval;
- time_t fake_time;
-
- /* Let's do the example in prop224 section [TIME-PERIODS] */
- retval = parse_rfc1123_time("Wed, 13 Apr 2016 11:00:00 UTC",
- &fake_time);
- tt_int_op(retval, ==, 0);
-
- /* Check that the time period number is right */
- tn = get_time_period_num(fake_time);
- tt_u64_op(tn, ==, 16903);
-
- /* Increase current time to 11:59:59 UTC and check that the time period
- number is still the same */
- fake_time += 3599;
- tn = get_time_period_num(fake_time);
- tt_u64_op(tn, ==, 16903);
-
- /* Now take time to 12:00:00 UTC and check that the time period rotated */
- fake_time += 1;
- tn = get_time_period_num(fake_time);
- tt_u64_op(tn, ==, 16904);
-
- /* Now also check our hs_get_next_time_period_num() function */
- tn = hs_get_next_time_period_num(fake_time);
- tt_u64_op(tn, ==, 16905);
-
+ int ret = 0;
+ or_options_t *options = NULL;
+ tt_assert(conf);
+ options = helper_parse_options(conf);
+ tt_assert(options);
+ ret = hs_config_service_all(options, 0);
done:
- ;
+ or_options_free(options);
+ return ret;
}
/* Test: Ensure that setting up rendezvous circuits works correctly. */
@@ -418,6 +191,77 @@ test_e2e_rend_circuit_setup(void *arg)
circuit_free(TO_CIRCUIT(or_circ));
}
+/* Helper: Return a newly allocated and initialized origin circuit with
+ * purpose and flags. A default HS identifier is set to an ed25519
+ * authentication key for introduction point. */
+static origin_circuit_t *
+helper_create_origin_circuit(int purpose, int flags)
+{
+ origin_circuit_t *circ = NULL;
+
+ circ = origin_circuit_init(purpose, flags);
+ tt_assert(circ);
+ circ->cpath = tor_malloc_zero(sizeof(crypt_path_t));
+ circ->cpath->magic = CRYPT_PATH_MAGIC;
+ circ->cpath->state = CPATH_STATE_OPEN;
+ circ->cpath->package_window = circuit_initial_package_window();
+ circ->cpath->deliver_window = CIRCWINDOW_START;
+ circ->cpath->prev = circ->cpath;
+ /* Random nonce. */
+ crypto_rand(circ->cpath->prev->rend_circ_nonce, DIGEST_LEN);
+ /* Create a default HS identifier. */
+ circ->hs_ident = tor_malloc_zero(sizeof(hs_ident_circuit_t));
+
+ done:
+ return circ;
+}
+
+/* Helper: Return a newly allocated service object with the identity keypair
+ * sets and the current descriptor. Then register it to the global map.
+ * Caller should us hs_free_all() to free this service or remove it from the
+ * global map before freeing. */
+static hs_service_t *
+helper_create_service(void)
+{
+ /* Set a service for this circuit. */
+ hs_service_t *service = hs_service_new(get_options());
+ tt_assert(service);
+ service->config.version = HS_VERSION_THREE;
+ ed25519_secret_key_generate(&service->keys.identity_sk, 0);
+ ed25519_public_key_generate(&service->keys.identity_pk,
+ &service->keys.identity_sk);
+ service->desc_current = service_descriptor_new();
+ tt_assert(service->desc_current);
+ /* Register service to global map. */
+ int ret = register_service(get_hs_service_map(), service);
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ return service;
+}
+
+/* Helper: Return a newly allocated service intro point with two link
+ * specifiers, one IPv4 and one legacy ID set to As. */
+static hs_service_intro_point_t *
+helper_create_service_ip(void)
+{
+ hs_desc_link_specifier_t *ls;
+ hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0);
+ tt_assert(ip);
+ /* Add a first unused link specifier. */
+ ls = tor_malloc_zero(sizeof(*ls));
+ ls->type = LS_IPV4;
+ smartlist_add(ip->base.link_specifiers, ls);
+ /* Add a second link specifier used by a test. */
+ ls = tor_malloc_zero(sizeof(*ls));
+ ls->type = LS_LEGACY_ID;
+ memset(ls->u.legacy_id, 'A', sizeof(ls->u.legacy_id));
+ smartlist_add(ip->base.link_specifiers, ls);
+
+ done:
+ return ip;
+}
+
static void
test_load_keys(void *arg)
{
@@ -446,7 +290,7 @@ test_load_keys(void *arg)
tt_int_op(ret, OP_EQ, 0);
/* This one should now be registered into the v2 list. */
tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 0);
- tt_int_op(num_rend_services(), OP_EQ, 1);
+ tt_int_op(rend_num_services(), OP_EQ, 1);
/* v3 service. */
tor_asprintf(&conf, conf_fmt, hsdir_v3, HS_VERSION_THREE);
@@ -553,24 +397,939 @@ test_access_service(void *arg)
hs_free_all();
}
+/** Test that we can create intro point objects, index them and find them */
+static void
+test_service_intro_point(void *arg)
+{
+ hs_service_t *service = NULL;
+ hs_service_intro_point_t *ip = NULL;
+
+ (void) arg;
+
+ /* Test simple creation of an object. */
+ {
+ time_t now = time(NULL);
+ ip = helper_create_service_ip();
+ tt_assert(ip);
+ /* Make sure the authentication keypair is not zeroes. */
+ tt_int_op(tor_mem_is_zero((const char *) &ip->auth_key_kp,
+ sizeof(ed25519_keypair_t)), OP_EQ, 0);
+ /* The introduce2_max MUST be in that range. */
+ tt_u64_op(ip->introduce2_max, OP_GE,
+ INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS);
+ tt_u64_op(ip->introduce2_max, OP_LE,
+ INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS);
+ /* Time to expire MUST also be in that range. We add 5 seconds because
+ * there could be a gap between setting now and the time taken in
+ * service_intro_point_new. On ARM, it can be surprisingly slow... */
+ tt_u64_op(ip->time_to_expire, OP_GE,
+ now + INTRO_POINT_LIFETIME_MIN_SECONDS + 5);
+ tt_u64_op(ip->time_to_expire, OP_LE,
+ now + INTRO_POINT_LIFETIME_MAX_SECONDS + 5);
+ tt_assert(ip->replay_cache);
+ tt_assert(ip->base.link_specifiers);
+ /* By default, this is NOT a legacy object. */
+ tt_int_op(ip->base.is_only_legacy, OP_EQ, 0);
+ }
+
+ /* Test functions that uses a service intropoints map with that previously
+ * created object (non legacy). */
+ {
+ uint8_t garbage[DIGEST256_LEN] = {0};
+ hs_service_intro_point_t *query;
+
+ service = hs_service_new(get_options());
+ tt_assert(service);
+ service->desc_current = service_descriptor_new();
+ tt_assert(service->desc_current);
+ /* Add intropoint to descriptor map. */
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+ query = service_intro_point_find(service, &ip->auth_key_kp.pubkey);
+ tt_mem_op(query, OP_EQ, ip, sizeof(hs_service_intro_point_t));
+ query = service_intro_point_find(service,
+ (const ed25519_public_key_t *) garbage);
+ tt_assert(query == NULL);
+
+ /* While at it, can I find the descriptor with the intro point? */
+ hs_service_descriptor_t *desc_lookup =
+ service_desc_find_by_intro(service, ip);
+ tt_mem_op(service->desc_current, OP_EQ, desc_lookup,
+ sizeof(hs_service_descriptor_t));
+
+ /* Remove object from service descriptor and make sure it is out. */
+ service_intro_point_remove(service, ip);
+ query = service_intro_point_find(service, &ip->auth_key_kp.pubkey);
+ tt_assert(query == NULL);
+ }
+
+ done:
+ /* If the test succeed, this object is no longer referenced in the service
+ * so we can free it without use after free. Else, it might explode because
+ * it's still in the service descriptor map. */
+ service_intro_point_free(ip);
+ hs_service_free(service);
+}
+
+static node_t mock_node;
+static const node_t *
+mock_node_get_by_id(const char *digest)
+{
+ (void) digest;
+ memset(mock_node.identity, 'A', DIGEST_LEN);
+ /* Only return the matchin identity of As */
+ if (!tor_memcmp(mock_node.identity, digest, DIGEST_LEN)) {
+ return &mock_node;
+ }
+ return NULL;
+}
+
+static void
+test_helper_functions(void *arg)
+{
+ int ret;
+ hs_service_t *service = NULL;
+ hs_service_intro_point_t *ip = NULL;
+ hs_ident_circuit_t ident;
+
+ (void) arg;
+
+ MOCK(node_get_by_id, mock_node_get_by_id);
+
+ hs_service_init();
+
+ service = helper_create_service();
+
+ ip = helper_create_service_ip();
+ /* Immediately add the intro point to the service so the free service at the
+ * end cleans it as well. */
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+
+ /* Setup the circuit identifier. */
+ ed25519_pubkey_copy(&ident.intro_auth_pk, &ip->auth_key_kp.pubkey);
+ ed25519_pubkey_copy(&ident.identity_pk, &service->keys.identity_pk);
+
+ /* Testing get_objects_from_ident(). */
+ {
+ hs_service_t *s_lookup = NULL;
+ hs_service_intro_point_t *ip_lookup = NULL;
+ hs_service_descriptor_t *desc_lookup = NULL;
+
+ get_objects_from_ident(&ident, &s_lookup, &ip_lookup, &desc_lookup);
+ tt_mem_op(s_lookup, OP_EQ, service, sizeof(hs_service_t));
+ tt_mem_op(ip_lookup, OP_EQ, ip, sizeof(hs_service_intro_point_t));
+ tt_mem_op(desc_lookup, OP_EQ, service->desc_current,
+ sizeof(hs_service_descriptor_t));
+ /* Reset */
+ s_lookup = NULL; ip_lookup = NULL; desc_lookup = NULL;
+
+ /* NULL parameter should work. */
+ get_objects_from_ident(&ident, NULL, &ip_lookup, &desc_lookup);
+ tt_mem_op(ip_lookup, OP_EQ, ip, sizeof(hs_service_intro_point_t));
+ tt_mem_op(desc_lookup, OP_EQ, service->desc_current,
+ sizeof(hs_service_descriptor_t));
+ /* Reset. */
+ s_lookup = NULL; ip_lookup = NULL; desc_lookup = NULL;
+
+ /* Break the ident and we should find nothing. */
+ memset(&ident, 0, sizeof(ident));
+ get_objects_from_ident(&ident, &s_lookup, &ip_lookup, &desc_lookup);
+ tt_assert(s_lookup == NULL);
+ tt_assert(ip_lookup == NULL);
+ tt_assert(desc_lookup == NULL);
+ }
+
+ /* Testing get_node_from_intro_point() */
+ {
+ const node_t *node = get_node_from_intro_point(ip);
+ tt_ptr_op(node, OP_EQ, &mock_node);
+ SMARTLIST_FOREACH_BEGIN(ip->base.link_specifiers,
+ hs_desc_link_specifier_t *, ls) {
+ if (ls->type == LS_LEGACY_ID) {
+ /* Change legacy id in link specifier which is not the mock node. */
+ memset(ls->u.legacy_id, 'B', sizeof(ls->u.legacy_id));
+ }
+ } SMARTLIST_FOREACH_END(ls);
+ node = get_node_from_intro_point(ip);
+ tt_assert(node == NULL);
+ }
+
+ /* Testing can_service_launch_intro_circuit() */
+ {
+ time_t now = time(NULL);
+ /* Put the start of the retry period back in time, we should be allowed.
+ * to launch intro circuit. */
+ service->state.num_intro_circ_launched = 2;
+ service->state.intro_circ_retry_started_time =
+ (now - INTRO_CIRC_RETRY_PERIOD - 1);
+ ret = can_service_launch_intro_circuit(service, now);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_u64_op(service->state.intro_circ_retry_started_time, OP_EQ, now);
+ tt_u64_op(service->state.num_intro_circ_launched, OP_EQ, 0);
+ /* Call it again, we should still be allowed because we are under
+ * MAX_INTRO_CIRCS_PER_PERIOD which been set to 0 previously. */
+ ret = can_service_launch_intro_circuit(service, now);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_u64_op(service->state.intro_circ_retry_started_time, OP_EQ, now);
+ tt_u64_op(service->state.num_intro_circ_launched, OP_EQ, 0);
+ /* Too many intro circuit launched means we are not allowed. */
+ service->state.num_intro_circ_launched = 20;
+ ret = can_service_launch_intro_circuit(service, now);
+ tt_int_op(ret, OP_EQ, 0);
+ }
+
+ /* Testing intro_point_should_expire(). */
+ {
+ time_t now = time(NULL);
+ /* Just some basic test of the current state. */
+ tt_u64_op(ip->introduce2_max, OP_GE,
+ INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS);
+ tt_u64_op(ip->introduce2_max, OP_LE,
+ INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS);
+ tt_u64_op(ip->time_to_expire, OP_GE,
+ now + INTRO_POINT_LIFETIME_MIN_SECONDS);
+ tt_u64_op(ip->time_to_expire, OP_LE,
+ now + INTRO_POINT_LIFETIME_MAX_SECONDS);
+
+ /* This newly created IP from above shouldn't expire now. */
+ ret = intro_point_should_expire(ip, now);
+ tt_int_op(ret, OP_EQ, 0);
+ /* Maximum number of INTRODUCE2 cell reached, it should expire. */
+ ip->introduce2_count = INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS + 1;
+ ret = intro_point_should_expire(ip, now);
+ tt_int_op(ret, OP_EQ, 1);
+ ip->introduce2_count = 0;
+ /* It should expire if time to expire has been reached. */
+ ip->time_to_expire = now - 1000;
+ ret = intro_point_should_expire(ip, now);
+ tt_int_op(ret, OP_EQ, 1);
+ }
+
+ done:
+ /* This will free the service and all objects associated to it. */
+ hs_service_free_all();
+ UNMOCK(node_get_by_id);
+}
+
+/** Test that we do the right operations when an intro circuit opens */
+static void
+test_intro_circuit_opened(void *arg)
+{
+ int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
+ hs_service_t *service;
+ origin_circuit_t *circ = NULL;
+
+ (void) arg;
+
+ hs_init();
+ MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close);
+ MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge);
+
+ circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO,
+ flags);
+
+ /* No service associated with this circuit. */
+ setup_full_capture_of_logs(LOG_WARN);
+ hs_service_circuit_has_opened(circ);
+ expect_log_msg_containing("Unknown service identity key");
+ teardown_capture_of_logs();
+
+ /* Set a service for this circuit. */
+ {
+ service = helper_create_service();
+ ed25519_pubkey_copy(&circ->hs_ident->identity_pk,
+ &service->keys.identity_pk);
+
+ /* No intro point associated with this circuit. */
+ setup_full_capture_of_logs(LOG_WARN);
+ hs_service_circuit_has_opened(circ);
+ expect_log_msg_containing("Unknown introduction point auth key");
+ teardown_capture_of_logs();
+ }
+
+ /* Set an IP object now for this circuit. */
+ {
+ hs_service_intro_point_t *ip = helper_create_service_ip();
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+ /* Update ident to contain the intro point auth key. */
+ ed25519_pubkey_copy(&circ->hs_ident->intro_auth_pk,
+ &ip->auth_key_kp.pubkey);
+ }
+
+ /* This one should go all the way. */
+ setup_full_capture_of_logs(LOG_INFO);
+ hs_service_circuit_has_opened(circ);
+ expect_log_msg_containing("Introduction circuit 0 established for service");
+ teardown_capture_of_logs();
+
+ done:
+ circuit_free(TO_CIRCUIT(circ));
+ hs_free_all();
+ UNMOCK(circuit_mark_for_close_);
+ UNMOCK(relay_send_command_from_edge_);
+}
+
+/** Test the operations we do on a circuit after we learn that we successfuly
+ * established an intro point on it */
+static void
+test_intro_established(void *arg)
+{
+ int ret;
+ int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
+ uint8_t payload[RELAY_PAYLOAD_SIZE] = {0};
+ origin_circuit_t *circ = NULL;
+ hs_service_t *service;
+ hs_service_intro_point_t *ip = NULL;
+
+ (void) arg;
+
+ hs_init();
+ MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close);
+
+ circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO,
+ flags);
+ /* Test a wrong purpose. */
+ TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_S_INTRO;
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_service_receive_intro_established(circ, payload, sizeof(payload));
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("Received an INTRO_ESTABLISHED cell on a "
+ "non introduction circuit of purpose");
+ teardown_capture_of_logs();
+
+ /* Back to normal. */
+ TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_S_ESTABLISH_INTRO;
+
+ /* No service associated to it. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_service_receive_intro_established(circ, payload, sizeof(payload));
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("Unknown service identity key");
+ teardown_capture_of_logs();
+
+ /* Set a service for this circuit. */
+ service = helper_create_service();
+ ed25519_pubkey_copy(&circ->hs_ident->identity_pk,
+ &service->keys.identity_pk);
+ /* No introduction point associated to it. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_service_receive_intro_established(circ, payload, sizeof(payload));
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("Introduction circuit established without an "
+ "intro point object on circuit");
+ teardown_capture_of_logs();
+
+ /* Set an IP object now for this circuit. */
+ {
+ ip = helper_create_service_ip();
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+ /* Update ident to contain the intro point auth key. */
+ ed25519_pubkey_copy(&circ->hs_ident->intro_auth_pk,
+ &ip->auth_key_kp.pubkey);
+ }
+
+ /* Send an empty payload. INTRO_ESTABLISHED cells are basically zeroes. */
+ ret = hs_service_receive_intro_established(circ, payload, sizeof(payload));
+ tt_int_op(ret, OP_EQ, 0);
+ tt_u64_op(ip->circuit_established, OP_EQ, 1);
+ tt_int_op(TO_CIRCUIT(circ)->purpose, OP_EQ, CIRCUIT_PURPOSE_S_INTRO);
+
+ done:
+ circuit_free(TO_CIRCUIT(circ));
+ hs_free_all();
+ UNMOCK(circuit_mark_for_close_);
+}
+
+/** Check the operations we do on a rendezvous circuit after we learn it's
+ * open */
+static void
+test_rdv_circuit_opened(void *arg)
+{
+ int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
+ origin_circuit_t *circ = NULL;
+ hs_service_t *service;
+
+ (void) arg;
+
+ hs_init();
+ MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close);
+ MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge);
+
+ circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_S_CONNECT_REND, flags);
+ crypto_rand((char *) circ->hs_ident->rendezvous_cookie, REND_COOKIE_LEN);
+ crypto_rand((char *) circ->hs_ident->rendezvous_handshake_info,
+ sizeof(circ->hs_ident->rendezvous_handshake_info));
+
+ /* No service associated with this circuit. */
+ setup_full_capture_of_logs(LOG_WARN);
+ hs_service_circuit_has_opened(circ);
+ expect_log_msg_containing("Unknown service identity key");
+ teardown_capture_of_logs();
+ /* This should be set to a non zero timestamp. */
+ tt_u64_op(TO_CIRCUIT(circ)->timestamp_dirty, OP_NE, 0);
+
+ /* Set a service for this circuit. */
+ service = helper_create_service();
+ ed25519_pubkey_copy(&circ->hs_ident->identity_pk,
+ &service->keys.identity_pk);
+ /* Should be all good. */
+ hs_service_circuit_has_opened(circ);
+ tt_int_op(TO_CIRCUIT(circ)->purpose, OP_EQ, CIRCUIT_PURPOSE_S_REND_JOINED);
+
+ done:
+ circuit_free(TO_CIRCUIT(circ));
+ hs_free_all();
+ UNMOCK(circuit_mark_for_close_);
+ UNMOCK(relay_send_command_from_edge_);
+}
+
+/** Test sending and receiving introduce2 cells */
+static void
+test_introduce2(void *arg)
+{
+ int ret;
+ int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
+ uint8_t payload[RELAY_PAYLOAD_SIZE] = {0};
+ origin_circuit_t *circ = NULL;
+ hs_service_t *service;
+ hs_service_intro_point_t *ip = NULL;
+
+ (void) arg;
+
+ hs_init();
+ MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close);
+ MOCK(get_or_state,
+ get_or_state_replacement);
+
+ dummy_state = tor_malloc_zero(sizeof(or_state_t));
+
+ circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_S_INTRO, flags);
+
+ /* Test a wrong purpose. */
+ TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_S_ESTABLISH_INTRO;
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_service_receive_introduce2(circ, payload, sizeof(payload));
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("Received an INTRODUCE2 cell on a "
+ "non introduction circuit of purpose");
+ teardown_capture_of_logs();
+
+ /* Back to normal. */
+ TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_S_INTRO;
+
+ /* No service associated to it. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_service_receive_introduce2(circ, payload, sizeof(payload));
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("Unknown service identity key");
+ teardown_capture_of_logs();
+
+ /* Set a service for this circuit. */
+ service = helper_create_service();
+ ed25519_pubkey_copy(&circ->hs_ident->identity_pk,
+ &service->keys.identity_pk);
+ /* No introduction point associated to it. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_service_receive_introduce2(circ, payload, sizeof(payload));
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("Unknown introduction auth key when handling "
+ "an INTRODUCE2 cell on circuit");
+ teardown_capture_of_logs();
+
+ /* Set an IP object now for this circuit. */
+ {
+ ip = helper_create_service_ip();
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+ /* Update ident to contain the intro point auth key. */
+ ed25519_pubkey_copy(&circ->hs_ident->intro_auth_pk,
+ &ip->auth_key_kp.pubkey);
+ }
+
+ /* This will fail because receiving an INTRODUCE2 cell implies a valid cell
+ * and then launching circuits so let's not do that and instead test that
+ * behaviour differently. */
+ ret = hs_service_receive_introduce2(circ, payload, sizeof(payload));
+ tt_int_op(ret, OP_EQ, -1);
+ tt_u64_op(ip->introduce2_count, OP_EQ, 0);
+
+ done:
+ or_state_free(dummy_state);
+ dummy_state = NULL;
+ circuit_free(TO_CIRCUIT(circ));
+ hs_free_all();
+ UNMOCK(circuit_mark_for_close_);
+}
+
+/** Test basic hidden service housekeeping operations (maintaining intro
+ * points, etc) */
+static void
+test_service_event(void *arg)
+{
+ int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
+ time_t now = time(NULL);
+ hs_service_t *service;
+ origin_circuit_t *circ = NULL;
+
+ (void) arg;
+
+ hs_init();
+ MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close);
+
+ circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_S_INTRO, flags);
+
+ /* Set a service for this circuit. */
+ service = helper_create_service();
+ ed25519_pubkey_copy(&circ->hs_ident->identity_pk,
+ &service->keys.identity_pk);
+
+ /* Currently this consists of cleaning invalid intro points. So adding IPs
+ * here that should get cleaned up. */
+ {
+ hs_service_intro_point_t *ip = helper_create_service_ip();
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+ /* This run will remove the IP because we have no circuits nor node_t
+ * associated with it. */
+ run_housekeeping_event(now);
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 0);
+ /* We'll trigger a removal because we've reached our maximum amount of
+ * times we should retry a circuit. For this, we need to have a node_t
+ * that matches the identity of this IP. */
+ routerinfo_t ri;
+ ip = helper_create_service_ip();
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+ memset(ri.cache_info.identity_digest, 'A', DIGEST_LEN);
+ /* This triggers a node_t creation. */
+ tt_assert(nodelist_set_routerinfo(&ri, NULL));
+ ip->circuit_retries = MAX_INTRO_POINT_CIRCUIT_RETRIES + 1;
+ run_housekeeping_event(now);
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 0);
+ /* No removal but no circuit so this means the IP object will stay in the
+ * descriptor map so we can retry it. */
+ ip = helper_create_service_ip();
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+ ip->circuit_established = 1; /* We'll test that, it MUST be 0 after. */
+ run_housekeeping_event(now);
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 1);
+ /* Remove the IP object at once for the next test. */
+ ip->circuit_retries = MAX_INTRO_POINT_CIRCUIT_RETRIES + 1;
+ run_housekeeping_event(now);
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 0);
+ /* Now, we'll create an IP with a registered circuit. The IP object
+ * shouldn't go away. */
+ ip = helper_create_service_ip();
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+ ed25519_pubkey_copy(&circ->hs_ident->intro_auth_pk,
+ &ip->auth_key_kp.pubkey);
+ hs_circuitmap_register_intro_circ_v3_service_side(
+ circ, &ip->auth_key_kp.pubkey);
+ run_housekeeping_event(now);
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 1);
+ /* We'll mangle the IP object to expire. */
+ ip->time_to_expire = now;
+ run_housekeeping_event(now);
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 0);
+ }
+
+ done:
+ hs_circuitmap_remove_circuit(TO_CIRCUIT(circ));
+ circuit_free(TO_CIRCUIT(circ));
+ hs_free_all();
+ UNMOCK(circuit_mark_for_close_);
+}
+
+/** Test that we rotate descriptors correctly in overlap period */
+static void
+test_rotate_descriptors(void *arg)
+{
+ int ret;
+ time_t now = time(NULL);
+ hs_service_t *service;
+ hs_service_descriptor_t *desc_next;
+ hs_service_intro_point_t *ip;
+
+ (void) arg;
+
+ hs_init();
+ MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close);
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+
+ /* Setup the valid_after time to be 13:00 UTC, not in overlap period. The
+ * overlap check doesn't care about the year. */
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
+ &mock_ns.valid_after);
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
+ &mock_ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Create a service with a default descriptor and state. It's added to the
+ * global map. */
+ service = helper_create_service();
+ ip = helper_create_service_ip();
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+
+ /* Nothing should happen because we are not in the overlap period. */
+ rotate_all_descriptors(now);
+ tt_int_op(service->state.in_overlap_period, OP_EQ, 0);
+ tt_assert(service->desc_current);
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 1);
+
+ /* Entering an overlap period. */
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 01:00:00 UTC",
+ &mock_ns.valid_after);
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 02:00:00 UTC",
+ &mock_ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+ desc_next = service_descriptor_new();
+ desc_next->next_upload_time = 42; /* Our marker to recognize it. */
+ service->desc_next = desc_next;
+ /* We should have our state flagged to be in the overlap period, our current
+ * descriptor cleaned up and the next descriptor becoming the current. */
+ rotate_all_descriptors(now);
+ tt_int_op(service->state.in_overlap_period, OP_EQ, 1);
+ tt_mem_op(service->desc_current, OP_EQ, desc_next, sizeof(*desc_next));
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 0);
+ tt_assert(service->desc_next == NULL);
+ /* A second time should do nothing. */
+ rotate_all_descriptors(now);
+ tt_int_op(service->state.in_overlap_period, OP_EQ, 1);
+ tt_mem_op(service->desc_current, OP_EQ, desc_next, sizeof(*desc_next));
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 0);
+ tt_assert(service->desc_next == NULL);
+
+ /* Going out of the overlap period. */
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 12:00:00 UTC",
+ &mock_ns.valid_after);
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
+ &mock_ns.fresh_until);
+ /* This should reset the state and not touch the current descriptor. */
+ tt_int_op(ret, OP_EQ, 0);
+ rotate_all_descriptors(now);
+ tt_int_op(service->state.in_overlap_period, OP_EQ, 0);
+ tt_mem_op(service->desc_current, OP_EQ, desc_next, sizeof(*desc_next));
+ tt_assert(service->desc_next == NULL);
+
+ done:
+ hs_free_all();
+ UNMOCK(circuit_mark_for_close_);
+ UNMOCK(networkstatus_get_live_consensus);
+}
+
+/** Test building descriptors: picking intro points, setting up their link
+ * specifiers, etc. */
+static void
+test_build_update_descriptors(void *arg)
+{
+ int ret;
+ time_t now = time(NULL);
+ time_t period_num = hs_get_time_period_num(now);
+ time_t next_period_num = hs_get_next_time_period_num(now);
+ node_t *node;
+ hs_service_t *service;
+ hs_service_intro_point_t *ip_cur, *ip_next;
+
+ (void) arg;
+
+ hs_init();
+ MOCK(hs_overlap_mode_is_active, mock_hs_overlap_mode_is_active_true);
+ MOCK(get_or_state,
+ get_or_state_replacement);
+
+ dummy_state = tor_malloc_zero(sizeof(or_state_t));
+
+ /* Create a service without a current descriptor to trigger a build. */
+ service = hs_service_new(get_options());
+ tt_assert(service);
+ service->config.version = HS_VERSION_THREE;
+ ed25519_secret_key_generate(&service->keys.identity_sk, 0);
+ ed25519_public_key_generate(&service->keys.identity_pk,
+ &service->keys.identity_sk);
+ /* Register service to global map. */
+ ret = register_service(get_hs_service_map(), service);
+ tt_int_op(ret, OP_EQ, 0);
+
+ build_all_descriptors(now);
+ /* Check *current* descriptor. */
+ tt_assert(service->desc_current);
+ tt_assert(service->desc_current->desc);
+ tt_assert(service->desc_current->intro_points.map);
+ tt_u64_op(service->desc_current->time_period_num, OP_EQ, period_num);
+ /* This should be untouched, the update descriptor process changes it. */
+ tt_u64_op(service->desc_current->next_upload_time, OP_EQ, 0);
+
+ /* Check *next* descriptor. */
+ tt_assert(service->desc_next);
+ tt_assert(service->desc_next->desc);
+ tt_assert(service->desc_next->intro_points.map);
+ tt_assert(service->desc_current != service->desc_next);
+ tt_u64_op(service->desc_next->time_period_num, OP_EQ, next_period_num);
+ /* This should be untouched, the update descriptor process changes it. */
+ tt_u64_op(service->desc_next->next_upload_time, OP_EQ, 0);
+
+ /* Time to test the update of those descriptors. At first, we have no node
+ * in the routerlist so this will find NO suitable node for the IPs. */
+ setup_full_capture_of_logs(LOG_INFO);
+ update_all_descriptors(now);
+ expect_log_msg_containing("Unable to find a suitable node to be an "
+ "introduction point for service");
+ teardown_capture_of_logs();
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 0);
+ tt_int_op(digest256map_size(service->desc_next->intro_points.map),
+ OP_EQ, 0);
+
+ /* Now, we'll setup a node_t. */
+ {
+ routerinfo_t ri;
+ tor_addr_t ipv4_addr;
+ curve25519_secret_key_t curve25519_secret_key;
+
+ memset(&ri, 0, sizeof(routerinfo_t));
+
+ tor_addr_parse(&ipv4_addr, "127.0.0.1");
+ ri.addr = tor_addr_to_ipv4h(&ipv4_addr);
+ ri.or_port = 1337;
+ ri.purpose = ROUTER_PURPOSE_GENERAL;
+ /* Ugly yes but we never free the "ri" object so this just makes things
+ * easier. */
+ ri.protocol_list = (char *) "HSDir=1-2 LinkAuth=3";
+ ret = curve25519_secret_key_generate(&curve25519_secret_key, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ ri.onion_curve25519_pkey =
+ tor_malloc_zero(sizeof(curve25519_public_key_t));
+ ri.onion_pkey = crypto_pk_new();
+ curve25519_public_key_generate(ri.onion_curve25519_pkey,
+ &curve25519_secret_key);
+ memset(ri.cache_info.identity_digest, 'A', DIGEST_LEN);
+ /* Setup ed25519 identity */
+ ed25519_keypair_t kp1;
+ ed25519_keypair_generate(&kp1, 0);
+ ri.cache_info.signing_key_cert = tor_malloc_zero(sizeof(tor_cert_t));
+ tt_assert(ri.cache_info.signing_key_cert);
+ ed25519_pubkey_copy(&ri.cache_info.signing_key_cert->signing_key,
+ &kp1.pubkey);
+ nodelist_set_routerinfo(&ri, NULL);
+ node = node_get_mutable_by_id(ri.cache_info.identity_digest);
+ tt_assert(node);
+ node->is_running = node->is_valid = node->is_fast = node->is_stable = 1;
+ }
+
+ /* We expect to pick only one intro point from the node above. */
+ setup_full_capture_of_logs(LOG_INFO);
+ update_all_descriptors(now);
+ tor_free(node->ri->onion_curve25519_pkey); /* Avoid memleak. */
+ tor_free(node->ri->cache_info.signing_key_cert);
+ crypto_pk_free(node->ri->onion_pkey);
+ expect_log_msg_containing("just picked 1 intro points and wanted 3. It "
+ "currently has 0 intro points. Launching "
+ "ESTABLISH_INTRO circuit shortly.");
+ teardown_capture_of_logs();
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 1);
+ tt_int_op(digest256map_size(service->desc_next->intro_points.map),
+ OP_EQ, 1);
+ /* Get the IP object. Because we don't have the auth key of the IP, we can't
+ * query it so get the first element in the map. */
+ {
+ void *obj = NULL;
+ const uint8_t *key;
+ digest256map_iter_t *iter =
+ digest256map_iter_init(service->desc_current->intro_points.map);
+ digest256map_iter_get(iter, &key, &obj);
+ tt_assert(obj);
+ ip_cur = obj;
+ /* Get also the IP from the next descriptor. We'll make sure it's not the
+ * same object as in the current descriptor. */
+ iter = digest256map_iter_init(service->desc_next->intro_points.map);
+ digest256map_iter_get(iter, &key, &obj);
+ tt_assert(obj);
+ ip_next = obj;
+ }
+ tt_mem_op(ip_cur, OP_NE, ip_next, sizeof(hs_desc_intro_point_t));
+
+ /* We won't test the service IP object because there is a specific test
+ * already for this but we'll make sure that the state is coherent.*/
+
+ /* Three link specifiers are mandatoy so make sure we do have them. */
+ tt_int_op(smartlist_len(ip_cur->base.link_specifiers), OP_EQ, 3);
+ /* Make sure we have a valid encryption keypair generated when we pick an
+ * intro point in the update process. */
+ tt_assert(!tor_mem_is_zero((char *) ip_cur->enc_key_kp.seckey.secret_key,
+ CURVE25519_SECKEY_LEN));
+ tt_assert(!tor_mem_is_zero((char *) ip_cur->enc_key_kp.pubkey.public_key,
+ CURVE25519_PUBKEY_LEN));
+ tt_u64_op(ip_cur->time_to_expire, OP_GE, now +
+ INTRO_POINT_LIFETIME_MIN_SECONDS);
+ tt_u64_op(ip_cur->time_to_expire, OP_LE, now +
+ INTRO_POINT_LIFETIME_MAX_SECONDS);
+
+ done:
+ hs_free_all();
+ nodelist_free_all();
+ UNMOCK(hs_overlap_mode_is_active);
+}
+
+static void
+test_upload_descriptors(void *arg)
+{
+ int ret;
+ time_t now = time(NULL);
+ hs_service_t *service;
+ hs_service_intro_point_t *ip;
+
+ (void) arg;
+
+ hs_init();
+ MOCK(hs_overlap_mode_is_active, mock_hs_overlap_mode_is_active_true);
+ MOCK(get_or_state,
+ get_or_state_replacement);
+
+ dummy_state = tor_malloc_zero(sizeof(or_state_t));
+
+ /* Create a service with no descriptor. It's added to the global map. */
+ service = hs_service_new(get_options());
+ tt_assert(service);
+ service->config.version = HS_VERSION_THREE;
+ ed25519_secret_key_generate(&service->keys.identity_sk, 0);
+ ed25519_public_key_generate(&service->keys.identity_pk,
+ &service->keys.identity_sk);
+ /* Register service to global map. */
+ ret = register_service(get_hs_service_map(), service);
+ tt_int_op(ret, OP_EQ, 0);
+ /* But first, build our descriptor. */
+ build_all_descriptors(now);
+
+ /* Nothing should happen because we have 0 introduction circuit established
+ * and we want (by default) 3 intro points. */
+ run_upload_descriptor_event(now);
+ /* If no upload happened, this should be untouched. */
+ tt_u64_op(service->desc_current->next_upload_time, OP_EQ, 0);
+ /* We'll simulate that we've opened our intro point circuit and that we only
+ * want one intro point. */
+ service->config.num_intro_points = 1;
+
+ /* Set our next upload time after now which will skip the upload. */
+ service->desc_current->next_upload_time = now + 1000;
+ run_upload_descriptor_event(now);
+ /* If no upload happened, this should be untouched. */
+ tt_u64_op(service->desc_current->next_upload_time, OP_EQ, now + 1000);
+
+ /* Set our upload time in the past so we trigger an upload. */
+ service->desc_current->next_upload_time = now - 1000;
+ service->desc_next->next_upload_time = now - 1000;
+ ip = helper_create_service_ip();
+ ip->circuit_established = 1;
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+
+ setup_full_capture_of_logs(LOG_WARN);
+ run_upload_descriptor_event(now);
+ expect_log_msg_containing("No valid consensus so we can't get the");
+ teardown_capture_of_logs();
+ tt_u64_op(service->desc_current->next_upload_time, OP_GE,
+ now + HS_SERVICE_NEXT_UPLOAD_TIME_MIN);
+ tt_u64_op(service->desc_current->next_upload_time, OP_LE,
+ now + HS_SERVICE_NEXT_UPLOAD_TIME_MAX);
+
+ done:
+ hs_free_all();
+ UNMOCK(hs_overlap_mode_is_active);
+}
+
+/** Test the functions that save and load HS revision counters to state. */
+static void
+test_revision_counter_state(void *arg)
+{
+ char *state_line_one = NULL;
+ char *state_line_two = NULL;
+
+ hs_service_descriptor_t *desc_one = service_descriptor_new();
+ hs_service_descriptor_t *desc_two = service_descriptor_new();
+
+ (void) arg;
+
+ /* Prepare both descriptors */
+ desc_one->desc->plaintext_data.revision_counter = 42;
+ desc_two->desc->plaintext_data.revision_counter = 240;
+ memset(&desc_one->blinded_kp.pubkey.pubkey, '\x42',
+ sizeof(desc_one->blinded_kp.pubkey.pubkey));
+ memset(&desc_two->blinded_kp.pubkey.pubkey, '\xf0',
+ sizeof(desc_one->blinded_kp.pubkey.pubkey));
+
+ /* Turn the descriptor rev counters into state lines */
+ state_line_one = encode_desc_rev_counter_for_state(desc_one);
+ tt_str_op(state_line_one, OP_EQ,
+ "QkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkI 42");
+
+ state_line_two = encode_desc_rev_counter_for_state(desc_two);
+ tt_str_op(state_line_two, OP_EQ,
+ "8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PA 240");
+
+ /* Now let's test our state parsing function: */
+ int service_found;
+ uint64_t cached_rev_counter;
+
+ /* First's try with wrong pubkey and check that no service was found */
+ cached_rev_counter =check_state_line_for_service_rev_counter(state_line_one,
+ &desc_two->blinded_kp.pubkey,
+ &service_found);
+ tt_int_op(service_found, OP_EQ, 0);
+
+ /* Now let's try with the right pubkeys */
+ cached_rev_counter =check_state_line_for_service_rev_counter(state_line_one,
+ &desc_one->blinded_kp.pubkey,
+ &service_found);
+ tt_int_op(service_found, OP_EQ, 1);
+ tt_int_op(cached_rev_counter, OP_EQ, 42);
+
+ cached_rev_counter =check_state_line_for_service_rev_counter(state_line_two,
+ &desc_two->blinded_kp.pubkey,
+ &service_found);
+ tt_int_op(service_found, OP_EQ, 1);
+ tt_int_op(cached_rev_counter, OP_EQ, 240);
+
+ done:
+ tor_free(state_line_one);
+ tor_free(state_line_two);
+ service_descriptor_free(desc_one);
+ service_descriptor_free(desc_two);
+}
+
struct testcase_t hs_service_tests[] = {
- { "gen_establish_intro_cell", test_gen_establish_intro_cell, TT_FORK,
+ { "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, TT_FORK,
NULL, NULL },
- { "gen_establish_intro_cell_bad", test_gen_establish_intro_cell_bad, TT_FORK,
+ { "load_keys", test_load_keys, TT_FORK,
NULL, NULL },
- { "hs_ntor", test_hs_ntor, TT_FORK,
+ { "access_service", test_access_service, TT_FORK,
NULL, NULL },
- { "time_period", test_time_period, TT_FORK,
+ { "service_intro_point", test_service_intro_point, TT_FORK,
NULL, NULL },
- { "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, TT_FORK,
+ { "helper_functions", test_helper_functions, TT_FORK,
NULL, NULL },
- { "build_address", test_build_address, TT_FORK,
+ { "intro_circuit_opened", test_intro_circuit_opened, TT_FORK,
NULL, NULL },
- { "validate_address", test_validate_address, TT_FORK,
+ { "intro_established", test_intro_established, TT_FORK,
NULL, NULL },
- { "load_keys", test_load_keys, TT_FORK,
+ { "rdv_circuit_opened", test_rdv_circuit_opened, TT_FORK,
NULL, NULL },
- { "access_service", test_access_service, TT_FORK,
+ { "introduce2", test_introduce2, TT_FORK,
+ NULL, NULL },
+ { "service_event", test_service_event, TT_FORK,
+ NULL, NULL },
+ { "rotate_descriptors", test_rotate_descriptors, TT_FORK,
+ NULL, NULL },
+ { "build_update_descriptors", test_build_update_descriptors, TT_FORK,
+ NULL, NULL },
+ { "upload_descriptors", test_upload_descriptors, TT_FORK,
+ NULL, NULL },
+ { "revision_counter_state", test_revision_counter_state, TT_FORK,
NULL, NULL },
END_OF_TESTCASES
diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c
index 026a0f3825..bee0ea0a32 100644
--- a/src/test/test_shared_random.c
+++ b/src/test/test_shared_random.c
@@ -189,6 +189,120 @@ test_get_state_valid_until_time(void *arg)
;
}
+/** Test the function that calculates the start time of the current SRV
+ * protocol run. */
+static void
+test_get_start_time_of_current_run(void *arg)
+{
+ int retval;
+ char tbuf[ISO_TIME_LEN + 1];
+ time_t current_time, run_start_time;
+
+ (void) arg;
+
+ {
+ /* Get start time if called at 00:00:01 */
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:01 UTC",
+ &current_time);
+ tt_int_op(retval, ==, 0);
+ run_start_time =
+ sr_state_get_start_time_of_current_protocol_run(current_time);
+
+ /* Compare it with the correct result */
+ format_iso_time(tbuf, run_start_time);
+ tt_str_op("2015-04-20 00:00:00", OP_EQ, tbuf);
+ }
+
+ {
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 23:59:59 UTC",
+ &current_time);
+ tt_int_op(retval, ==, 0);
+ run_start_time =
+ sr_state_get_start_time_of_current_protocol_run(current_time);
+
+ /* Compare it with the correct result */
+ format_iso_time(tbuf, run_start_time);
+ tt_str_op("2015-04-20 00:00:00", OP_EQ, tbuf);
+ }
+
+ {
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC",
+ &current_time);
+ tt_int_op(retval, ==, 0);
+ run_start_time =
+ sr_state_get_start_time_of_current_protocol_run(current_time);
+
+ /* Compare it with the correct result */
+ format_iso_time(tbuf, run_start_time);
+ tt_str_op("2015-04-20 00:00:00", OP_EQ, tbuf);
+ }
+
+ /* Now let's alter the voting schedule and check the correctness of the
+ * function. Voting interval of 10 seconds, means that an SRV protocol run
+ * takes 10 seconds * 24 rounds = 4 mins */
+ {
+ or_options_t *options = get_options_mutable();
+ options->V3AuthVotingInterval = 10;
+ options->TestingV3AuthInitialVotingInterval = 10;
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:15:32 UTC",
+ &current_time);
+
+ tt_int_op(retval, ==, 0);
+ run_start_time =
+ sr_state_get_start_time_of_current_protocol_run(current_time);
+
+ /* Compare it with the correct result */
+ format_iso_time(tbuf, run_start_time);
+ tt_str_op("2015-04-20 00:12:00", OP_EQ, tbuf);
+ }
+
+ done:
+ ;
+}
+
+/** Do some rudimentary consistency checks between the functions that
+ * understand the shared random protocol schedule */
+static void
+test_get_start_time_functions(void *arg)
+{
+ (void) arg;
+ time_t now = approx_time();
+
+ time_t start_time_of_protocol_run =
+ sr_state_get_start_time_of_current_protocol_run(now);
+ tt_assert(start_time_of_protocol_run);
+
+ /* Check that the round start time of the beginning of the run, is itself */
+ tt_int_op(get_start_time_of_current_round(start_time_of_protocol_run), OP_EQ,
+ start_time_of_protocol_run);
+
+ /* Check that even if we increment the start time, we still get the start
+ time of the run as the beginning of the round. */
+ tt_int_op(get_start_time_of_current_round(start_time_of_protocol_run+1),
+ OP_EQ, start_time_of_protocol_run);
+
+ done: ;
+}
+
+static void
+test_get_sr_protocol_duration(void *arg)
+{
+ (void) arg;
+
+ /* Check that by default an SR phase is 12 hours */
+ tt_int_op(sr_state_get_phase_duration(), ==, 12*60*60);
+ tt_int_op(sr_state_get_protocol_run_duration(), ==, 24*60*60);
+
+ /* Now alter the voting interval and check that the SR phase is 2 mins long
+ * if voting happens every 10 seconds (10*12 seconds = 2 mins) */
+ or_options_t *options = get_options_mutable();
+ options->V3AuthVotingInterval = 10;
+ tt_int_op(sr_state_get_phase_duration(), ==, 2*60);
+ tt_int_op(sr_state_get_protocol_run_duration(), ==, 4*60);
+
+ done: ;
+}
+
/* Mock function to immediately return our local 'mock_consensus'. */
static networkstatus_t *
mock_networkstatus_get_live_consensus(time_t now)
@@ -1272,6 +1386,12 @@ struct testcase_t sr_tests[] = {
NULL, NULL },
{ "get_next_valid_after_time", test_get_next_valid_after_time, TT_FORK,
NULL, NULL },
+ { "get_start_time_of_current_run", test_get_start_time_of_current_run,
+ TT_FORK, NULL, NULL },
+ { "get_start_time_functions", test_get_start_time_functions,
+ TT_FORK, NULL, NULL },
+ { "get_sr_protocol_duration", test_get_sr_protocol_duration, TT_FORK,
+ NULL, NULL },
{ "get_state_valid_until_time", test_get_state_valid_until_time, TT_FORK,
NULL, NULL },
{ "vote", test_vote, TT_FORK,