LCOV - code coverage report
Current view: top level - test - test_hs_client.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 713 716 99.6 %
Date: 2021-11-24 03:28:48 Functions: 29 29 100.0 %

          Line data    Source code
       1             : /* Copyright (c) 2016-2021, The Tor Project, Inc. */
       2             : /* See LICENSE for licensing information */
       3             : 
       4             : /**
       5             :  * \file test_hs_client.c
       6             :  * \brief Test prop224 HS client functionality.
       7             :  */
       8             : 
       9             : #define CONFIG_PRIVATE
      10             : #define CRYPTO_PRIVATE
      11             : #define MAINLOOP_PRIVATE
      12             : #define HS_CLIENT_PRIVATE
      13             : #define CHANNEL_OBJECT_PRIVATE
      14             : #define CIRCUITBUILD_PRIVATE
      15             : #define CIRCUITLIST_PRIVATE
      16             : #define CONNECTION_PRIVATE
      17             : #define CRYPT_PATH_PRIVATE
      18             : 
      19             : #include "test/test.h"
      20             : #include "test/test_helpers.h"
      21             : #include "test/log_test_helpers.h"
      22             : #include "test/hs_test_helpers.h"
      23             : 
      24             : #include "app/config/config.h"
      25             : #include "lib/crypt_ops/crypto_cipher.h"
      26             : #include "lib/crypt_ops/crypto_dh.h"
      27             : #include "lib/crypt_ops/crypto_rand.h"
      28             : #include "core/or/channeltls.h"
      29             : #include "feature/dircommon/directory.h"
      30             : #include "core/mainloop/mainloop.h"
      31             : #include "feature/nodelist/nodelist.h"
      32             : #include "feature/nodelist/routerset.h"
      33             : 
      34             : #include "feature/hs/hs_circuit.h"
      35             : #include "feature/hs/hs_circuitmap.h"
      36             : #include "feature/hs/hs_client.h"
      37             : #include "feature/hs/hs_config.h"
      38             : #include "feature/hs/hs_ident.h"
      39             : #include "feature/hs/hs_cache.h"
      40             : #include "core/or/circuitlist.h"
      41             : #include "core/or/circuitbuild.h"
      42             : #include "core/or/extendinfo.h"
      43             : #include "core/mainloop/connection.h"
      44             : #include "core/or/connection_edge.h"
      45             : #include "feature/nodelist/networkstatus.h"
      46             : 
      47             : #include "core/or/cpath_build_state_st.h"
      48             : #include "core/or/crypt_path_st.h"
      49             : #include "core/or/crypt_path.h"
      50             : #include "feature/dircommon/dir_connection_st.h"
      51             : #include "core/or/entry_connection_st.h"
      52             : #include "core/or/extend_info_st.h"
      53             : #include "feature/nodelist/networkstatus_st.h"
      54             : #include "core/or/origin_circuit_st.h"
      55             : #include "core/or/socks_request_st.h"
      56             : 
      57             : static int
      58           1 : mock_connection_ap_handshake_send_begin(entry_connection_t *ap_conn)
      59             : {
      60           1 :   (void) ap_conn;
      61           1 :   return 0;
      62             : }
      63             : 
      64             : static networkstatus_t mock_ns;
      65             : 
      66             : /* Always return NULL. */
      67             : static networkstatus_t *
      68           1 : mock_networkstatus_get_reasonably_live_consensus_false(time_t now, int flavor)
      69             : {
      70           1 :   (void) now;
      71           1 :   (void) flavor;
      72           1 :   return NULL;
      73             : }
      74             : 
      75             : static networkstatus_t *
      76         173 : mock_networkstatus_get_reasonably_live_consensus(time_t now, int flavor)
      77             : {
      78         173 :   (void) now;
      79         173 :   (void) flavor;
      80         173 :   return &mock_ns;
      81             : }
      82             : 
      83             : static int
      84           1 : mock_write_str_to_file(const char *path, const char *str, int bin)
      85             : {
      86           1 :   (void) bin;
      87           1 :   (void) path;
      88           1 :   (void) str;
      89           1 :   return 0;
      90             : }
      91             : 
      92             : static or_options_t mocked_options;
      93             : 
      94             : static const or_options_t *
      95           1 : mock_get_options(void)
      96             : {
      97           1 :   return &mocked_options;
      98             : }
      99             : 
     100             : static int
     101           2 : helper_config_client(const char *conf, int validate_only)
     102             : {
     103           2 :   int ret = 0;
     104           2 :   or_options_t *options = NULL;
     105           2 :   tt_assert(conf);
     106           2 :   options = helper_parse_options(conf);
     107           2 :   tt_assert(options);
     108           2 :   ret = hs_config_client_auth_all(options, validate_only);
     109           2 :  done:
     110           2 :   or_options_free(options);
     111           2 :   return ret;
     112             : }
     113             : 
     114             : static void
     115           1 : helper_add_random_client_auth(const ed25519_public_key_t *service_pk)
     116             : {
     117           1 :   char *conf = NULL;
     118             : #define conf_fmt "ClientOnionAuthDir %s\n"
     119           1 :   tor_asprintf(&conf, conf_fmt, get_fname("auth_keys"));
     120             : #undef conf_fmt
     121           1 :   helper_config_client(conf, 0);
     122           1 :   tor_free(conf);
     123             : 
     124           1 :   digest256map_t *client_auths = get_hs_client_auths_map();
     125           1 :   hs_client_service_authorization_t *auth =
     126           1 :     tor_malloc_zero(sizeof(hs_client_service_authorization_t));
     127           1 :   curve25519_secret_key_generate(&auth->enc_seckey, 0);
     128           1 :   hs_build_address(service_pk, HS_VERSION_THREE, auth->onion_address);
     129           1 :   digest256map_set(client_auths, service_pk->pubkey, auth);
     130           1 : }
     131             : 
     132             : /* Test helper function: Setup a circuit and a stream with the same hidden
     133             :  * service destination, and put them in <b>circ_out</b> and
     134             :  * <b>conn_out</b>. Make the stream wait for circuits to be established to the
     135             :  * hidden service. */
     136             : static int
     137           1 : helper_get_circ_and_stream_for_test(origin_circuit_t **circ_out,
     138             :                                     connection_t **conn_out)
     139             : {
     140           1 :   channel_tls_t *n_chan=NULL;
     141           1 :   origin_circuit_t *or_circ = NULL;
     142           1 :   connection_t *conn = NULL;
     143           1 :   ed25519_public_key_t service_pk;
     144             : 
     145             :   /* Make a dummy connection stream and make it wait for our circuit */
     146           1 :   conn = test_conn_get_connection(AP_CONN_STATE_CIRCUIT_WAIT,
     147             :                                   CONN_TYPE_AP /* ??? */,
     148             :                                   0);
     149             :   /* prop224: Setup hs conn identifier on the stream */
     150           1 :   ed25519_secret_key_t sk;
     151           1 :   tt_int_op(0, OP_EQ, ed25519_secret_key_generate(&sk, 0));
     152           1 :   tt_int_op(0, OP_EQ, ed25519_public_key_generate(&service_pk, &sk));
     153             : 
     154             :   /* Setup hs_conn_identifier of stream */
     155           1 :   TO_EDGE_CONN(conn)->hs_ident = hs_ident_edge_conn_new(&service_pk);
     156             : 
     157             :   /* Make it wait for circuit */
     158           1 :   connection_ap_mark_as_pending_circuit(TO_ENTRY_CONN(conn));
     159             : 
     160             :   /* This is needed to silence a BUG warning from
     161             :      connection_edge_update_circuit_isolation() */
     162           3 :   TO_ENTRY_CONN(conn)->original_dest_address =
     163           1 :     tor_strdup(TO_ENTRY_CONN(conn)->socks_request->address);
     164             : 
     165             :   /****************************************************/
     166             : 
     167             :   /* Now make dummy circuit */
     168           1 :   or_circ = origin_circuit_new();
     169             : 
     170           1 :   or_circ->base_.purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED;
     171             : 
     172           1 :   or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
     173           1 :   or_circ->build_state->is_internal = 1;
     174             : 
     175             :   /* prop224: Setup hs ident on the circuit */
     176           1 :   or_circ->hs_ident = hs_ident_circuit_new(&service_pk);
     177             : 
     178           1 :   TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN;
     179             : 
     180             :   /* fake n_chan */
     181           1 :   n_chan = tor_malloc_zero(sizeof(channel_tls_t));
     182           1 :   n_chan->base_.global_identifier = 1;
     183           1 :   or_circ->base_.n_chan = &(n_chan->base_);
     184             : 
     185           1 :   *circ_out = or_circ;
     186           1 :   *conn_out = conn;
     187             : 
     188           1 :   return 0;
     189             : 
     190             :  done:
     191             :   /* something failed */
     192             :   return -1;
     193             : }
     194             : 
     195             : /* Test: Ensure that setting up v3 rendezvous circuits works correctly. */
     196             : static void
     197           1 : test_e2e_rend_circuit_setup(void *arg)
     198             : {
     199           1 :   uint8_t ntor_key_seed[DIGEST256_LEN] = {0};
     200           1 :   origin_circuit_t *or_circ = NULL;
     201           1 :   int retval;
     202           1 :   connection_t *conn = NULL;
     203             : 
     204           1 :   (void) arg;
     205             : 
     206             :   /** In this test we create a prop224 v3 HS stream and a circuit with the same
     207             :    *  hidden service destination. We make the stream wait for circuits to be
     208             :    *  established to the hidden service, and then we complete the circuit using
     209             :    *  the hs_circuit_setup_e2e_rend_circ() function. We then check that the
     210             :    *  end-to-end cpath was setup correctly and that the stream was attached to
     211             :    *  the circuit as expected. */
     212             : 
     213           1 :   MOCK(connection_ap_handshake_send_begin,
     214             :        mock_connection_ap_handshake_send_begin);
     215             : 
     216             :   /* Setup */
     217           1 :   retval = helper_get_circ_and_stream_for_test(&or_circ, &conn);
     218           1 :   tt_int_op(retval, OP_EQ, 0);
     219           1 :   tt_assert(or_circ);
     220           1 :   tt_assert(conn);
     221             : 
     222             :   /* Check number of hops: There should be no hops yet to this circ */
     223           1 :   retval = cpath_get_n_hops(&or_circ->cpath);
     224           1 :   tt_int_op(retval, OP_EQ, 0);
     225           1 :   tt_ptr_op(or_circ->cpath, OP_EQ, NULL);
     226             : 
     227             :   /* Check that our stream is not attached on any circuits */
     228           1 :   tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, NULL);
     229             : 
     230             :   /**********************************************/
     231             : 
     232             :   /* Setup the circuit */
     233           1 :   retval = hs_circuit_setup_e2e_rend_circ(or_circ, ntor_key_seed,
     234             :                                           sizeof(ntor_key_seed), 0);
     235           1 :   tt_int_op(retval, OP_EQ, 0);
     236             : 
     237             :   /**********************************************/
     238             : 
     239             :   /* See that a hop was added to the circuit's cpath */
     240           1 :   retval = cpath_get_n_hops(&or_circ->cpath);
     241           1 :   tt_int_op(retval, OP_EQ, 1);
     242             : 
     243             :   /* Check that the crypt path has prop224 algorithm parameters */
     244           1 :   tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.f_digest),
     245             :             OP_EQ, DIGEST_SHA3_256);
     246           1 :   tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->pvt_crypto.b_digest),
     247             :             OP_EQ, DIGEST_SHA3_256);
     248           1 :   tt_assert(or_circ->cpath->pvt_crypto.f_crypto);
     249           1 :   tt_assert(or_circ->cpath->pvt_crypto.b_crypto);
     250             : 
     251             :   /* Ensure that circ purpose was changed */
     252           1 :   tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED);
     253             : 
     254             :   /* Test that stream got attached */
     255           1 :   tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ));
     256             : 
     257           1 :  done:
     258           1 :   connection_free_minimal(conn);
     259           1 :   if (or_circ)
     260           1 :     tor_free(TO_CIRCUIT(or_circ)->n_chan);
     261           1 :   circuit_free_(TO_CIRCUIT(or_circ));
     262           1 : }
     263             : 
     264             : /** Test client logic for picking intro points from a descriptor. Also test how
     265             :  *  ExcludeNodes and intro point failures affect picking intro points. */
     266             : static void
     267           1 : test_client_pick_intro(void *arg)
     268             : {
     269           1 :   int ret;
     270           1 :   ed25519_keypair_t service_kp;
     271           1 :   hs_descriptor_t *desc = NULL;
     272             : 
     273           1 :   MOCK(networkstatus_get_reasonably_live_consensus,
     274             :        mock_networkstatus_get_reasonably_live_consensus);
     275             : 
     276           1 :   (void) arg;
     277             : 
     278           1 :   hs_init();
     279             : 
     280             :   /* Generate service keypair */
     281           1 :   tt_int_op(0, OP_EQ, ed25519_keypair_generate(&service_kp, 0));
     282             : 
     283             :   /* Set time */
     284           1 :   ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
     285             :                            &mock_ns.valid_after);
     286           1 :   tt_int_op(ret, OP_EQ, 0);
     287           1 :   ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
     288             :                            &mock_ns.fresh_until);
     289           1 :   tt_int_op(ret, OP_EQ, 0);
     290             : 
     291           1 :   update_approx_time(mock_ns.fresh_until-10);
     292           1 :   time_t now = approx_time();
     293             : 
     294             :   /* Test logic:
     295             :    *
     296             :    * 1) Add our desc with intro points to the HS cache.
     297             :    *
     298             :    * 2) Mark all descriptor intro points except _the chosen one_ as
     299             :    *    failed. Then query the desc to get a random intro: check that we got
     300             :    *    _the chosen one_. Then fail the chosen one as well, and see that no
     301             :    *    intros are returned.
     302             :    *
     303             :    * 3) Then clean the intro state cache and get an intro point.
     304             :    *
     305             :    * 4) Try fetching an intro with the wrong service key: shouldn't work
     306             :    *
     307             :    * 5) Set StrictNodes and put all our intro points in ExcludeNodes: see that
     308             :    *    nothing is returned.
     309             :    */
     310             : 
     311             :   /* 1) Add desc to HS cache */
     312             :   {
     313           1 :     char *encoded = NULL;
     314           1 :     desc = hs_helper_build_hs_desc_with_ip(&service_kp);
     315           1 :     ret = hs_desc_encode_descriptor(desc, &service_kp, NULL, &encoded);
     316           1 :     tt_int_op(ret, OP_EQ, 0);
     317           1 :     tt_assert(encoded);
     318             : 
     319             :     /* store it */
     320           1 :     ret = hs_cache_store_as_client(encoded, &service_kp.pubkey);
     321           1 :     tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
     322             : 
     323             :     /* fetch it to make sure it works */
     324           1 :     const hs_descriptor_t *fetched_desc =
     325           1 :       hs_cache_lookup_as_client(&service_kp.pubkey);
     326           1 :     tt_assert(fetched_desc);
     327           1 :     tt_mem_op(fetched_desc->subcredential.subcred,
     328             :               OP_EQ, desc->subcredential.subcred,
     329           1 :               SUBCRED_LEN);
     330           1 :     tt_assert(!fast_mem_is_zero((char*)fetched_desc->subcredential.subcred,
     331             :                                DIGEST256_LEN));
     332           1 :     tor_free(encoded);
     333             :   }
     334             : 
     335             :   /* 2) Mark all intro points except _the chosen one_ as failed. Then query the
     336             :    *   desc and get a random intro: check that we got _the chosen one_. */
     337             :   {
     338             :     /* Tell hs_get_extend_info_from_lspecs() to skip the private address check.
     339             :      */
     340           1 :     get_options_mutable()->ExtendAllowPrivateAddresses = 1;
     341             :     /* Pick the chosen intro point and get its ei */
     342           1 :     hs_desc_intro_point_t *chosen_intro_point =
     343           1 :       smartlist_get(desc->encrypted_data.intro_points, 0);
     344           2 :     extend_info_t *chosen_intro_ei =
     345           1 :       desc_intro_point_to_extend_info(chosen_intro_point);
     346           1 :     tt_assert(chosen_intro_point);
     347           1 :     tt_assert(chosen_intro_ei);
     348             : 
     349             :     /* Now mark all other intro points as failed */
     350           5 :     SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
     351             :                             hs_desc_intro_point_t *, ip) {
     352             :       /* Skip the chosen intro point */
     353           4 :       if (ip == chosen_intro_point) {
     354           1 :         continue;
     355             :       }
     356           3 :       ed25519_public_key_t *intro_auth_key = &ip->auth_key_cert->signed_key;
     357           3 :       hs_cache_client_intro_state_note(&service_kp.pubkey,
     358             :                                        intro_auth_key,
     359             :                                        INTRO_POINT_FAILURE_GENERIC);
     360           4 :     } SMARTLIST_FOREACH_END(ip);
     361             : 
     362             :     /* Try to get a random intro: Should return the chosen one! */
     363             :     /* (We try several times, to make sure this behavior is consistent, and to
     364             :      * cover the different cases of client_get_random_intro().) */
     365          65 :     for (int i = 0; i < 64; ++i) {
     366          64 :       extend_info_t *ip = client_get_random_intro(&service_kp.pubkey);
     367          64 :       tor_assert(ip);
     368          64 :       tt_assert(!fast_mem_is_zero((char*)ip->identity_digest, DIGEST_LEN));
     369          64 :       tt_mem_op(ip->identity_digest, OP_EQ, chosen_intro_ei->identity_digest,
     370          64 :                 DIGEST_LEN);
     371          64 :       extend_info_free(ip);
     372             :     }
     373             : 
     374           1 :     extend_info_free(chosen_intro_ei);
     375             : 
     376             :     /* Now also mark the chosen one as failed: See that we can't get any intro
     377             :        points anymore. */
     378           1 :     hs_cache_client_intro_state_note(&service_kp.pubkey,
     379           1 :                                 &chosen_intro_point->auth_key_cert->signed_key,
     380             :                                      INTRO_POINT_FAILURE_TIMEOUT);
     381           1 :     extend_info_t *ip = client_get_random_intro(&service_kp.pubkey);
     382           1 :     tor_assert(!ip);
     383             :   }
     384             : 
     385             :   /* 3) Clean the intro state cache and get an intro point */
     386             :   {
     387             :     /* Pretend we are 5 mins in the future and order a cleanup of the intro
     388             :      * state. This should clean up the intro point failures and allow us to get
     389             :      * an intro. */
     390           1 :     hs_cache_client_intro_state_clean(now + 5*60);
     391             : 
     392             :     /* Get an intro. It should work! */
     393           1 :     extend_info_t *ip = client_get_random_intro(&service_kp.pubkey);
     394           1 :     tor_assert(ip);
     395           1 :     extend_info_free(ip);
     396             :   }
     397             : 
     398             :   /* 4) Try fetching an intro with the wrong service key: shouldn't work */
     399             :   {
     400           1 :     ed25519_keypair_t dummy_kp;
     401           1 :     tt_int_op(0, OP_EQ, ed25519_keypair_generate(&dummy_kp, 0));
     402           1 :     extend_info_t *ip = client_get_random_intro(&dummy_kp.pubkey);
     403           1 :     tor_assert(!ip);
     404             :   }
     405             : 
     406             :   /* 5) Set StrictNodes and put all our intro points in ExcludeNodes: see that
     407             :    *    nothing is returned. */
     408             :   {
     409           1 :     get_options_mutable()->ExcludeNodes = routerset_new();
     410           1 :     get_options_mutable()->StrictNodes = 1;
     411           5 :     SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
     412             :                             hs_desc_intro_point_t *, ip) {
     413           4 :       extend_info_t *intro_ei = desc_intro_point_to_extend_info(ip);
     414             :       /* desc_intro_point_to_extend_info() doesn't return IPv6 intro points
     415             :        * yet, because we can't extend to them. See #24404, #24451, and #24181.
     416             :        */
     417           4 :       if (intro_ei == NULL) {
     418             :         /* Pretend we're making a direct connection, and that we can use IPv6
     419             :          */
     420           1 :         get_options_mutable()->ClientUseIPv6 = 1;
     421           2 :         intro_ei = hs_get_extend_info_from_lspecs(ip->link_specifiers,
     422           1 :                                                   &ip->onion_key, 1);
     423           1 :         tt_assert(tor_addr_family(&intro_ei->orports[0].addr) == AF_INET6);
     424             :       }
     425           4 :       tt_assert(intro_ei);
     426           4 :       if (intro_ei) {
     427           4 :         const char *ptr;
     428           4 :         char ip_addr[TOR_ADDR_BUF_LEN];
     429             :         /* We need to decorate in case it is an IPv6 else routerset_parse()
     430             :          * doesn't like it. */
     431           4 :         ptr = tor_addr_to_str(ip_addr, &intro_ei->orports[0].addr,
     432             :                               sizeof(ip_addr), 1);
     433           4 :         tt_assert(ptr == ip_addr);
     434           4 :         ret = routerset_parse(get_options_mutable()->ExcludeNodes,
     435             :                               ip_addr, "");
     436           4 :         tt_int_op(ret, OP_EQ, 0);
     437           4 :         extend_info_free(intro_ei);
     438             :       }
     439           4 :     } SMARTLIST_FOREACH_END(ip);
     440             : 
     441           1 :     extend_info_t *ip = client_get_random_intro(&service_kp.pubkey);
     442           1 :     tt_assert(!ip);
     443             :   }
     444             : 
     445           1 :  done:
     446           1 :   hs_descriptor_free(desc);
     447           1 : }
     448             : 
     449             : static int
     450           1 : mock_router_have_minimum_dir_info_false(void)
     451             : {
     452           1 :   return 0;
     453             : }
     454             : static int
     455           2 : mock_router_have_minimum_dir_info_true(void)
     456             : {
     457           2 :   return 1;
     458             : }
     459             : 
     460             : static hs_client_fetch_status_t
     461           1 : mock_fetch_v3_desc_error(const ed25519_public_key_t *key)
     462             : {
     463           1 :   (void) key;
     464           1 :   return HS_CLIENT_FETCH_ERROR;
     465             : }
     466             : 
     467             : static void
     468           3 : mock_connection_mark_unattached_ap_(entry_connection_t *conn, int endreason,
     469             :                                     int line, const char *file)
     470             : {
     471           3 :   (void) line;
     472           3 :   (void) file;
     473           3 :   conn->edge_.end_reason = endreason;
     474             :   /* This function ultimately will flag this so make sure we do also in the
     475             :    * MOCK one so we can assess closed connections vs open ones. */
     476           3 :   conn->edge_.base_.marked_for_close = 1;
     477           3 : }
     478             : 
     479             : static void
     480           4 : mock_connection_mark_unattached_ap_no_close(entry_connection_t *conn,
     481             :                                             int endreason, int line,
     482             :                                             const char *file)
     483             : {
     484           4 :   (void) conn;
     485           4 :   (void) endreason;
     486           4 :   (void) line;
     487           4 :   (void) file;
     488           4 : }
     489             : 
     490             : static void
     491           1 : test_descriptor_fetch(void *arg)
     492             : {
     493           1 :   int ret;
     494           1 :   entry_connection_t *ec = NULL;
     495           1 :   ed25519_public_key_t service_pk;
     496           1 :   ed25519_secret_key_t service_sk;
     497             : 
     498           1 :   (void) arg;
     499             : 
     500           1 :   hs_init();
     501           1 :   memset(&service_sk, 'A', sizeof(service_sk));
     502           1 :   ret = ed25519_public_key_generate(&service_pk, &service_sk);
     503           1 :   tt_int_op(ret, OP_EQ, 0);
     504             : 
     505             :   /* Initialize this so get_voting_interval() doesn't freak out. */
     506           1 :   ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
     507             :                            &mock_ns.valid_after);
     508           1 :   tt_int_op(ret, OP_EQ, 0);
     509           1 :   ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
     510             :                            &mock_ns.fresh_until);
     511           1 :   tt_int_op(ret, OP_EQ, 0);
     512             : 
     513           1 :   ec = entry_connection_new(CONN_TYPE_AP, AF_INET);
     514           1 :   tt_assert(ec);
     515           1 :   ENTRY_TO_EDGE_CONN(ec)->hs_ident = hs_ident_edge_conn_new(&service_pk);
     516           1 :   tt_assert(ENTRY_TO_EDGE_CONN(ec)->hs_ident);
     517           1 :   TO_CONN(ENTRY_TO_EDGE_CONN(ec))->state = AP_CONN_STATE_RENDDESC_WAIT;
     518           1 :   smartlist_add(get_connection_array(), &ec->edge_.base_);
     519             : 
     520             :   /* 1. FetchHidServDescriptors is false so we shouldn't be able to fetch. */
     521           1 :   get_options_mutable()->FetchHidServDescriptors = 0;
     522           1 :   ret = hs_client_refetch_hsdesc(&service_pk);
     523           1 :   tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_NOT_ALLOWED);
     524           1 :   get_options_mutable()->FetchHidServDescriptors = 1;
     525             : 
     526             :   /* 2. We don't have a live consensus. */
     527           1 :   MOCK(networkstatus_get_reasonably_live_consensus,
     528             :        mock_networkstatus_get_reasonably_live_consensus_false);
     529           1 :   ret = hs_client_refetch_hsdesc(&service_pk);
     530           1 :   UNMOCK(networkstatus_get_reasonably_live_consensus);
     531           1 :   tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_MISSING_INFO);
     532             : 
     533             :   /* From now on, return a live consensus. */
     534           1 :   MOCK(networkstatus_get_reasonably_live_consensus,
     535             :        mock_networkstatus_get_reasonably_live_consensus);
     536             : 
     537             :   /* 3. Not enough dir information. */
     538           1 :   MOCK(router_have_minimum_dir_info,
     539             :        mock_router_have_minimum_dir_info_false);
     540           1 :   ret = hs_client_refetch_hsdesc(&service_pk);
     541           1 :   UNMOCK(router_have_minimum_dir_info);
     542           1 :   tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_MISSING_INFO);
     543             : 
     544             :   /* From now on, we do have enough directory information. */
     545           1 :   MOCK(router_have_minimum_dir_info,
     546             :        mock_router_have_minimum_dir_info_true);
     547             : 
     548             :   /* 4. We do have a pending directory request. */
     549             :   {
     550           1 :     dir_connection_t *dir_conn = dir_connection_new(AF_INET);
     551           1 :     dir_conn->hs_ident = tor_malloc_zero(sizeof(hs_ident_dir_conn_t));
     552           1 :     TO_CONN(dir_conn)->purpose = DIR_PURPOSE_FETCH_HSDESC;
     553           1 :     ed25519_pubkey_copy(&dir_conn->hs_ident->identity_pk, &service_pk);
     554           1 :     smartlist_add(get_connection_array(), TO_CONN(dir_conn));
     555           1 :     ret = hs_client_refetch_hsdesc(&service_pk);
     556           1 :     smartlist_remove(get_connection_array(), TO_CONN(dir_conn));
     557           1 :     connection_free_minimal(TO_CONN(dir_conn));
     558           1 :     tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_PENDING);
     559             :   }
     560             : 
     561             :   /* 5. We'll trigger an error on the fetch_desc_v3 and force to close all
     562             :    *    pending SOCKS request. */
     563           1 :   MOCK(router_have_minimum_dir_info,
     564             :        mock_router_have_minimum_dir_info_true);
     565           1 :   MOCK(fetch_v3_desc, mock_fetch_v3_desc_error);
     566           1 :   MOCK(connection_mark_unattached_ap_,
     567             :        mock_connection_mark_unattached_ap_);
     568           1 :   ret = hs_client_refetch_hsdesc(&service_pk);
     569           1 :   UNMOCK(fetch_v3_desc);
     570           1 :   UNMOCK(connection_mark_unattached_ap_);
     571           1 :   tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_ERROR);
     572             :   /* The close waiting for descriptor function has been called. */
     573           1 :   tt_int_op(ec->edge_.end_reason, OP_EQ, END_STREAM_REASON_RESOLVEFAILED);
     574             : 
     575           1 :  done:
     576           1 :   connection_free_minimal(ENTRY_TO_CONN(ec));
     577           1 :   UNMOCK(networkstatus_get_reasonably_live_consensus);
     578           1 :   UNMOCK(router_have_minimum_dir_info);
     579           1 :   hs_free_all();
     580           1 : }
     581             : 
     582             : static void
     583           1 : test_auth_key_filename_is_valid(void *arg)
     584             : {
     585           1 :   (void) arg;
     586             : 
     587             :   /* Valid file name. */
     588           1 :   tt_assert(auth_key_filename_is_valid("a.auth_private"));
     589             :   /* Valid file name with special character. */
     590           1 :   tt_assert(auth_key_filename_is_valid("a-.auth_private"));
     591             :   /* Invalid extension. */
     592           1 :   tt_assert(!auth_key_filename_is_valid("a.ath_private"));
     593             :   /* Nothing before the extension. */
     594           1 :   tt_assert(!auth_key_filename_is_valid(".auth_private"));
     595             : 
     596           1 :  done:
     597           1 :   ;
     598           1 : }
     599             : 
     600             : static void
     601           1 : test_parse_auth_file_content(void *arg)
     602             : {
     603           1 :   hs_client_service_authorization_t *auth = NULL;
     604             : 
     605           1 :   (void) arg;
     606             : 
     607             :   /* Valid authorized client. */
     608           1 :   auth = parse_auth_file_content(
     609             :       "4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad:descriptor:"
     610             :       "x25519:zdsyvn2jq534ugyiuzgjy4267jbtzcjbsgedhshzx5mforyxtryq");
     611           1 :   tt_assert(auth);
     612             : 
     613             :   /* Wrong number of fields. */
     614           1 :   tt_assert(!parse_auth_file_content("a:b"));
     615             :   /* Wrong auth type. */
     616           1 :   tt_assert(!parse_auth_file_content(
     617             :       "4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad:x:"
     618             :       "x25519:zdsyvn2jq534ugyiuzgjy4267jbtzcjbsgedhshzx5mforyxtryq"));
     619             :   /* Wrong key type. */
     620           1 :   tt_assert(!parse_auth_file_content(
     621             :       "4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad:descriptor:"
     622             :       "x:zdsyvn2jq534ugyiuzgjy4267jbtzcjbsgedhshzx5mforyxtryq"));
     623             :   /* Some malformed string. */
     624           1 :   tt_assert(!parse_auth_file_content("xx:descriptor:x25519:aa=="));
     625             :   /* Bigger key than it should be */
     626           1 :   tt_assert(!parse_auth_file_content("xx:descriptor:x25519:"
     627             :                      "vjqea4jbhwwc4hto7ekyvqfbeodghbaq6nxi45hz4wr3qvhqv3yqa"));
     628             :   /* All-zeroes key */
     629           1 :   tt_assert(!parse_auth_file_content("xx:descriptor:x25519:"
     630             :             "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
     631             : 
     632           1 :  done:
     633           1 :   tor_free(auth);
     634           1 : }
     635             : 
     636             : static char *
     637           4 : mock_read_file_to_str(const char *filename, int flags, struct stat *stat_out)
     638             : {
     639           4 :   char *ret = NULL;
     640             : 
     641           4 :   (void) flags;
     642           4 :   (void) stat_out;
     643             : 
     644           4 :   if (!strcmp(filename, get_fname("auth_keys" PATH_SEPARATOR
     645             :                                               "client1.auth_private"))) {
     646           2 :     ret = tor_strdup(
     647             :         "4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad:descriptor:"
     648             :         "x25519:zdsyvn2jq534ugyiuzgjy4267jbtzcjbsgedhshzx5mforyxtryq");
     649           2 :     goto done;
     650             :   }
     651             : 
     652           2 :   if (!strcmp(filename, get_fname("auth_keys" PATH_SEPARATOR "dummy.xxx"))) {
     653           0 :     ret = tor_strdup(
     654             :         "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:descriptor:"
     655             :         "x25519:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
     656           0 :     goto done;
     657             :   }
     658             : 
     659           2 :   if (!strcmp(filename, get_fname("auth_keys" PATH_SEPARATOR
     660             :                                               "client2.auth_private"))) {
     661           2 :     ret = tor_strdup(
     662             :         "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid:descriptor:"
     663             :         "x25519:fdreqzjqso7d2ac7qscrxfl5qfpamdvgy5d6cxejcgzc3hvhurmq");
     664           2 :     goto done;
     665             :   }
     666             : 
     667           0 :  done:
     668           4 :   return ret;
     669             : }
     670             : 
     671             : static int
     672           3 : mock_check_private_dir(const char *dirname, cpd_check_t check,
     673             :                        const char *effective_user)
     674             : {
     675           3 :   (void) dirname;
     676           3 :   (void) check;
     677           3 :   (void) effective_user;
     678             : 
     679           3 :   return 0;
     680             : }
     681             : 
     682             : static smartlist_t *
     683           2 : mock_tor_listdir(const char *dirname)
     684             : {
     685           2 :   smartlist_t *file_list = smartlist_new();
     686             : 
     687           2 :   (void) dirname;
     688             : 
     689           2 :   smartlist_add(file_list, tor_strdup("client1.auth_private"));
     690           2 :   smartlist_add(file_list, tor_strdup("dummy.xxx"));
     691           2 :   smartlist_add(file_list, tor_strdup("client2.auth_private"));
     692             : 
     693           2 :   return file_list;
     694             : }
     695             : 
     696             : static void
     697           1 : test_config_client_authorization(void *arg)
     698             : {
     699           1 :   int ret;
     700           1 :   char *conf = NULL;
     701           1 :   ed25519_public_key_t pk1, pk2;
     702           1 :   digest256map_t *global_map = NULL;
     703           1 :   char *key_dir = tor_strdup(get_fname("auth_keys"));
     704             : 
     705           1 :   (void) arg;
     706             : 
     707           1 :   MOCK(read_file_to_str, mock_read_file_to_str);
     708           1 :   MOCK(tor_listdir, mock_tor_listdir);
     709           1 :   MOCK(check_private_dir, mock_check_private_dir);
     710             : 
     711             : #define conf_fmt \
     712             :   "ClientOnionAuthDir %s\n"
     713             : 
     714           1 :   tor_asprintf(&conf, conf_fmt, key_dir);
     715           1 :   ret = helper_config_client(conf, 0);
     716           1 :   tor_free(conf);
     717           1 :   tt_int_op(ret, OP_EQ, 0);
     718             : 
     719             : #undef conf_fmt
     720             : 
     721           1 :   global_map = get_hs_client_auths_map();
     722           1 :   tt_int_op(digest256map_size(global_map), OP_EQ, 2);
     723             : 
     724           1 :   hs_parse_address("4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad",
     725             :                    &pk1, NULL, NULL);
     726           1 :   hs_parse_address("25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid",
     727             :                    &pk2, NULL, NULL);
     728             : 
     729           1 :   tt_assert(digest256map_get(global_map, pk1.pubkey));
     730           1 :   tt_assert(digest256map_get(global_map, pk2.pubkey));
     731             : 
     732           1 :  done:
     733           1 :   tor_free(key_dir);
     734           1 :   hs_free_all();
     735           1 :   UNMOCK(read_file_to_str);
     736           1 :   UNMOCK(tor_listdir);
     737           1 :   UNMOCK(check_private_dir);
     738           1 : }
     739             : 
     740             : static entry_connection_t *
     741           3 : helper_build_socks_connection(const ed25519_public_key_t *service_pk,
     742             :                               int conn_state)
     743             : {
     744           3 :   entry_connection_t *socks = entry_connection_new(CONN_TYPE_AP, AF_INET);
     745           3 :   ENTRY_TO_EDGE_CONN(socks)->hs_ident = hs_ident_edge_conn_new(service_pk);
     746           3 :   TO_CONN(ENTRY_TO_EDGE_CONN(socks))->state = conn_state;
     747           3 :   smartlist_add(get_connection_array(), &socks->edge_.base_);
     748           3 :   return socks;
     749             : }
     750             : 
     751             : static void
     752           1 : test_desc_has_arrived_cleanup(void *arg)
     753             : {
     754             :   /* The goal of this test is to make sure we clean up everything in between
     755             :    * two descriptors from the same .onion. Because intro points can change
     756             :    * from one descriptor to another, once we received a new descriptor, we
     757             :    * need to cleanup the remaining circuits so they aren't used or selected
     758             :    * when establishing a connection with the newly stored descriptor.
     759             :    *
     760             :    * This test was created because of #27410. */
     761             : 
     762           1 :   int ret;
     763           1 :   char *desc_str = NULL;
     764           1 :   hs_descriptor_t *desc = NULL;
     765           1 :   const hs_descriptor_t *cached_desc;
     766           1 :   ed25519_keypair_t signing_kp;
     767           1 :   entry_connection_t *socks1 = NULL, *socks2 = NULL;
     768           1 :   hs_ident_dir_conn_t hs_dir_ident;
     769           1 :   dir_connection_t *dir_conn = NULL;
     770             : 
     771           1 :   (void) arg;
     772             : 
     773           1 :   hs_init();
     774             : 
     775           1 :   MOCK(networkstatus_get_reasonably_live_consensus,
     776             :        mock_networkstatus_get_reasonably_live_consensus);
     777           1 :   MOCK(connection_mark_unattached_ap_,
     778             :        mock_connection_mark_unattached_ap_);
     779           1 :   MOCK(router_have_minimum_dir_info,
     780             :        mock_router_have_minimum_dir_info_true);
     781             : 
     782             :   /* Set consensus time before our time so the cache lookup can always
     783             :    * validate that the entry is not expired. */
     784           1 :   parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC", &mock_ns.valid_after);
     785           1 :   parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC", &mock_ns.fresh_until);
     786           1 :   parse_rfc1123_time("Sat, 26 Oct 1985 16:00:00 UTC", &mock_ns.valid_until);
     787             : 
     788             :   /* Build a descriptor for a specific .onion. */
     789           1 :   ret = ed25519_keypair_generate(&signing_kp, 0);
     790           1 :   tt_int_op(ret, OP_EQ, 0);
     791           1 :   desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
     792           1 :   tt_assert(desc);
     793           1 :   ret = hs_desc_encode_descriptor(desc, &signing_kp, NULL, &desc_str);
     794           1 :   tt_int_op(ret, OP_EQ, 0);
     795             : 
     796             :   /* Store in the client cache. */
     797           1 :   ret = hs_cache_store_as_client(desc_str, &signing_kp.pubkey);
     798           1 :   tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
     799           1 :   cached_desc = hs_cache_lookup_as_client(&signing_kp.pubkey);
     800           1 :   tt_assert(cached_desc);
     801           1 :   hs_helper_desc_equal(desc, cached_desc);
     802             : 
     803             :   /* Create two SOCKS connection for the same .onion both in the waiting for a
     804             :    * descriptor state. */
     805           1 :   socks1 = helper_build_socks_connection(&signing_kp.pubkey,
     806             :                                          AP_CONN_STATE_RENDDESC_WAIT);
     807           1 :   tt_assert(socks1);
     808           1 :   socks2 = helper_build_socks_connection(&signing_kp.pubkey,
     809             :                                          AP_CONN_STATE_RENDDESC_WAIT);
     810           1 :   tt_assert(socks2);
     811             : 
     812             :   /* Now, we'll make the intro points in the current descriptor unusable so
     813             :    * the hs_client_desc_has_arrived() will take the right code path that we
     814             :    * want to test that is the fetched descriptor has bad intro points. */
     815           5 :   SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
     816             :                         hs_desc_intro_point_t *, ip) {
     817           4 :     hs_cache_client_intro_state_note(&signing_kp.pubkey,
     818           4 :                                      &ip->auth_key_cert->signed_key,
     819             :                                      INTRO_POINT_FAILURE_GENERIC);
     820           4 :   } SMARTLIST_FOREACH_END(ip);
     821             : 
     822             :   /* Simulate that a new descriptor just arrived. We should have both of our
     823             :    * SOCKS connection to be ended with a resolved failed. */
     824           1 :   hs_ident_dir_conn_init(&signing_kp.pubkey,
     825           1 :                          &desc->plaintext_data.blinded_pubkey, &hs_dir_ident);
     826           1 :   dir_conn = dir_connection_new(AF_INET);
     827           1 :   dir_conn->hs_ident = hs_ident_dir_conn_dup(&hs_dir_ident);
     828           1 :   hs_client_dir_fetch_done(dir_conn, "A reason", desc_str, 200);
     829           1 :   connection_free_minimal(TO_CONN(dir_conn));
     830           1 :   tt_int_op(socks1->edge_.end_reason, OP_EQ, END_STREAM_REASON_RESOLVEFAILED);
     831           1 :   tt_int_op(socks2->edge_.end_reason, OP_EQ, END_STREAM_REASON_RESOLVEFAILED);
     832             : 
     833             :   /* Now let say tor cleans up the intro state cache which resets all intro
     834             :    * point failure count. */
     835           1 :   hs_cache_client_intro_state_purge();
     836             : 
     837             :   /* Retrying all SOCKS which should basically do nothing since we don't have
     838             :    * any pending SOCKS connection in AP_CONN_STATE_RENDDESC_WAIT state. */
     839           1 :   retry_all_socks_conn_waiting_for_desc();
     840             : 
     841           1 :  done:
     842           1 :   connection_free_minimal(ENTRY_TO_CONN(socks1));
     843           1 :   connection_free_minimal(ENTRY_TO_CONN(socks2));
     844           1 :   hs_descriptor_free(desc);
     845           1 :   tor_free(desc_str);
     846           1 :   hs_free_all();
     847             : 
     848           1 :   UNMOCK(networkstatus_get_reasonably_live_consensus);
     849           1 :   UNMOCK(connection_mark_unattached_ap_);
     850           1 :   UNMOCK(router_have_minimum_dir_info);
     851           1 : }
     852             : 
     853             : static void
     854           1 : test_close_intro_circuits_new_desc(void *arg)
     855             : {
     856           1 :   int ret;
     857           1 :   ed25519_keypair_t service_kp;
     858           1 :   circuit_t *circ = NULL;
     859           1 :   origin_circuit_t *ocirc = NULL;
     860           1 :   hs_descriptor_t *desc1 = NULL, *desc2 = NULL;
     861             : 
     862           1 :   (void) arg;
     863             : 
     864           1 :   hs_init();
     865             : 
     866             :   /* This is needed because of the client cache expiration timestamp is based
     867             :    * on having a consensus. See cached_client_descriptor_has_expired(). */
     868           1 :   MOCK(networkstatus_get_reasonably_live_consensus,
     869             :        mock_networkstatus_get_reasonably_live_consensus);
     870             : 
     871             :   /* Set consensus time */
     872           1 :   parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
     873             :                            &mock_ns.valid_after);
     874           1 :   parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
     875             :                            &mock_ns.fresh_until);
     876           1 :   parse_rfc1123_time("Sat, 26 Oct 1985 16:00:00 UTC",
     877             :                            &mock_ns.valid_until);
     878             : 
     879             :   /* Generate service keypair */
     880           1 :   tt_int_op(0, OP_EQ, ed25519_keypair_generate(&service_kp, 0));
     881             : 
     882             :   /* Create and add to the global list a dummy client introduction circuits.
     883             :    * We'll then make sure the hs_ident is attached to a dummy descriptor. */
     884           1 :   circ = dummy_origin_circuit_new(0);
     885           1 :   tt_assert(circ);
     886           1 :   circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING;
     887           1 :   ocirc = TO_ORIGIN_CIRCUIT(circ);
     888             : 
     889             :   /* Build a descriptor _without_ client authorization and thus not
     890             :    * decryptable. Make sure the close circuit code path is not triggered. */
     891             :   {
     892           1 :     char *desc_encoded = NULL;
     893           1 :     uint8_t descriptor_cookie[HS_DESC_DESCRIPTOR_COOKIE_LEN];
     894           1 :     curve25519_keypair_t client_kp;
     895           1 :     hs_descriptor_t *desc = NULL;
     896             : 
     897           1 :     tt_int_op(0, OP_EQ, curve25519_keypair_generate(&client_kp, 0));
     898           1 :     crypto_rand((char *) descriptor_cookie, sizeof(descriptor_cookie));
     899             : 
     900           1 :     desc = hs_helper_build_hs_desc_with_client_auth(descriptor_cookie,
     901             :                                                     &client_kp.pubkey,
     902             :                                                     &service_kp);
     903           1 :     tt_assert(desc);
     904           1 :     ret = hs_desc_encode_descriptor(desc, &service_kp, descriptor_cookie,
     905             :                                     &desc_encoded);
     906           1 :     tt_int_op(ret, OP_EQ, 0);
     907             :     /* Associate descriptor intro key with the dummy circuit. */
     908           1 :     const hs_desc_intro_point_t *ip =
     909           1 :       smartlist_get(desc->encrypted_data.intro_points, 0);
     910           1 :     tt_assert(ip);
     911           1 :     ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey);
     912           1 :     ed25519_pubkey_copy(&ocirc->hs_ident->intro_auth_pk,
     913           1 :                         &ip->auth_key_cert->signed_key);
     914           1 :     hs_descriptor_free(desc);
     915           1 :     tt_assert(desc_encoded);
     916             :     /* Put it in the cache. Should not be decrypted since the client
     917             :      * authorization creds were not added to the global map. */
     918           1 :     ret = hs_cache_store_as_client(desc_encoded, &service_kp.pubkey);
     919           1 :     tor_free(desc_encoded);
     920           1 :     tt_int_op(ret, OP_EQ, HS_DESC_DECODE_NEED_CLIENT_AUTH);
     921             : 
     922             :     /* Clean cache with a future timestamp. It will trigger the clean up and
     923             :      * attempt to close the circuit but only if the descriptor is decryptable.
     924             :      * Cache object should be removed and circuit untouched. */
     925           1 :     hs_cache_clean_as_client(mock_ns.valid_after + (60 * 60 * 24));
     926           1 :     tt_assert(!hs_cache_lookup_as_client(&service_kp.pubkey));
     927             : 
     928             :     /* Make sure the circuit still there. */
     929           1 :     tt_assert(circuit_get_next_intro_circ(NULL, true));
     930             :     /* Get rid of the ident, it will be replaced in the next tests. */
     931           1 :     hs_ident_circuit_free(ocirc->hs_ident);
     932             :   }
     933             : 
     934             :   /* Build the first descriptor and cache it. */
     935             :   {
     936           1 :     char *encoded;
     937           1 :     desc1 = hs_helper_build_hs_desc_with_ip(&service_kp);
     938           1 :     tt_assert(desc1);
     939           1 :     ret = hs_desc_encode_descriptor(desc1, &service_kp, NULL, &encoded);
     940           1 :     tt_int_op(ret, OP_EQ, 0);
     941           1 :     tt_assert(encoded);
     942             : 
     943             :     /* Store it */
     944           1 :     ret = hs_cache_store_as_client(encoded, &service_kp.pubkey);
     945           1 :     tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
     946           1 :     tor_free(encoded);
     947           1 :     tt_assert(hs_cache_lookup_as_client(&service_kp.pubkey));
     948             :   }
     949             : 
     950             :   /* We'll pick one introduction point and associate it with the circuit. */
     951             :   {
     952           1 :     const hs_desc_intro_point_t *ip =
     953           1 :       smartlist_get(desc1->encrypted_data.intro_points, 0);
     954           1 :     tt_assert(ip);
     955           1 :     ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey);
     956           1 :     ed25519_pubkey_copy(&ocirc->hs_ident->intro_auth_pk,
     957           1 :                         &ip->auth_key_cert->signed_key);
     958             :   }
     959             : 
     960             :   /* Before we are about to clean up the intro circuits, make sure it is
     961             :    * actually there. */
     962           1 :   tt_assert(circuit_get_next_intro_circ(NULL, true));
     963             : 
     964             :   /* Build the second descriptor for the same service and cache it. */
     965             :   {
     966           1 :     char *encoded;
     967           1 :     desc2 = hs_helper_build_hs_desc_with_ip(&service_kp);
     968           1 :     tt_assert(desc2);
     969           1 :     tt_mem_op(&desc1->plaintext_data.signing_pubkey, OP_EQ,
     970           1 :               &desc2->plaintext_data.signing_pubkey, ED25519_PUBKEY_LEN);
     971             :     /* To replace the existing descriptor, the revision counter needs to be
     972             :      * bigger. */
     973           1 :     desc2->plaintext_data.revision_counter =
     974           1 :       desc1->plaintext_data.revision_counter + 1;
     975             : 
     976           1 :     ret = hs_desc_encode_descriptor(desc2, &service_kp, NULL, &encoded);
     977           1 :     tt_int_op(ret, OP_EQ, 0);
     978           1 :     tt_assert(encoded);
     979             : 
     980           1 :     ret = hs_cache_store_as_client(encoded, &service_kp.pubkey);
     981           1 :     tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
     982           1 :     tor_free(encoded);
     983           1 :     tt_assert(hs_cache_lookup_as_client(&service_kp.pubkey));
     984             :   }
     985             : 
     986             :   /* Once stored, our intro circuit should be closed because it is related to
     987             :    * an old introduction point that doesn't exists anymore. */
     988           1 :   tt_assert(!circuit_get_next_intro_circ(NULL, true));
     989             : 
     990           1 :  done:
     991           1 :   circuit_free(circ);
     992           1 :   hs_descriptor_free(desc1);
     993           1 :   hs_descriptor_free(desc2);
     994           1 :   hs_free_all();
     995           1 :   UNMOCK(networkstatus_get_reasonably_live_consensus);
     996           1 : }
     997             : 
     998             : static void
     999           1 : test_close_intro_circuits_cache_clean(void *arg)
    1000             : {
    1001           1 :   int ret;
    1002           1 :   ed25519_keypair_t service_kp;
    1003           1 :   circuit_t *circ = NULL;
    1004           1 :   origin_circuit_t *ocirc = NULL;
    1005           1 :   hs_descriptor_t *desc1 = NULL;
    1006             : 
    1007           1 :   (void) arg;
    1008             : 
    1009           1 :   hs_init();
    1010             : 
    1011             :   /* This is needed because of the client cache expiration timestamp is based
    1012             :    * on having a consensus. See cached_client_descriptor_has_expired(). */
    1013           1 :   MOCK(networkstatus_get_reasonably_live_consensus,
    1014             :        mock_networkstatus_get_reasonably_live_consensus);
    1015             : 
    1016             :   /* Set consensus time */
    1017           1 :   parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
    1018             :                      &mock_ns.valid_after);
    1019           1 :   parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
    1020             :                      &mock_ns.fresh_until);
    1021           1 :   parse_rfc1123_time("Sat, 26 Oct 1985 16:00:00 UTC",
    1022             :                      &mock_ns.valid_until);
    1023             : 
    1024             :   /* Generate service keypair */
    1025           1 :   tt_int_op(0, OP_EQ, ed25519_keypair_generate(&service_kp, 0));
    1026             : 
    1027             :   /* Create and add to the global list a dummy client introduction circuits.
    1028             :    * We'll then make sure the hs_ident is attached to a dummy descriptor. */
    1029           1 :   circ = dummy_origin_circuit_new(0);
    1030           1 :   tt_assert(circ);
    1031           1 :   circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING;
    1032           1 :   ocirc = TO_ORIGIN_CIRCUIT(circ);
    1033             : 
    1034             :   /* Build the first descriptor and cache it. */
    1035             :   {
    1036           1 :     char *encoded;
    1037           1 :     desc1 = hs_helper_build_hs_desc_with_ip(&service_kp);
    1038           1 :     tt_assert(desc1);
    1039           1 :     ret = hs_desc_encode_descriptor(desc1, &service_kp, NULL, &encoded);
    1040           1 :     tt_int_op(ret, OP_EQ, 0);
    1041           1 :     tt_assert(encoded);
    1042             : 
    1043             :     /* Store it */
    1044           1 :     ret = hs_cache_store_as_client(encoded, &service_kp.pubkey);
    1045           1 :     tt_int_op(ret, OP_EQ, 0);
    1046           1 :     tor_free(encoded);
    1047           1 :     tt_assert(hs_cache_lookup_as_client(&service_kp.pubkey));
    1048             :   }
    1049             : 
    1050             :   /* We'll pick one introduction point and associate it with the circuit. */
    1051             :   {
    1052           1 :     const hs_desc_intro_point_t *ip =
    1053           1 :       smartlist_get(desc1->encrypted_data.intro_points, 0);
    1054           1 :     tt_assert(ip);
    1055           1 :     ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey);
    1056           1 :     ed25519_pubkey_copy(&ocirc->hs_ident->intro_auth_pk,
    1057           1 :                         &ip->auth_key_cert->signed_key);
    1058             :   }
    1059             : 
    1060             :   /* Before we are about to clean up the intro circuits, make sure it is
    1061             :    * actually there. */
    1062           1 :   tt_assert(circuit_get_next_intro_circ(NULL, true));
    1063             : 
    1064             :   /* Cleanup the client cache. The ns valid after time is what decides if the
    1065             :    * descriptor has expired so put it in the future enough (72h) so we are
    1066             :    * sure to always expire. */
    1067           1 :   mock_ns.valid_after = approx_time() + (72 * 24 * 60 * 60);
    1068           1 :   hs_cache_clean_as_client(0);
    1069             : 
    1070             :   /* Once stored, our intro circuit should be closed because it is related to
    1071             :    * an old introduction point that doesn't exists anymore. */
    1072           1 :   tt_assert(!circuit_get_next_intro_circ(NULL, true));
    1073             : 
    1074           1 :  done:
    1075           1 :   circuit_free(circ);
    1076           1 :   hs_descriptor_free(desc1);
    1077           1 :   hs_free_all();
    1078           1 :   UNMOCK(networkstatus_get_reasonably_live_consensus);
    1079           1 : }
    1080             : 
    1081             : static void
    1082           1 : test_socks_hs_errors(void *arg)
    1083             : {
    1084           1 :   int ret;
    1085           1 :   char digest[DIGEST_LEN];
    1086           1 :   char *desc_encoded = NULL;
    1087           1 :   circuit_t *circ = NULL;
    1088           1 :   origin_circuit_t *ocirc = NULL;
    1089           1 :   tor_addr_t addr;
    1090           1 :   ed25519_keypair_t service_kp;
    1091           1 :   ed25519_keypair_t signing_kp;
    1092           1 :   entry_connection_t *socks_conn = NULL;
    1093           1 :   dir_connection_t *dir_conn = NULL;
    1094           1 :   hs_descriptor_t *desc = NULL;
    1095           1 :   uint8_t descriptor_cookie[HS_DESC_DESCRIPTOR_COOKIE_LEN];
    1096             : 
    1097           1 :   (void) arg;
    1098             : 
    1099           1 :   MOCK(networkstatus_get_reasonably_live_consensus,
    1100             :        mock_networkstatus_get_reasonably_live_consensus);
    1101           1 :   MOCK(connection_mark_unattached_ap_,
    1102             :        mock_connection_mark_unattached_ap_no_close);
    1103           1 :   MOCK(read_file_to_str, mock_read_file_to_str);
    1104           1 :   MOCK(tor_listdir, mock_tor_listdir);
    1105           1 :   MOCK(check_private_dir, mock_check_private_dir);
    1106             : 
    1107             :     /* Set consensus time */
    1108           1 :   parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
    1109             :                            &mock_ns.valid_after);
    1110           1 :   parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
    1111             :                            &mock_ns.fresh_until);
    1112           1 :   parse_rfc1123_time("Sat, 26 Oct 1985 16:00:00 UTC",
    1113             :                            &mock_ns.valid_until);
    1114             : 
    1115           1 :   hs_init();
    1116             : 
    1117           1 :   ret = ed25519_keypair_generate(&service_kp, 0);
    1118           1 :   tt_int_op(ret, OP_EQ, 0);
    1119           1 :   ret = ed25519_keypair_generate(&signing_kp, 0);
    1120           1 :   tt_int_op(ret, OP_EQ, 0);
    1121             : 
    1122           1 :   socks_conn = helper_build_socks_connection(&service_kp.pubkey,
    1123             :                                              AP_CONN_STATE_RENDDESC_WAIT);
    1124           1 :   tt_assert(socks_conn);
    1125             : 
    1126             :   /* Create directory connection. */
    1127           1 :   dir_conn = dir_connection_new(AF_INET);
    1128           1 :   dir_conn->hs_ident = tor_malloc_zero(sizeof(hs_ident_dir_conn_t));
    1129           1 :   TO_CONN(dir_conn)->purpose = DIR_PURPOSE_FETCH_HSDESC;
    1130           1 :   ed25519_pubkey_copy(&dir_conn->hs_ident->identity_pk, &service_kp.pubkey);
    1131             : 
    1132             :   /* Encode descriptor so we can decode it. */
    1133           1 :   desc = hs_helper_build_hs_desc_with_ip(&service_kp);
    1134           1 :   tt_assert(desc);
    1135             : 
    1136             :   /* Before testing the client authentication error code, encode the
    1137             :    * descriptor with no client auth. */
    1138           1 :   ret = hs_desc_encode_descriptor(desc, &service_kp, NULL, &desc_encoded);
    1139           1 :   tt_int_op(ret, OP_EQ, 0);
    1140           1 :   tt_assert(desc_encoded);
    1141             : 
    1142             :   /*
    1143             :    * Test the introduction failure codes (X'F2' and X'F7')
    1144             :    */
    1145             : 
    1146             :   /* First, we have to put all the IPs in the failure cache. */
    1147           5 :   SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
    1148             :                           hs_desc_intro_point_t *, ip) {
    1149           4 :     hs_cache_client_intro_state_note(&service_kp.pubkey,
    1150           4 :                                      &ip->auth_key_cert->signed_key,
    1151             :                                      INTRO_POINT_FAILURE_GENERIC);
    1152           4 :   } SMARTLIST_FOREACH_END(ip);
    1153             : 
    1154           1 :   hs_client_dir_fetch_done(dir_conn, "Reason", desc_encoded, 200);
    1155           1 :   tt_int_op(socks_conn->socks_request->socks_extended_error_code, OP_EQ,
    1156             :             SOCKS5_HS_INTRO_FAILED);
    1157             : 
    1158             :   /* Purge client cache of the descriptor so we can go again. */
    1159           1 :   hs_cache_purge_as_client();
    1160             : 
    1161             :   /* Second, set all failures to be time outs. */
    1162           5 :   SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
    1163             :                           hs_desc_intro_point_t *, ip) {
    1164           4 :     hs_cache_client_intro_state_note(&service_kp.pubkey,
    1165           4 :                                      &ip->auth_key_cert->signed_key,
    1166             :                                      INTRO_POINT_FAILURE_TIMEOUT);
    1167           4 :   } SMARTLIST_FOREACH_END(ip);
    1168             : 
    1169           1 :   hs_client_dir_fetch_done(dir_conn, "Reason", desc_encoded, 200);
    1170           1 :   tt_int_op(socks_conn->socks_request->socks_extended_error_code, OP_EQ,
    1171             :             SOCKS5_HS_INTRO_TIMEDOUT);
    1172             : 
    1173             :   /* Purge client cache of the descriptor so we can go again. */
    1174           1 :   hs_cache_purge_as_client();
    1175             : 
    1176             :   /*
    1177             :    * Test the rendezvous failure codes (X'F3')
    1178             :    */
    1179             : 
    1180           1 :   circ = dummy_origin_circuit_new(0);
    1181           1 :   tt_assert(circ);
    1182           1 :   circ->purpose = CIRCUIT_PURPOSE_C_REND_READY;
    1183           1 :   ocirc = TO_ORIGIN_CIRCUIT(circ);
    1184           1 :   ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey);
    1185           1 :   ocirc->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
    1186             :   /* Code path will log this exit so build it. */
    1187           1 :   ocirc->build_state->chosen_exit = extend_info_new("TestNickname", digest,
    1188             :                                                     NULL, NULL, NULL, &addr,
    1189             :                                                     4242);
    1190             :   /* Attach socks connection to this rendezvous circuit. */
    1191           1 :   ocirc->p_streams = ENTRY_TO_EDGE_CONN(socks_conn);
    1192             :   /* Trigger the rendezvous failure. Timeout the circuit and free. */
    1193           1 :   circuit_mark_for_close(circ, END_CIRC_REASON_TIMEOUT);
    1194             : 
    1195           1 :   tt_int_op(socks_conn->socks_request->socks_extended_error_code, OP_EQ,
    1196             :             SOCKS5_HS_REND_FAILED);
    1197             : 
    1198             :   /*
    1199             :    * Test client authorization codes.
    1200             :    */
    1201             : 
    1202           1 :   tor_free(desc_encoded);
    1203           1 :   crypto_rand((char *) descriptor_cookie, sizeof(descriptor_cookie));
    1204           1 :   ret = hs_desc_encode_descriptor(desc, &service_kp, descriptor_cookie,
    1205             :                                   &desc_encoded);
    1206           1 :   tt_int_op(ret, OP_EQ, 0);
    1207           1 :   tt_assert(desc_encoded);
    1208             : 
    1209             :   /* Try decoding. Point this to an existing descriptor. The following should
    1210             :    * fail thus the desc_out should be set to NULL. */
    1211           1 :   hs_descriptor_t *desc_out = desc;
    1212           1 :   ret = hs_client_decode_descriptor(desc_encoded, &service_kp.pubkey,
    1213             :                                     &desc_out);
    1214           1 :   tt_int_op(ret, OP_EQ, HS_DESC_DECODE_NEED_CLIENT_AUTH);
    1215           1 :   tt_assert(desc_out == NULL);
    1216             : 
    1217             :   /* The caching will fail to decrypt because the descriptor_cookie used above
    1218             :    * is not known to the HS subsystem. This will lead to a missing client
    1219             :    * auth. */
    1220           1 :   hs_client_dir_fetch_done(dir_conn, "Reason", desc_encoded, 200);
    1221             : 
    1222           1 :   tt_int_op(socks_conn->socks_request->socks_extended_error_code, OP_EQ,
    1223             :             SOCKS5_HS_MISSING_CLIENT_AUTH);
    1224             : 
    1225             :   /* Add in the global client auth list bad creds for this service. */
    1226           1 :   helper_add_random_client_auth(&service_kp.pubkey);
    1227             : 
    1228           1 :   ret = hs_client_decode_descriptor(desc_encoded, &service_kp.pubkey,
    1229             :                                     &desc_out);
    1230           1 :   tt_int_op(ret, OP_EQ, HS_DESC_DECODE_BAD_CLIENT_AUTH);
    1231           1 :   tt_assert(desc_out == NULL);
    1232             : 
    1233             :   /* Simmulate a fetch done again. This should replace the cached descriptor
    1234             :    * and signal a bad client authorization. */
    1235           1 :   hs_client_dir_fetch_done(dir_conn, "Reason", desc_encoded, 200);
    1236           1 :   tt_int_op(socks_conn->socks_request->socks_extended_error_code, OP_EQ,
    1237             :             SOCKS5_HS_BAD_CLIENT_AUTH);
    1238             : 
    1239           1 :  done:
    1240           1 :   connection_free_minimal(ENTRY_TO_CONN(socks_conn));
    1241           1 :   connection_free_minimal(TO_CONN(dir_conn));
    1242           1 :   hs_descriptor_free(desc);
    1243           1 :   tor_free(desc_encoded);
    1244           1 :   circuit_free(circ);
    1245             : 
    1246           1 :   hs_free_all();
    1247             : 
    1248           1 :   UNMOCK(networkstatus_get_reasonably_live_consensus);
    1249           1 :   UNMOCK(connection_mark_unattached_ap_);
    1250           1 :   UNMOCK(read_file_to_str);
    1251           1 :   UNMOCK(tor_listdir);
    1252           1 :   UNMOCK(check_private_dir);
    1253           1 : }
    1254             : 
    1255             : static void
    1256           1 : test_close_intro_circuit_failure(void *arg)
    1257             : {
    1258           1 :   char digest[DIGEST_LEN];
    1259           1 :   circuit_t *circ = NULL;
    1260           1 :   ed25519_keypair_t service_kp, intro_kp;
    1261           1 :   origin_circuit_t *ocirc = NULL;
    1262           1 :   tor_addr_t addr;
    1263           1 :   const hs_cache_intro_state_t *entry;
    1264             : 
    1265           1 :   (void) arg;
    1266             : 
    1267           1 :   hs_init();
    1268             : 
    1269             :   /* Generate service keypair */
    1270           1 :   tt_int_op(0, OP_EQ, ed25519_keypair_generate(&service_kp, 0));
    1271           1 :   tt_int_op(0, OP_EQ, ed25519_keypair_generate(&intro_kp, 0));
    1272             : 
    1273             :   /* Create and add to the global list a dummy client introduction circuit at
    1274             :    * the ACK WAIT state. */
    1275           1 :   circ = dummy_origin_circuit_new(0);
    1276           1 :   tt_assert(circ);
    1277           1 :   circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT;
    1278           1 :   ocirc = TO_ORIGIN_CIRCUIT(circ);
    1279           1 :   ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey);
    1280           1 :   ocirc->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
    1281             :   /* Code path will log this exit so build it. */
    1282           1 :   ocirc->build_state->chosen_exit = extend_info_new("TestNickname", digest,
    1283             :                                                     NULL, NULL, NULL, &addr,
    1284             :                                                     4242);
    1285           1 :   ed25519_pubkey_copy(&ocirc->hs_ident->intro_auth_pk, &intro_kp.pubkey);
    1286             : 
    1287             :   /* We'll make for close the circuit for a timeout failure. It should _NOT_
    1288             :    * end up in the failure cache just yet. We do that on free() only. */
    1289           1 :   circuit_mark_for_close(circ, END_CIRC_REASON_TIMEOUT);
    1290           1 :   tt_assert(!hs_cache_client_intro_state_find(&service_kp.pubkey,
    1291             :                                               &intro_kp.pubkey));
    1292             :   /* Time to free. It should get removed. */
    1293           1 :   circuit_free(circ);
    1294           1 :   entry = hs_cache_client_intro_state_find(&service_kp.pubkey,
    1295             :                                            &intro_kp.pubkey);
    1296           1 :   tt_assert(entry);
    1297           1 :   tt_uint_op(entry->timed_out, OP_EQ, 1);
    1298           1 :   hs_cache_client_intro_state_purge();
    1299             : 
    1300             :   /* Again, create and add to the global list a dummy client introduction
    1301             :    * circuit at the INTRODUCING state. */
    1302           1 :   circ = dummy_origin_circuit_new(0);
    1303           1 :   tt_assert(circ);
    1304           1 :   circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING;
    1305           1 :   ocirc = TO_ORIGIN_CIRCUIT(circ);
    1306           1 :   ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey);
    1307           1 :   ocirc->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
    1308             :   /* Code path will log this exit so build it. */
    1309           1 :   ocirc->build_state->chosen_exit = extend_info_new("TestNickname", digest,
    1310             :                                                     NULL, NULL, NULL, &addr,
    1311             :                                                     4242);
    1312           1 :   ed25519_pubkey_copy(&ocirc->hs_ident->intro_auth_pk, &intro_kp.pubkey);
    1313             : 
    1314             :   /* On free, we should get an unreachable failure. */
    1315           1 :   circuit_free(circ);
    1316           1 :   entry = hs_cache_client_intro_state_find(&service_kp.pubkey,
    1317             :                                            &intro_kp.pubkey);
    1318           1 :   tt_assert(entry);
    1319           1 :   tt_uint_op(entry->unreachable_count, OP_EQ, 1);
    1320           1 :   hs_cache_client_intro_state_purge();
    1321             : 
    1322             :   /* Again, create and add to the global list a dummy client introduction
    1323             :    * circuit at the INTRODUCING state but we'll close it for timeout. It
    1324             :    * should not be noted as a timeout failure. */
    1325           1 :   circ = dummy_origin_circuit_new(0);
    1326           1 :   tt_assert(circ);
    1327           1 :   circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING;
    1328           1 :   ocirc = TO_ORIGIN_CIRCUIT(circ);
    1329           1 :   ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey);
    1330           1 :   ocirc->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
    1331             :   /* Code path will log this exit so build it. */
    1332           1 :   ocirc->build_state->chosen_exit = extend_info_new("TestNickname", digest,
    1333             :                                                     NULL, NULL, NULL, &addr,
    1334             :                                                     4242);
    1335           1 :   ed25519_pubkey_copy(&ocirc->hs_ident->intro_auth_pk, &intro_kp.pubkey);
    1336             : 
    1337           1 :   circuit_mark_for_close(circ, END_CIRC_REASON_TIMEOUT);
    1338           1 :   circuit_free(circ);
    1339           1 :   tt_assert(!hs_cache_client_intro_state_find(&service_kp.pubkey,
    1340             :                                               &intro_kp.pubkey));
    1341             : 
    1342             :   /* Again, create and add to the global list a dummy client introduction
    1343             :    * circuit at the INTRODUCING state but without a chosen_exit. In theory, it
    1344             :    * can not happen but we'll make sure it doesn't end up in the failure cache
    1345             :    * anyway. */
    1346           1 :   circ = dummy_origin_circuit_new(0);
    1347           1 :   tt_assert(circ);
    1348           1 :   circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCING;
    1349           1 :   ocirc = TO_ORIGIN_CIRCUIT(circ);
    1350           1 :   ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey);
    1351           1 :   ed25519_pubkey_copy(&ocirc->hs_ident->intro_auth_pk, &intro_kp.pubkey);
    1352             : 
    1353           1 :   circuit_free(circ);
    1354           1 :   tt_assert(!hs_cache_client_intro_state_find(&service_kp.pubkey,
    1355             :                                               &intro_kp.pubkey));
    1356             : 
    1357           1 :  done:
    1358           1 :   circuit_free(circ);
    1359           1 :   hs_free_all();
    1360           1 : }
    1361             : 
    1362             : static void
    1363           1 : test_purge_ephemeral_client_auth(void *arg)
    1364             : {
    1365           1 :   ed25519_keypair_t service_kp;
    1366           1 :   hs_client_service_authorization_t *auth = NULL;
    1367           1 :   hs_client_register_auth_status_t status;
    1368             : 
    1369           1 :   (void) arg;
    1370             : 
    1371             :   /* We will try to write on disk client credentials. */
    1372           1 :   MOCK(check_private_dir, mock_check_private_dir);
    1373           1 :   MOCK(get_options, mock_get_options);
    1374           1 :   MOCK(write_str_to_file, mock_write_str_to_file);
    1375             : 
    1376             :   /* Bogus directory so when we try to write the permanent client
    1377             :    * authorization data to disk, we don't fail. See
    1378             :    * store_permanent_client_auth_credentials() for more details. */
    1379           1 :   mocked_options.ClientOnionAuthDir = tor_strdup("auth_dir");
    1380             : 
    1381           1 :   hs_init();
    1382             : 
    1383             :   /* Generate service keypair */
    1384           1 :   tt_int_op(0, OP_EQ, ed25519_keypair_generate(&service_kp, 0));
    1385             : 
    1386             :   /* Generate a client authorization object. */
    1387           1 :   auth = tor_malloc_zero(sizeof(hs_client_service_authorization_t));
    1388             : 
    1389             :   /* Set it up. No flags meaning it is ephemeral. */
    1390           1 :   curve25519_secret_key_generate(&auth->enc_seckey, 0);
    1391           1 :   hs_build_address(&service_kp.pubkey, HS_VERSION_THREE, auth->onion_address);
    1392           1 :   auth->flags = 0;
    1393             : 
    1394             :   /* Confirm that there is nothing in the client auth map. It is unallocated
    1395             :    * until we add the first entry. */
    1396           1 :   tt_assert(!get_hs_client_auths_map());
    1397             : 
    1398             :   /* Add an entry to the client auth list. We loose ownership of the auth
    1399             :    * object so nullify it. */
    1400           1 :   status = hs_client_register_auth_credentials(auth);
    1401           1 :   auth = NULL;
    1402           1 :   tt_int_op(status, OP_EQ, REGISTER_SUCCESS);
    1403             : 
    1404             :   /* We should have the entry now. */
    1405           1 :   digest256map_t *client_auths = get_hs_client_auths_map();
    1406           1 :   tt_assert(client_auths);
    1407           1 :   tt_int_op(digest256map_size(client_auths), OP_EQ, 1);
    1408             : 
    1409             :   /* Purge the cache that should remove all ephemeral values. */
    1410           1 :   purge_ephemeral_client_auth();
    1411           1 :   tt_int_op(digest256map_size(client_auths), OP_EQ, 0);
    1412             : 
    1413             :   /* Now add a new authorization object but permanent. */
    1414             :   /* Generate a client authorization object. */
    1415           1 :   auth = tor_malloc_zero(sizeof(hs_client_service_authorization_t));
    1416           1 :   curve25519_secret_key_generate(&auth->enc_seckey, 0);
    1417           1 :   hs_build_address(&service_kp.pubkey, HS_VERSION_THREE, auth->onion_address);
    1418           1 :   auth->flags = CLIENT_AUTH_FLAG_IS_PERMANENT;
    1419             : 
    1420             :   /* Add an entry to the client auth list. We loose ownership of the auth
    1421             :    * object so nullify it. */
    1422           1 :   status = hs_client_register_auth_credentials(auth);
    1423           1 :   auth = NULL;
    1424           1 :   tt_int_op(status, OP_EQ, REGISTER_SUCCESS);
    1425           1 :   tt_int_op(digest256map_size(client_auths), OP_EQ, 1);
    1426             : 
    1427             :   /* Purge again, the entry should still be there. */
    1428           1 :   purge_ephemeral_client_auth();
    1429           1 :   tt_int_op(digest256map_size(client_auths), OP_EQ, 1);
    1430             : 
    1431           1 :  done:
    1432           1 :   client_service_authorization_free(auth);
    1433           1 :   hs_free_all();
    1434           1 :   tor_free(mocked_options.ClientOnionAuthDir);
    1435             : 
    1436           1 :   UNMOCK(check_private_dir);
    1437           1 :   UNMOCK(get_options);
    1438           1 :   UNMOCK(write_str_to_file);
    1439           1 : }
    1440             : 
    1441             : struct testcase_t hs_client_tests[] = {
    1442             :   { "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup,
    1443             :     TT_FORK, NULL, NULL },
    1444             :   { "client_pick_intro", test_client_pick_intro,
    1445             :     TT_FORK, NULL, NULL },
    1446             :   { "descriptor_fetch", test_descriptor_fetch,
    1447             :     TT_FORK, NULL, NULL },
    1448             :   { "auth_key_filename_is_valid", test_auth_key_filename_is_valid, TT_FORK,
    1449             :     NULL, NULL },
    1450             :   { "parse_auth_file_content", test_parse_auth_file_content, TT_FORK,
    1451             :     NULL, NULL },
    1452             :   { "config_client_authorization", test_config_client_authorization,
    1453             :     TT_FORK, NULL, NULL },
    1454             :   { "desc_has_arrived_cleanup", test_desc_has_arrived_cleanup,
    1455             :     TT_FORK, NULL, NULL },
    1456             :   { "close_intro_circuit_failure", test_close_intro_circuit_failure,
    1457             :     TT_FORK, NULL, NULL },
    1458             :   { "close_intro_circuits_new_desc", test_close_intro_circuits_new_desc,
    1459             :     TT_FORK, NULL, NULL },
    1460             :   { "close_intro_circuits_cache_clean", test_close_intro_circuits_cache_clean,
    1461             :     TT_FORK, NULL, NULL },
    1462             : 
    1463             :   /* SOCKS5 Extended Error Code. */
    1464             :   { "socks_hs_errors", test_socks_hs_errors, TT_FORK, NULL, NULL },
    1465             : 
    1466             :   /* Client authorization. */
    1467             :   { "purge_ephemeral_client_auth", test_purge_ephemeral_client_auth, TT_FORK,
    1468             :     NULL, NULL },
    1469             : 
    1470             :   END_OF_TESTCASES
    1471             : };

Generated by: LCOV version 1.14