Remove unused function: dns_randfn_() in dns.c.
[tor.git] / src / test / test_hs_client.c
blob53ee3c53d2553182c42ffe70ce1c0b421fbf1b64
1 /* Copyright (c) 2016-2019, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 /**
5 * \file test_hs_client.c
6 * \brief Test prop224 HS client functionality.
7 */
9 #define CONFIG_PRIVATE
10 #define CRYPTO_PRIVATE
11 #define MAINLOOP_PRIVATE
12 #define HS_CLIENT_PRIVATE
13 #define TOR_CHANNEL_INTERNAL_
14 #define CIRCUITBUILD_PRIVATE
15 #define CIRCUITLIST_PRIVATE
16 #define CONNECTION_PRIVATE
18 #include "test/test.h"
19 #include "test/test_helpers.h"
20 #include "test/log_test_helpers.h"
21 #include "test/rend_test_helpers.h"
22 #include "test/hs_test_helpers.h"
24 #include "app/config/config.h"
25 #include "lib/crypt_ops/crypto_cipher.h"
26 #include "lib/crypt_ops/crypto_dh.h"
27 #include "core/or/channeltls.h"
28 #include "feature/dircommon/directory.h"
29 #include "core/mainloop/mainloop.h"
30 #include "feature/nodelist/nodelist.h"
31 #include "feature/nodelist/routerset.h"
33 #include "feature/hs/hs_circuit.h"
34 #include "feature/hs/hs_circuitmap.h"
35 #include "feature/hs/hs_client.h"
36 #include "feature/hs/hs_config.h"
37 #include "feature/hs/hs_ident.h"
38 #include "feature/hs/hs_cache.h"
39 #include "core/or/circuitlist.h"
40 #include "core/or/circuitbuild.h"
41 #include "core/mainloop/connection.h"
42 #include "core/or/connection_edge.h"
43 #include "feature/nodelist/networkstatus.h"
45 #include "core/or/cpath_build_state_st.h"
46 #include "core/or/crypt_path_st.h"
47 #include "feature/dircommon/dir_connection_st.h"
48 #include "core/or/entry_connection_st.h"
49 #include "core/or/extend_info_st.h"
50 #include "feature/nodelist/networkstatus_st.h"
51 #include "core/or/origin_circuit_st.h"
52 #include "core/or/socks_request_st.h"
54 static int
55 mock_connection_ap_handshake_send_begin(entry_connection_t *ap_conn)
57 (void) ap_conn;
58 return 0;
61 static networkstatus_t mock_ns;
63 /* Always return NULL. */
64 static networkstatus_t *
65 mock_networkstatus_get_reasonably_live_consensus_false(time_t now, int flavor)
67 (void) now;
68 (void) flavor;
69 return NULL;
72 static networkstatus_t *
73 mock_networkstatus_get_reasonably_live_consensus(time_t now, int flavor)
75 (void) now;
76 (void) flavor;
77 return &mock_ns;
80 static int
81 helper_config_client(const char *conf, int validate_only)
83 int ret = 0;
84 or_options_t *options = NULL;
85 tt_assert(conf);
86 options = helper_parse_options(conf);
87 tt_assert(options);
88 ret = hs_config_client_auth_all(options, validate_only);
89 done:
90 or_options_free(options);
91 return ret;
94 /* Test helper function: Setup a circuit and a stream with the same hidden
95 * service destination, and put them in <b>circ_out</b> and
96 * <b>conn_out</b>. Make the stream wait for circuits to be established to the
97 * hidden service. */
98 static int
99 helper_get_circ_and_stream_for_test(origin_circuit_t **circ_out,
100 connection_t **conn_out,
101 int is_legacy)
103 int retval;
104 channel_tls_t *n_chan=NULL;
105 rend_data_t *conn_rend_data = NULL;
106 origin_circuit_t *or_circ = NULL;
107 connection_t *conn = NULL;
108 ed25519_public_key_t service_pk;
110 /* Make a dummy connection stream and make it wait for our circuit */
111 conn = test_conn_get_connection(AP_CONN_STATE_CIRCUIT_WAIT,
112 CONN_TYPE_AP /* ??? */,
114 if (is_legacy) {
115 /* Legacy: Setup rend_data of stream */
116 char service_id[REND_SERVICE_ID_LEN_BASE32+1] = {0};
117 TO_EDGE_CONN(conn)->rend_data = mock_rend_data(service_id);
118 conn_rend_data = TO_EDGE_CONN(conn)->rend_data;
119 } else {
120 /* prop224: Setup hs conn identifier on the stream */
121 ed25519_secret_key_t sk;
122 tt_int_op(0, OP_EQ, ed25519_secret_key_generate(&sk, 0));
123 tt_int_op(0, OP_EQ, ed25519_public_key_generate(&service_pk, &sk));
125 /* Setup hs_conn_identifier of stream */
126 TO_EDGE_CONN(conn)->hs_ident = hs_ident_edge_conn_new(&service_pk);
129 /* Make it wait for circuit */
130 connection_ap_mark_as_pending_circuit(TO_ENTRY_CONN(conn));
132 /* This is needed to silence a BUG warning from
133 connection_edge_update_circuit_isolation() */
134 TO_ENTRY_CONN(conn)->original_dest_address =
135 tor_strdup(TO_ENTRY_CONN(conn)->socks_request->address);
137 /****************************************************/
139 /* Now make dummy circuit */
140 or_circ = origin_circuit_new();
142 or_circ->base_.purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED;
144 or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
145 or_circ->build_state->is_internal = 1;
147 if (is_legacy) {
148 /* Legacy: Setup rend data and final cpath */
149 or_circ->build_state->pending_final_cpath =
150 tor_malloc_zero(sizeof(crypt_path_t));
151 or_circ->build_state->pending_final_cpath->magic = CRYPT_PATH_MAGIC;
152 or_circ->build_state->pending_final_cpath->rend_dh_handshake_state =
153 crypto_dh_new(DH_TYPE_REND);
154 tt_assert(
155 or_circ->build_state->pending_final_cpath->rend_dh_handshake_state);
156 retval = crypto_dh_generate_public(
157 or_circ->build_state->pending_final_cpath->rend_dh_handshake_state);
158 tt_int_op(retval, OP_EQ, 0);
159 or_circ->rend_data = rend_data_dup(conn_rend_data);
160 } else {
161 /* prop224: Setup hs ident on the circuit */
162 or_circ->hs_ident = hs_ident_circuit_new(&service_pk,
163 HS_IDENT_CIRCUIT_RENDEZVOUS);
166 TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN;
168 /* fake n_chan */
169 n_chan = tor_malloc_zero(sizeof(channel_tls_t));
170 n_chan->base_.global_identifier = 1;
171 or_circ->base_.n_chan = &(n_chan->base_);
173 *circ_out = or_circ;
174 *conn_out = conn;
176 return 0;
178 done:
179 /* something failed */
180 return -1;
183 /* Test: Ensure that setting up legacy e2e rendezvous circuits works
184 * correctly. */
185 static void
186 test_e2e_rend_circuit_setup_legacy(void *arg)
188 ssize_t retval;
189 origin_circuit_t *or_circ = NULL;
190 connection_t *conn = NULL;
192 (void) arg;
194 /** In this test we create a v2 legacy HS stream and a circuit with the same
195 * hidden service destination. We make the stream wait for circuits to be
196 * established to the hidden service, and then we complete the circuit using
197 * the hs_circuit_setup_e2e_rend_circ_legacy_client() function. We then
198 * check that the end-to-end cpath was setup correctly and that the stream
199 * was attached to the circuit as expected. */
201 MOCK(connection_ap_handshake_send_begin,
202 mock_connection_ap_handshake_send_begin);
204 /* Setup */
205 retval = helper_get_circ_and_stream_for_test( &or_circ, &conn, 1);
206 tt_int_op(retval, OP_EQ, 0);
207 tt_assert(or_circ);
208 tt_assert(conn);
210 /* Check number of hops */
211 retval = cpath_get_n_hops(&or_circ->cpath);
212 tt_int_op(retval, OP_EQ, 0);
214 /* Check that our stream is not attached on any circuits */
215 tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, NULL);
217 /********************************************** */
219 /* Make a good RENDEZVOUS1 cell body because it needs to pass key exchange
220 * digest verification... */
221 uint8_t rend_cell_body[DH1024_KEY_LEN+DIGEST_LEN] = {2};
223 char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN];
224 crypto_dh_t *dh_state =
225 or_circ->build_state->pending_final_cpath->rend_dh_handshake_state;
226 /* compute and overwrite digest of cell body with the right value */
227 retval = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh_state,
228 (char*)rend_cell_body, DH1024_KEY_LEN,
229 keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN);
230 tt_int_op(retval, OP_GT, 0);
231 memcpy(rend_cell_body+DH1024_KEY_LEN, keys, DIGEST_LEN);
234 /* Setup the circuit */
235 retval = hs_circuit_setup_e2e_rend_circ_legacy_client(or_circ,
236 rend_cell_body);
237 tt_int_op(retval, OP_EQ, 0);
239 /**********************************************/
241 /* See that a hop was added to the circuit's cpath */
242 retval = cpath_get_n_hops(&or_circ->cpath);
243 tt_int_op(retval, OP_EQ, 1);
245 /* Check the digest algo */
246 tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->crypto.f_digest),
247 OP_EQ, DIGEST_SHA1);
248 tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->crypto.b_digest),
249 OP_EQ, DIGEST_SHA1);
250 tt_assert(or_circ->cpath->crypto.f_crypto);
251 tt_assert(or_circ->cpath->crypto.b_crypto);
253 /* Ensure that circ purpose was changed */
254 tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED);
256 /* Test that stream got attached */
257 tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ));
259 done:
260 connection_free_minimal(conn);
261 if (or_circ)
262 tor_free(TO_CIRCUIT(or_circ)->n_chan);
263 circuit_free_(TO_CIRCUIT(or_circ));
266 /* Test: Ensure that setting up v3 rendezvous circuits works correctly. */
267 static void
268 test_e2e_rend_circuit_setup(void *arg)
270 uint8_t ntor_key_seed[DIGEST256_LEN] = {0};
271 origin_circuit_t *or_circ = NULL;
272 int retval;
273 connection_t *conn = NULL;
275 (void) arg;
277 /** In this test we create a prop224 v3 HS stream and a circuit with the same
278 * hidden service destination. We make the stream wait for circuits to be
279 * established to the hidden service, and then we complete the circuit using
280 * the hs_circuit_setup_e2e_rend_circ() function. We then check that the
281 * end-to-end cpath was setup correctly and that the stream was attached to
282 * the circuit as expected. */
284 MOCK(connection_ap_handshake_send_begin,
285 mock_connection_ap_handshake_send_begin);
287 /* Setup */
288 retval = helper_get_circ_and_stream_for_test( &or_circ, &conn, 0);
289 tt_int_op(retval, OP_EQ, 0);
290 tt_assert(or_circ);
291 tt_assert(conn);
293 /* Check number of hops: There should be no hops yet to this circ */
294 retval = cpath_get_n_hops(&or_circ->cpath);
295 tt_int_op(retval, OP_EQ, 0);
296 tt_ptr_op(or_circ->cpath, OP_EQ, NULL);
298 /* Check that our stream is not attached on any circuits */
299 tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, NULL);
301 /**********************************************/
303 /* Setup the circuit */
304 retval = hs_circuit_setup_e2e_rend_circ(or_circ,
305 ntor_key_seed, sizeof(ntor_key_seed),
307 tt_int_op(retval, OP_EQ, 0);
309 /**********************************************/
311 /* See that a hop was added to the circuit's cpath */
312 retval = cpath_get_n_hops(&or_circ->cpath);
313 tt_int_op(retval, OP_EQ, 1);
315 /* Check that the crypt path has prop224 algorithm parameters */
316 tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->crypto.f_digest),
317 OP_EQ, DIGEST_SHA3_256);
318 tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->crypto.b_digest),
319 OP_EQ, DIGEST_SHA3_256);
320 tt_assert(or_circ->cpath->crypto.f_crypto);
321 tt_assert(or_circ->cpath->crypto.b_crypto);
323 /* Ensure that circ purpose was changed */
324 tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED);
326 /* Test that stream got attached */
327 tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ));
329 done:
330 connection_free_minimal(conn);
331 if (or_circ)
332 tor_free(TO_CIRCUIT(or_circ)->n_chan);
333 circuit_free_(TO_CIRCUIT(or_circ));
336 /** Test client logic for picking intro points from a descriptor. Also test how
337 * ExcludeNodes and intro point failures affect picking intro points. */
338 static void
339 test_client_pick_intro(void *arg)
341 int ret;
342 ed25519_keypair_t service_kp;
343 hs_descriptor_t *desc = NULL;
345 MOCK(networkstatus_get_reasonably_live_consensus,
346 mock_networkstatus_get_reasonably_live_consensus);
348 (void) arg;
350 hs_init();
352 /* Generate service keypair */
353 tt_int_op(0, OP_EQ, ed25519_keypair_generate(&service_kp, 0));
355 /* Set time */
356 ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
357 &mock_ns.valid_after);
358 tt_int_op(ret, OP_EQ, 0);
359 ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
360 &mock_ns.fresh_until);
361 tt_int_op(ret, OP_EQ, 0);
363 update_approx_time(mock_ns.fresh_until-10);
364 time_t now = approx_time();
366 /* Test logic:
368 * 1) Add our desc with intro points to the HS cache.
370 * 2) Mark all descriptor intro points except _the chosen one_ as
371 * failed. Then query the desc to get a random intro: check that we got
372 * _the chosen one_. Then fail the chosen one as well, and see that no
373 * intros are returned.
375 * 3) Then clean the intro state cache and get an intro point.
377 * 4) Try fetching an intro with the wrong service key: shouldn't work
379 * 5) Set StrictNodes and put all our intro points in ExcludeNodes: see that
380 * nothing is returned.
383 /* 1) Add desc to HS cache */
385 char *encoded = NULL;
386 desc = hs_helper_build_hs_desc_with_ip(&service_kp);
387 ret = hs_desc_encode_descriptor(desc, &service_kp, NULL, &encoded);
388 tt_int_op(ret, OP_EQ, 0);
389 tt_assert(encoded);
391 /* store it */
392 hs_cache_store_as_client(encoded, &service_kp.pubkey);
394 /* fetch it to make sure it works */
395 const hs_descriptor_t *fetched_desc =
396 hs_cache_lookup_as_client(&service_kp.pubkey);
397 tt_assert(fetched_desc);
398 tt_mem_op(fetched_desc->subcredential, OP_EQ, desc->subcredential,
399 DIGEST256_LEN);
400 tt_assert(!tor_mem_is_zero((char*)fetched_desc->subcredential,
401 DIGEST256_LEN));
402 tor_free(encoded);
405 /* 2) Mark all intro points except _the chosen one_ as failed. Then query the
406 * desc and get a random intro: check that we got _the chosen one_. */
408 /* Pick the chosen intro point and get its ei */
409 hs_desc_intro_point_t *chosen_intro_point =
410 smartlist_get(desc->encrypted_data.intro_points, 0);
411 extend_info_t *chosen_intro_ei =
412 desc_intro_point_to_extend_info(chosen_intro_point);
413 tt_assert(chosen_intro_point);
414 tt_assert(chosen_intro_ei);
416 /* Now mark all other intro points as failed */
417 SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
418 hs_desc_intro_point_t *, ip) {
419 /* Skip the chosen intro point */
420 if (ip == chosen_intro_point) {
421 continue;
423 ed25519_public_key_t *intro_auth_key = &ip->auth_key_cert->signed_key;
424 hs_cache_client_intro_state_note(&service_kp.pubkey,
425 intro_auth_key,
426 INTRO_POINT_FAILURE_GENERIC);
427 } SMARTLIST_FOREACH_END(ip);
429 /* Try to get a random intro: Should return the chosen one! */
430 /* (We try several times, to make sure this behavior is consistent, and to
431 * cover the different cases of client_get_random_intro().) */
432 for (int i = 0; i < 64; ++i) {
433 extend_info_t *ip = client_get_random_intro(&service_kp.pubkey);
434 tor_assert(ip);
435 tt_assert(!tor_mem_is_zero((char*)ip->identity_digest, DIGEST_LEN));
436 tt_mem_op(ip->identity_digest, OP_EQ, chosen_intro_ei->identity_digest,
437 DIGEST_LEN);
438 extend_info_free(ip);
441 extend_info_free(chosen_intro_ei);
443 /* Now also mark the chosen one as failed: See that we can't get any intro
444 points anymore. */
445 hs_cache_client_intro_state_note(&service_kp.pubkey,
446 &chosen_intro_point->auth_key_cert->signed_key,
447 INTRO_POINT_FAILURE_TIMEOUT);
448 extend_info_t *ip = client_get_random_intro(&service_kp.pubkey);
449 tor_assert(!ip);
452 /* 3) Clean the intro state cache and get an intro point */
454 /* Pretend we are 5 mins in the future and order a cleanup of the intro
455 * state. This should clean up the intro point failures and allow us to get
456 * an intro. */
457 hs_cache_client_intro_state_clean(now + 5*60);
459 /* Get an intro. It should work! */
460 extend_info_t *ip = client_get_random_intro(&service_kp.pubkey);
461 tor_assert(ip);
462 extend_info_free(ip);
465 /* 4) Try fetching an intro with the wrong service key: shouldn't work */
467 ed25519_keypair_t dummy_kp;
468 tt_int_op(0, OP_EQ, ed25519_keypair_generate(&dummy_kp, 0));
469 extend_info_t *ip = client_get_random_intro(&dummy_kp.pubkey);
470 tor_assert(!ip);
473 /* 5) Set StrictNodes and put all our intro points in ExcludeNodes: see that
474 * nothing is returned. */
476 get_options_mutable()->ExcludeNodes = routerset_new();
477 get_options_mutable()->StrictNodes = 1;
478 SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
479 hs_desc_intro_point_t *, ip) {
480 extend_info_t *intro_ei = desc_intro_point_to_extend_info(ip);
481 if (intro_ei) {
482 const char *ptr;
483 char ip_addr[TOR_ADDR_BUF_LEN];
484 /* We need to decorate in case it is an IPv6 else routerset_parse()
485 * doesn't like it. */
486 ptr = tor_addr_to_str(ip_addr, &intro_ei->addr, sizeof(ip_addr), 1);
487 tt_assert(ptr == ip_addr);
488 ret = routerset_parse(get_options_mutable()->ExcludeNodes,
489 ip_addr, "");
490 tt_int_op(ret, OP_EQ, 0);
491 extend_info_free(intro_ei);
493 } SMARTLIST_FOREACH_END(ip);
495 extend_info_t *ip = client_get_random_intro(&service_kp.pubkey);
496 tt_assert(!ip);
499 done:
500 hs_descriptor_free(desc);
503 static int
504 mock_router_have_minimum_dir_info_false(void)
506 return 0;
508 static int
509 mock_router_have_minimum_dir_info_true(void)
511 return 1;
514 static hs_client_fetch_status_t
515 mock_fetch_v3_desc_error(const ed25519_public_key_t *key)
517 (void) key;
518 return HS_CLIENT_FETCH_ERROR;
521 static void
522 mock_connection_mark_unattached_ap_(entry_connection_t *conn, int endreason,
523 int line, const char *file)
525 (void) line;
526 (void) file;
527 conn->edge_.end_reason = endreason;
528 /* This function ultimately will flag this so make sure we do also in the
529 * MOCK one so we can assess closed connections vs open ones. */
530 conn->edge_.base_.marked_for_close = 1;
533 static void
534 test_descriptor_fetch(void *arg)
536 int ret;
537 entry_connection_t *ec = NULL;
538 ed25519_public_key_t service_pk;
539 ed25519_secret_key_t service_sk;
541 (void) arg;
543 hs_init();
544 memset(&service_sk, 'A', sizeof(service_sk));
545 ret = ed25519_public_key_generate(&service_pk, &service_sk);
546 tt_int_op(ret, OP_EQ, 0);
548 /* Initialize this so get_voting_interval() doesn't freak out. */
549 ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
550 &mock_ns.valid_after);
551 tt_int_op(ret, OP_EQ, 0);
552 ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
553 &mock_ns.fresh_until);
554 tt_int_op(ret, OP_EQ, 0);
556 ec = entry_connection_new(CONN_TYPE_AP, AF_INET);
557 tt_assert(ec);
558 ENTRY_TO_EDGE_CONN(ec)->hs_ident = hs_ident_edge_conn_new(&service_pk);
559 tt_assert(ENTRY_TO_EDGE_CONN(ec)->hs_ident);
560 TO_CONN(ENTRY_TO_EDGE_CONN(ec))->state = AP_CONN_STATE_RENDDESC_WAIT;
561 smartlist_add(get_connection_array(), &ec->edge_.base_);
563 /* 1. FetchHidServDescriptors is false so we shouldn't be able to fetch. */
564 get_options_mutable()->FetchHidServDescriptors = 0;
565 ret = hs_client_refetch_hsdesc(&service_pk);
566 tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_NOT_ALLOWED);
567 get_options_mutable()->FetchHidServDescriptors = 1;
569 /* 2. We don't have a live consensus. */
570 MOCK(networkstatus_get_reasonably_live_consensus,
571 mock_networkstatus_get_reasonably_live_consensus_false);
572 ret = hs_client_refetch_hsdesc(&service_pk);
573 UNMOCK(networkstatus_get_reasonably_live_consensus);
574 tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_MISSING_INFO);
576 /* From now on, return a live consensus. */
577 MOCK(networkstatus_get_reasonably_live_consensus,
578 mock_networkstatus_get_reasonably_live_consensus);
580 /* 3. Not enough dir information. */
581 MOCK(router_have_minimum_dir_info,
582 mock_router_have_minimum_dir_info_false);
583 ret = hs_client_refetch_hsdesc(&service_pk);
584 UNMOCK(router_have_minimum_dir_info);
585 tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_MISSING_INFO);
587 /* From now on, we do have enough directory information. */
588 MOCK(router_have_minimum_dir_info,
589 mock_router_have_minimum_dir_info_true);
591 /* 4. We do have a pending directory request. */
593 dir_connection_t *dir_conn = dir_connection_new(AF_INET);
594 dir_conn->hs_ident = tor_malloc_zero(sizeof(hs_ident_dir_conn_t));
595 TO_CONN(dir_conn)->purpose = DIR_PURPOSE_FETCH_HSDESC;
596 ed25519_pubkey_copy(&dir_conn->hs_ident->identity_pk, &service_pk);
597 smartlist_add(get_connection_array(), TO_CONN(dir_conn));
598 ret = hs_client_refetch_hsdesc(&service_pk);
599 smartlist_remove(get_connection_array(), TO_CONN(dir_conn));
600 connection_free_minimal(TO_CONN(dir_conn));
601 tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_PENDING);
604 /* 5. We'll trigger an error on the fetch_desc_v3 and force to close all
605 * pending SOCKS request. */
606 MOCK(router_have_minimum_dir_info,
607 mock_router_have_minimum_dir_info_true);
608 MOCK(fetch_v3_desc, mock_fetch_v3_desc_error);
609 MOCK(connection_mark_unattached_ap_,
610 mock_connection_mark_unattached_ap_);
611 ret = hs_client_refetch_hsdesc(&service_pk);
612 UNMOCK(fetch_v3_desc);
613 UNMOCK(connection_mark_unattached_ap_);
614 tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_ERROR);
615 /* The close waiting for descriptor function has been called. */
616 tt_int_op(ec->edge_.end_reason, OP_EQ, END_STREAM_REASON_RESOLVEFAILED);
618 done:
619 connection_free_minimal(ENTRY_TO_CONN(ec));
620 UNMOCK(networkstatus_get_reasonably_live_consensus);
621 UNMOCK(router_have_minimum_dir_info);
622 hs_free_all();
625 static void
626 test_auth_key_filename_is_valid(void *arg)
628 (void) arg;
630 /* Valid file name. */
631 tt_assert(auth_key_filename_is_valid("a.auth_private"));
632 /* Valid file name with special character. */
633 tt_assert(auth_key_filename_is_valid("a-.auth_private"));
634 /* Invalid extension. */
635 tt_assert(!auth_key_filename_is_valid("a.ath_private"));
636 /* Nothing before the extension. */
637 tt_assert(!auth_key_filename_is_valid(".auth_private"));
639 done:
643 static void
644 test_parse_auth_file_content(void *arg)
646 hs_client_service_authorization_t *auth = NULL;
648 (void) arg;
650 /* Valid authorized client. */
651 auth = parse_auth_file_content(
652 "4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad:descriptor:"
653 "x25519:zdsyvn2jq534ugyiuzgjy4267jbtzcjbsgedhshzx5mforyxtryq");
654 tt_assert(auth);
656 /* Wrong number of fields. */
657 tt_assert(!parse_auth_file_content("a:b"));
658 /* Wrong auth type. */
659 tt_assert(!parse_auth_file_content(
660 "4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad:x:"
661 "x25519:zdsyvn2jq534ugyiuzgjy4267jbtzcjbsgedhshzx5mforyxtryq"));
662 /* Wrong key type. */
663 tt_assert(!parse_auth_file_content(
664 "4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad:descriptor:"
665 "x:zdsyvn2jq534ugyiuzgjy4267jbtzcjbsgedhshzx5mforyxtryq"));
666 /* Some malformed string. */
667 tt_assert(!parse_auth_file_content("xx:descriptor:x25519:aa=="));
668 /* Bigger key than it should be */
669 tt_assert(!parse_auth_file_content("xx:descriptor:x25519:"
670 "vjqea4jbhwwc4hto7ekyvqfbeodghbaq6nxi45hz4wr3qvhqv3yqa"));
671 done:
672 tor_free(auth);
675 static char *
676 mock_read_file_to_str(const char *filename, int flags, struct stat *stat_out)
678 char *ret = NULL;
680 (void) flags;
681 (void) stat_out;
683 if (!strcmp(filename, get_fname("auth_keys" PATH_SEPARATOR
684 "client1.auth_private"))) {
685 ret = tor_strdup(
686 "4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad:descriptor:"
687 "x25519:zdsyvn2jq534ugyiuzgjy4267jbtzcjbsgedhshzx5mforyxtryq");
688 goto done;
691 if (!strcmp(filename, get_fname("auth_keys" PATH_SEPARATOR "dummy.xxx"))) {
692 ret = tor_strdup(
693 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:descriptor:"
694 "x25519:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
695 goto done;
698 if (!strcmp(filename, get_fname("auth_keys" PATH_SEPARATOR
699 "client2.auth_private"))) {
700 ret = tor_strdup(
701 "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid:descriptor:"
702 "x25519:fdreqzjqso7d2ac7qscrxfl5qfpamdvgy5d6cxejcgzc3hvhurmq");
703 goto done;
706 done:
707 return ret;
710 static int
711 mock_check_private_dir(const char *dirname, cpd_check_t check,
712 const char *effective_user)
714 (void) dirname;
715 (void) check;
716 (void) effective_user;
718 return 0;
721 static smartlist_t *
722 mock_tor_listdir(const char *dirname)
724 smartlist_t *file_list = smartlist_new();
726 (void) dirname;
728 smartlist_add(file_list, tor_strdup("client1.auth_private"));
729 smartlist_add(file_list, tor_strdup("dummy.xxx"));
730 smartlist_add(file_list, tor_strdup("client2.auth_private"));
732 return file_list;
735 static void
736 test_config_client_authorization(void *arg)
738 int ret;
739 char *conf = NULL;
740 ed25519_public_key_t pk1, pk2;
741 digest256map_t *global_map = NULL;
742 char *key_dir = tor_strdup(get_fname("auth_keys"));
744 (void) arg;
746 MOCK(read_file_to_str, mock_read_file_to_str);
747 MOCK(tor_listdir, mock_tor_listdir);
748 MOCK(check_private_dir, mock_check_private_dir);
750 #define conf_fmt \
751 "ClientOnionAuthDir %s\n"
753 tor_asprintf(&conf, conf_fmt, key_dir);
754 ret = helper_config_client(conf, 0);
755 tor_free(conf);
756 tt_int_op(ret, OP_EQ, 0);
758 #undef conf_fmt
760 global_map = get_hs_client_auths_map();
761 tt_int_op(digest256map_size(global_map), OP_EQ, 2);
763 hs_parse_address("4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad",
764 &pk1, NULL, NULL);
765 hs_parse_address("25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid",
766 &pk2, NULL, NULL);
768 tt_assert(digest256map_get(global_map, pk1.pubkey));
769 tt_assert(digest256map_get(global_map, pk2.pubkey));
771 done:
772 tor_free(key_dir);
773 hs_free_all();
774 UNMOCK(read_file_to_str);
775 UNMOCK(tor_listdir);
776 UNMOCK(check_private_dir);
779 static entry_connection_t *
780 helper_build_socks_connection(const ed25519_public_key_t *service_pk,
781 int conn_state)
783 entry_connection_t *socks = entry_connection_new(CONN_TYPE_AP, AF_INET);
784 ENTRY_TO_EDGE_CONN(socks)->hs_ident = hs_ident_edge_conn_new(service_pk);
785 TO_CONN(ENTRY_TO_EDGE_CONN(socks))->state = conn_state;
786 smartlist_add(get_connection_array(), &socks->edge_.base_);
787 return socks;
790 static void
791 test_desc_has_arrived_cleanup(void *arg)
793 /* The goal of this test is to make sure we clean up everything in between
794 * two descriptors from the same .onion. Because intro points can change
795 * from one descriptor to another, once we received a new descriptor, we
796 * need to cleanup the remaining circuits so they aren't used or selected
797 * when establishing a connection with the newly stored descriptor.
799 * This test was created because of #27410. */
801 int ret;
802 char *desc_str = NULL;
803 hs_descriptor_t *desc = NULL;
804 const hs_descriptor_t *cached_desc;
805 ed25519_keypair_t signing_kp;
806 entry_connection_t *socks1 = NULL, *socks2 = NULL;
807 hs_ident_dir_conn_t hs_dir_ident;
809 (void) arg;
811 hs_init();
813 MOCK(networkstatus_get_reasonably_live_consensus,
814 mock_networkstatus_get_reasonably_live_consensus);
815 MOCK(connection_mark_unattached_ap_,
816 mock_connection_mark_unattached_ap_);
817 MOCK(router_have_minimum_dir_info,
818 mock_router_have_minimum_dir_info_true);
820 /* Set consensus time before our time so the cache lookup can always
821 * validate that the entry is not expired. */
822 parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC", &mock_ns.valid_after);
823 parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC", &mock_ns.fresh_until);
824 parse_rfc1123_time("Sat, 26 Oct 1985 16:00:00 UTC", &mock_ns.valid_until);
826 /* Build a descriptor for a specific .onion. */
827 ret = ed25519_keypair_generate(&signing_kp, 0);
828 tt_int_op(ret, OP_EQ, 0);
829 desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
830 tt_assert(desc);
831 ret = hs_desc_encode_descriptor(desc, &signing_kp, NULL, &desc_str);
832 tt_int_op(ret, OP_EQ, 0);
834 /* Store in the client cache. */
835 ret = hs_cache_store_as_client(desc_str, &signing_kp.pubkey);
836 tt_int_op(ret, OP_EQ, 0);
837 cached_desc = hs_cache_lookup_as_client(&signing_kp.pubkey);
838 tt_assert(cached_desc);
839 hs_helper_desc_equal(desc, cached_desc);
841 /* Create two SOCKS connection for the same .onion both in the waiting for a
842 * descriptor state. */
843 socks1 = helper_build_socks_connection(&signing_kp.pubkey,
844 AP_CONN_STATE_RENDDESC_WAIT);
845 tt_assert(socks1);
846 socks2 = helper_build_socks_connection(&signing_kp.pubkey,
847 AP_CONN_STATE_RENDDESC_WAIT);
848 tt_assert(socks2);
850 /* Now, we'll make the intro points in the current descriptor unusable so
851 * the hs_client_desc_has_arrived() will take the right code path that we
852 * want to test that is the fetched descriptor has bad intro points. */
853 SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
854 hs_desc_intro_point_t *, ip) {
855 hs_cache_client_intro_state_note(&signing_kp.pubkey,
856 &ip->auth_key_cert->signed_key,
857 INTRO_POINT_FAILURE_GENERIC);
858 } SMARTLIST_FOREACH_END(ip);
860 /* Simulate that a new descriptor just arrived. We should have both of our
861 * SOCKS connection to be ended with a resolved failed. */
862 hs_ident_dir_conn_init(&signing_kp.pubkey,
863 &desc->plaintext_data.blinded_pubkey, &hs_dir_ident);
864 hs_client_desc_has_arrived(&hs_dir_ident);
865 tt_int_op(socks1->edge_.end_reason, OP_EQ, END_STREAM_REASON_RESOLVEFAILED);
866 /* XXX: MUST work with OP_EQ. */
867 tt_int_op(socks2->edge_.end_reason, OP_EQ, END_STREAM_REASON_RESOLVEFAILED);
869 /* Now let say tor cleans up the intro state cache which resets all intro
870 * point failure count. */
871 hs_cache_client_intro_state_purge();
873 /* Retrying all SOCKS which should basically do nothing since we don't have
874 * any pending SOCKS connection in AP_CONN_STATE_RENDDESC_WAIT state. */
875 /* XXX: BUG() is triggered here, shouldn't if socks2 wasn't alive. */
876 retry_all_socks_conn_waiting_for_desc();
878 done:
879 connection_free_minimal(ENTRY_TO_CONN(socks1));
880 connection_free_minimal(ENTRY_TO_CONN(socks2));
881 hs_descriptor_free(desc);
882 tor_free(desc_str);
883 hs_free_all();
885 UNMOCK(networkstatus_get_reasonably_live_consensus);
886 UNMOCK(connection_mark_unattached_ap_);
887 UNMOCK(router_have_minimum_dir_info);
890 static void
891 test_close_intro_circuits_new_desc(void *arg)
893 int ret;
894 ed25519_keypair_t service_kp;
895 circuit_t *circ = NULL;
896 origin_circuit_t *ocirc = NULL;
897 hs_descriptor_t *desc1 = NULL, *desc2 = NULL;
899 (void) arg;
901 hs_init();
903 /* This is needed because of the client cache expiration timestamp is based
904 * on having a consensus. See cached_client_descriptor_has_expired(). */
905 MOCK(networkstatus_get_reasonably_live_consensus,
906 mock_networkstatus_get_reasonably_live_consensus);
908 /* Set consensus time */
909 parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
910 &mock_ns.valid_after);
911 parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
912 &mock_ns.fresh_until);
913 parse_rfc1123_time("Sat, 26 Oct 1985 16:00:00 UTC",
914 &mock_ns.valid_until);
916 /* Generate service keypair */
917 tt_int_op(0, OP_EQ, ed25519_keypair_generate(&service_kp, 0));
919 /* Create and add to the global list a dummy client introduction circuits.
920 * We'll then make sure the hs_ident is attached to a dummy descriptor. */
921 circ = dummy_origin_circuit_new(0);
922 tt_assert(circ);
923 circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING;
924 ocirc = TO_ORIGIN_CIRCUIT(circ);
926 /* Build the first descriptor and cache it. */
928 char *encoded;
929 desc1 = hs_helper_build_hs_desc_with_ip(&service_kp);
930 tt_assert(desc1);
931 ret = hs_desc_encode_descriptor(desc1, &service_kp, NULL, &encoded);
932 tt_int_op(ret, OP_EQ, 0);
933 tt_assert(encoded);
935 /* Store it */
936 ret = hs_cache_store_as_client(encoded, &service_kp.pubkey);
937 tt_int_op(ret, OP_EQ, 0);
938 tor_free(encoded);
939 tt_assert(hs_cache_lookup_as_client(&service_kp.pubkey));
942 /* We'll pick one introduction point and associate it with the circuit. */
944 const hs_desc_intro_point_t *ip =
945 smartlist_get(desc1->encrypted_data.intro_points, 0);
946 tt_assert(ip);
947 ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey,
948 HS_IDENT_CIRCUIT_INTRO);
949 ed25519_pubkey_copy(&ocirc->hs_ident->intro_auth_pk,
950 &ip->auth_key_cert->signed_key);
953 /* Before we are about to clean up the intro circuits, make sure it is
954 * actually there. */
955 tt_assert(circuit_get_next_intro_circ(NULL, true));
957 /* Build the second descriptor for the same service and cache it. */
959 char *encoded;
960 desc2 = hs_helper_build_hs_desc_with_ip(&service_kp);
961 tt_assert(desc2);
962 tt_mem_op(&desc1->plaintext_data.signing_pubkey, OP_EQ,
963 &desc2->plaintext_data.signing_pubkey, ED25519_PUBKEY_LEN);
964 /* To replace the existing descriptor, the revision counter needs to be
965 * bigger. */
966 desc2->plaintext_data.revision_counter =
967 desc1->plaintext_data.revision_counter + 1;
969 ret = hs_desc_encode_descriptor(desc2, &service_kp, NULL, &encoded);
970 tt_int_op(ret, OP_EQ, 0);
971 tt_assert(encoded);
973 ret = hs_cache_store_as_client(encoded, &service_kp.pubkey);
974 tt_int_op(ret, OP_EQ, 0);
975 tor_free(encoded);
976 tt_assert(hs_cache_lookup_as_client(&service_kp.pubkey));
979 /* Once stored, our intro circuit should be closed because it is related to
980 * an old introduction point that doesn't exists anymore. */
981 tt_assert(!circuit_get_next_intro_circ(NULL, true));
983 done:
984 circuit_free(circ);
985 hs_descriptor_free(desc1);
986 hs_descriptor_free(desc2);
987 hs_free_all();
988 UNMOCK(networkstatus_get_reasonably_live_consensus);
991 struct testcase_t hs_client_tests[] = {
992 { "e2e_rend_circuit_setup_legacy", test_e2e_rend_circuit_setup_legacy,
993 TT_FORK, NULL, NULL },
994 { "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup,
995 TT_FORK, NULL, NULL },
996 { "client_pick_intro", test_client_pick_intro,
997 TT_FORK, NULL, NULL },
998 { "descriptor_fetch", test_descriptor_fetch,
999 TT_FORK, NULL, NULL },
1000 { "auth_key_filename_is_valid", test_auth_key_filename_is_valid, TT_FORK,
1001 NULL, NULL },
1002 { "parse_auth_file_content", test_parse_auth_file_content, TT_FORK,
1003 NULL, NULL },
1004 { "config_client_authorization", test_config_client_authorization,
1005 TT_FORK, NULL, NULL },
1006 { "desc_has_arrived_cleanup", test_desc_has_arrived_cleanup,
1007 TT_FORK, NULL, NULL },
1008 { "close_intro_circuits_new_desc", test_close_intro_circuits_new_desc,
1009 TT_FORK, NULL, NULL },
1011 END_OF_TESTCASES