1 /* Copyright (c) 2016-2019, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
5 * \file test_hs_client.c
6 * \brief Test prop224 HS client functionality.
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"
55 mock_connection_ap_handshake_send_begin(entry_connection_t
*ap_conn
)
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
)
72 static networkstatus_t
*
73 mock_networkstatus_get_reasonably_live_consensus(time_t now
, int flavor
)
81 helper_config_client(const char *conf
, int validate_only
)
84 or_options_t
*options
= NULL
;
86 options
= helper_parse_options(conf
);
88 ret
= hs_config_client_auth_all(options
, validate_only
);
90 or_options_free(options
);
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
99 helper_get_circ_and_stream_for_test(origin_circuit_t
**circ_out
,
100 connection_t
**conn_out
,
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
/* ??? */,
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
;
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;
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
);
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
);
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
;
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_
);
179 /* something failed */
183 /* Test: Ensure that setting up legacy e2e rendezvous circuits works
186 test_e2e_rend_circuit_setup_legacy(void *arg
)
189 origin_circuit_t
*or_circ
= NULL
;
190 connection_t
*conn
= NULL
;
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
);
205 retval
= helper_get_circ_and_stream_for_test( &or_circ
, &conn
, 1);
206 tt_int_op(retval
, OP_EQ
, 0);
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
,
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
),
248 tt_int_op(crypto_digest_get_algorithm(or_circ
->cpath
->crypto
.b_digest
),
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
));
260 connection_free_minimal(conn
);
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. */
268 test_e2e_rend_circuit_setup(void *arg
)
270 uint8_t ntor_key_seed
[DIGEST256_LEN
] = {0};
271 origin_circuit_t
*or_circ
= NULL
;
273 connection_t
*conn
= NULL
;
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
);
288 retval
= helper_get_circ_and_stream_for_test( &or_circ
, &conn
, 0);
289 tt_int_op(retval
, OP_EQ
, 0);
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
));
330 connection_free_minimal(conn
);
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. */
339 test_client_pick_intro(void *arg
)
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
);
352 /* Generate service keypair */
353 tt_int_op(0, OP_EQ
, ed25519_keypair_generate(&service_kp
, 0));
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();
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);
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
,
400 tt_assert(!tor_mem_is_zero((char*)fetched_desc
->subcredential
,
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
) {
423 ed25519_public_key_t
*intro_auth_key
= &ip
->auth_key_cert
->signed_key
;
424 hs_cache_client_intro_state_note(&service_kp
.pubkey
,
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
);
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
,
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
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
);
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
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
);
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
);
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
);
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
,
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
);
500 hs_descriptor_free(desc
);
504 mock_router_have_minimum_dir_info_false(void)
509 mock_router_have_minimum_dir_info_true(void)
514 static hs_client_fetch_status_t
515 mock_fetch_v3_desc_error(const ed25519_public_key_t
*key
)
518 return HS_CLIENT_FETCH_ERROR
;
522 mock_connection_mark_unattached_ap_(entry_connection_t
*conn
, int endreason
,
523 int line
, const char *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;
534 test_descriptor_fetch(void *arg
)
537 entry_connection_t
*ec
= NULL
;
538 ed25519_public_key_t service_pk
;
539 ed25519_secret_key_t service_sk
;
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
);
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
);
619 connection_free_minimal(ENTRY_TO_CONN(ec
));
620 UNMOCK(networkstatus_get_reasonably_live_consensus
);
621 UNMOCK(router_have_minimum_dir_info
);
626 test_auth_key_filename_is_valid(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"));
644 test_parse_auth_file_content(void *arg
)
646 hs_client_service_authorization_t
*auth
= NULL
;
650 /* Valid authorized client. */
651 auth
= parse_auth_file_content(
652 "4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad:descriptor:"
653 "x25519:zdsyvn2jq534ugyiuzgjy4267jbtzcjbsgedhshzx5mforyxtryq");
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"));
676 mock_read_file_to_str(const char *filename
, int flags
, struct stat
*stat_out
)
683 if (!strcmp(filename
, get_fname("auth_keys" PATH_SEPARATOR
684 "client1.auth_private"))) {
686 "4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad:descriptor:"
687 "x25519:zdsyvn2jq534ugyiuzgjy4267jbtzcjbsgedhshzx5mforyxtryq");
691 if (!strcmp(filename
, get_fname("auth_keys" PATH_SEPARATOR
"dummy.xxx"))) {
693 "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:descriptor:"
694 "x25519:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
698 if (!strcmp(filename
, get_fname("auth_keys" PATH_SEPARATOR
699 "client2.auth_private"))) {
701 "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid:descriptor:"
702 "x25519:fdreqzjqso7d2ac7qscrxfl5qfpamdvgy5d6cxejcgzc3hvhurmq");
711 mock_check_private_dir(const char *dirname
, cpd_check_t check
,
712 const char *effective_user
)
716 (void) effective_user
;
722 mock_tor_listdir(const char *dirname
)
724 smartlist_t
*file_list
= smartlist_new();
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"));
736 test_config_client_authorization(void *arg
)
740 ed25519_public_key_t pk1
, pk2
;
741 digest256map_t
*global_map
= NULL
;
742 char *key_dir
= tor_strdup(get_fname("auth_keys"));
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
);
751 "ClientOnionAuthDir %s\n"
753 tor_asprintf(&conf
, conf_fmt
, key_dir
);
754 ret
= helper_config_client(conf
, 0);
756 tt_int_op(ret
, OP_EQ
, 0);
760 global_map
= get_hs_client_auths_map();
761 tt_int_op(digest256map_size(global_map
), OP_EQ
, 2);
763 hs_parse_address("4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad",
765 hs_parse_address("25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid",
768 tt_assert(digest256map_get(global_map
, pk1
.pubkey
));
769 tt_assert(digest256map_get(global_map
, pk2
.pubkey
));
774 UNMOCK(read_file_to_str
);
776 UNMOCK(check_private_dir
);
779 static entry_connection_t
*
780 helper_build_socks_connection(const ed25519_public_key_t
*service_pk
,
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_
);
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. */
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
;
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
);
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
);
846 socks2
= helper_build_socks_connection(&signing_kp
.pubkey
,
847 AP_CONN_STATE_RENDDESC_WAIT
);
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();
879 connection_free_minimal(ENTRY_TO_CONN(socks1
));
880 connection_free_minimal(ENTRY_TO_CONN(socks2
));
881 hs_descriptor_free(desc
);
885 UNMOCK(networkstatus_get_reasonably_live_consensus
);
886 UNMOCK(connection_mark_unattached_ap_
);
887 UNMOCK(router_have_minimum_dir_info
);
891 test_close_intro_circuits_new_desc(void *arg
)
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
;
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);
923 circ
->purpose
= CIRCUIT_PURPOSE_C_INTRODUCING
;
924 ocirc
= TO_ORIGIN_CIRCUIT(circ
);
926 /* Build the first descriptor and cache it. */
929 desc1
= hs_helper_build_hs_desc_with_ip(&service_kp
);
931 ret
= hs_desc_encode_descriptor(desc1
, &service_kp
, NULL
, &encoded
);
932 tt_int_op(ret
, OP_EQ
, 0);
936 ret
= hs_cache_store_as_client(encoded
, &service_kp
.pubkey
);
937 tt_int_op(ret
, OP_EQ
, 0);
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);
947 ocirc
->hs_ident
= hs_ident_circuit_new(&service_kp
.pubkey
,
948 HS_IDENT_CIRCUIT_INTRO
);
949 ed25519_pubkey_copy(ô
->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
955 tt_assert(circuit_get_next_intro_circ(NULL
, true));
957 /* Build the second descriptor for the same service and cache it. */
960 desc2
= hs_helper_build_hs_desc_with_ip(&service_kp
);
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
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);
973 ret
= hs_cache_store_as_client(encoded
, &service_kp
.pubkey
);
974 tt_int_op(ret
, OP_EQ
, 0);
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));
985 hs_descriptor_free(desc1
);
986 hs_descriptor_free(desc2
);
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
,
1002 { "parse_auth_file_content", test_parse_auth_file_content
, TT_FORK
,
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
},