Merge branch 'maint-0.4.5' into release-0.4.5
[tor.git] / src / test / test_hs_ob.c
blob7f40187b5fe7d02b40a365eed32baad3c71324ff
1 /* Copyright (c) 2020, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 /**
5 * \file test_hs_ob.c
6 * \brief Test hidden service onion balance functionality.
7 */
9 #define CONFIG_PRIVATE
10 #define HS_SERVICE_PRIVATE
11 #define HS_OB_PRIVATE
13 #include "test/test.h"
14 #include "test/test_helpers.h"
15 #include "test/log_test_helpers.h"
17 #include "app/config/config.h"
18 #include "feature/hs/hs_config.h"
19 #include "feature/hs/hs_ob.h"
20 #include "feature/hs/hs_service.h"
21 #include "feature/nodelist/networkstatus.h"
22 #include "feature/nodelist/networkstatus_st.h"
24 static ed25519_keypair_t onion_addr_kp_1;
25 static char onion_addr_1[HS_SERVICE_ADDR_LEN_BASE32 + 1];
27 static ed25519_keypair_t onion_addr_kp_2;
28 static char onion_addr_2[HS_SERVICE_ADDR_LEN_BASE32 + 1];
30 static bool config_is_good = true;
32 static int
33 helper_tor_config(const char *conf)
35 int ret = -1;
36 or_options_t *options = helper_parse_options(conf);
37 tt_assert(options);
38 ret = hs_config_service_all(options, 0);
39 done:
40 or_options_free(options);
41 return ret;
44 static networkstatus_t mock_ns;
46 static networkstatus_t *
47 mock_networkstatus_get_live_consensus(time_t now)
49 (void) now;
50 return &mock_ns;
53 static char *
54 mock_read_file_to_str(const char *filename, int flags, struct stat *stat_out)
56 char *ret = NULL;
58 (void) flags;
59 (void) stat_out;
61 if (!strcmp(filename, get_fname("hs3" PATH_SEPARATOR "ob_config"))) {
62 if (config_is_good) {
63 tor_asprintf(&ret, "MasterOnionAddress %s.onion\n"
64 "MasterOnionAddress %s.onion\n",
65 onion_addr_1, onion_addr_2);
66 } else {
67 tor_asprintf(&ret, "MasterOnionAddress JUNKJUNKJUNK.onion\n"
68 "UnknownOption BLAH\n");
70 goto done;
73 done:
74 return ret;
77 static void
78 test_parse_config_file(void *arg)
80 int ret;
81 char *conf = NULL;
82 const ed25519_public_key_t *pkey;
84 (void) arg;
86 hs_init();
88 MOCK(read_file_to_str, mock_read_file_to_str);
90 #define fmt_conf \
91 "HiddenServiceDir %s\n" \
92 "HiddenServicePort 22\n" \
93 "HiddenServiceOnionBalanceInstance 1\n"
94 tor_asprintf(&conf, fmt_conf, get_fname("hs3"));
95 #undef fmt_conf
97 /* Build the OB frontend onion addresses. */
98 ed25519_keypair_generate(&onion_addr_kp_1, 0);
99 hs_build_address(&onion_addr_kp_1.pubkey, HS_VERSION_THREE, onion_addr_1);
100 ed25519_keypair_generate(&onion_addr_kp_2, 0);
101 hs_build_address(&onion_addr_kp_2.pubkey, HS_VERSION_THREE, onion_addr_2);
103 ret = helper_tor_config(conf);
104 tor_free(conf);
105 tt_int_op(ret, OP_EQ, 0);
107 /* Load the keys for the service. After that, the v3 service should be
108 * registered in the global map and we'll be able to access it. */
109 tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1);
110 hs_service_load_all_keys();
111 tt_int_op(get_hs_service_map_size(), OP_EQ, 1);
112 const hs_service_t *s = get_first_service();
113 tt_assert(s);
114 tt_assert(s->config.ob_master_pubkeys);
115 tt_assert(hs_ob_service_is_instance(s));
116 tt_assert(smartlist_len(s->config.ob_master_pubkeys) == 2);
118 /* Test the public keys we've added. */
119 pkey = smartlist_get(s->config.ob_master_pubkeys, 0);
120 tt_mem_op(&onion_addr_kp_1.pubkey, OP_EQ, pkey, ED25519_PUBKEY_LEN);
121 pkey = smartlist_get(s->config.ob_master_pubkeys, 1);
122 tt_mem_op(&onion_addr_kp_2.pubkey, OP_EQ, pkey, ED25519_PUBKEY_LEN);
124 done:
125 hs_free_all();
127 UNMOCK(read_file_to_str);
130 static void
131 test_parse_config_file_bad(void *arg)
133 int ret;
134 char *conf = NULL;
136 (void) arg;
138 hs_init();
140 MOCK(read_file_to_str, mock_read_file_to_str);
142 /* Indicate mock_read_file_to_str() to use the bad config. */
143 config_is_good = false;
145 #define fmt_conf \
146 "HiddenServiceDir %s\n" \
147 "HiddenServicePort 22\n" \
148 "HiddenServiceOnionBalanceInstance 1\n"
149 tor_asprintf(&conf, fmt_conf, get_fname("hs3"));
150 #undef fmt_conf
152 setup_full_capture_of_logs(LOG_INFO);
153 ret = helper_tor_config(conf);
154 tor_free(conf);
155 tt_int_op(ret, OP_EQ, -1);
156 expect_log_msg_containing("OnionBalance: MasterOnionAddress "
157 "JUNKJUNKJUNK.onion is invalid");
158 expect_log_msg_containing("Found unrecognized option \'UnknownOption\'; "
159 "saving it.");
160 teardown_capture_of_logs();
162 done:
163 hs_free_all();
165 UNMOCK(read_file_to_str);
168 static void
169 test_get_subcredentials(void *arg)
171 int ret;
172 hs_service_t *service = NULL;
173 hs_service_config_t config;
174 hs_subcredential_t *subcreds = NULL;
176 (void) arg;
178 MOCK(networkstatus_get_live_consensus,
179 mock_networkstatus_get_live_consensus);
181 /* Setup consensus with proper time so we can compute the time period. */
182 ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
183 &mock_ns.valid_after);
184 tt_int_op(ret, OP_EQ, 0);
185 ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
186 &mock_ns.fresh_until);
187 tt_int_op(ret, OP_EQ, 0);
189 config.ob_master_pubkeys = smartlist_new();
190 tt_assert(config.ob_master_pubkeys);
192 /* Set up an instance */
193 service = tor_malloc_zero(sizeof(hs_service_t));
194 service->config = config;
195 /* Setup the service descriptors */
196 service->desc_current = service_descriptor_new();
197 service->desc_next = service_descriptor_new();
199 /* First try to compute subcredentials but with no OB keys. Make sure that
200 * subcreds get NULLed. To do this check we first poison subcreds. */
201 subcreds = (void*)999;
202 tt_ptr_op(subcreds, OP_NE, NULL);
203 size_t num = compute_subcredentials(service, &subcreds);
204 tt_ptr_op(subcreds, OP_EQ, NULL);
206 /* Generate a keypair to add to the OB keys list. */
207 ed25519_keypair_generate(&onion_addr_kp_1, 0);
208 smartlist_add(config.ob_master_pubkeys, &onion_addr_kp_1.pubkey);
210 /* Set up the instance subcredentials */
211 char current_subcred[SUBCRED_LEN];
212 char next_subcred[SUBCRED_LEN];
213 memset(current_subcred, 'C', SUBCRED_LEN);
214 memset(next_subcred, 'N', SUBCRED_LEN);
215 memcpy(service->desc_current->desc->subcredential.subcred, current_subcred,
216 SUBCRED_LEN);
217 memcpy(service->desc_next->desc->subcredential.subcred, next_subcred,
218 SUBCRED_LEN);
220 /* See that subcreds are computed properly */
221 num = compute_subcredentials(service, &subcreds);
222 /* 5 subcredentials: 3 for the frontend, 2 for the instance */
223 tt_uint_op(num, OP_EQ, 5);
224 tt_ptr_op(subcreds, OP_NE, NULL);
226 /* Validate the subcredentials we just got. We'll build them oursevles with
227 * the right time period steps and compare. */
228 const uint64_t tp = hs_get_time_period_num(0);
229 const int steps[3] = {0, -1, 1};
231 unsigned int i;
232 for (i = 0; i < 3; i++) {
233 hs_subcredential_t subcredential;
234 ed25519_public_key_t blinded_pubkey;
235 hs_build_blinded_pubkey(&onion_addr_kp_1.pubkey, NULL, 0, tp + steps[i],
236 &blinded_pubkey);
237 hs_get_subcredential(&onion_addr_kp_1.pubkey, &blinded_pubkey,
238 &subcredential);
239 tt_mem_op(subcreds[i].subcred, OP_EQ, subcredential.subcred,
240 SUBCRED_LEN);
243 tt_mem_op(subcreds[i++].subcred, OP_EQ, current_subcred, SUBCRED_LEN);
244 tt_mem_op(subcreds[i++].subcred, OP_EQ, next_subcred, SUBCRED_LEN);
246 done:
247 tor_free(subcreds);
249 smartlist_free(config.ob_master_pubkeys);
250 if (service) {
251 memset(&service->config, 0, sizeof(hs_service_config_t));
252 hs_service_free(service);
255 UNMOCK(networkstatus_get_live_consensus);
258 struct testcase_t hs_ob_tests[] = {
259 { "parse_config_file", test_parse_config_file, TT_FORK,
260 NULL, NULL },
261 { "parse_config_file_bad", test_parse_config_file_bad, TT_FORK,
262 NULL, NULL },
264 { "get_subcredentials", test_get_subcredentials, TT_FORK,
265 NULL, NULL },
267 END_OF_TESTCASES