aboutsummaryrefslogtreecommitdiff
path: root/src/test/test_hs_client.c
blob: f834a5c821f424c16e25227b686a73d6eccb807a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
/* Copyright (c) 2016-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */

/**
 * \file test_hs_client.c
 * \brief Test prop224 HS client functionality.
 */

#define CRYPTO_PRIVATE
#define MAIN_PRIVATE
#define TOR_CHANNEL_INTERNAL_
#define CIRCUITBUILD_PRIVATE
#define CIRCUITLIST_PRIVATE
#define CONNECTION_PRIVATE

#include "test.h"
#include "test_helpers.h"
#include "log_test_helpers.h"
#include "rend_test_helpers.h"

#include "config.h"
#include "crypto.h"
#include "channeltls.h"

#include "hs_circuit.h"
#include "hs_ident.h"
#include "circuitlist.h"
#include "circuitbuild.h"
#include "connection.h"
#include "connection_edge.h"

static int
mock_connection_ap_handshake_send_begin(entry_connection_t *ap_conn)
{
  (void) ap_conn;
  return 0;
}

/* Test helper function: Setup a circuit and a stream with the same hidden
 * service destination, and put them in <b>circ_out</b> and
 * <b>conn_out</b>. Make the stream wait for circuits to be established to the
 * hidden service. */
static int
helper_get_circ_and_stream_for_test(origin_circuit_t **circ_out,
                                    connection_t **conn_out,
                                    int is_legacy)
{
  int retval;
  channel_tls_t *n_chan=NULL;
  rend_data_t *conn_rend_data = NULL;
  origin_circuit_t *or_circ = NULL;
  connection_t *conn = NULL;
  ed25519_public_key_t service_pk;

  /* Make a dummy connection stream and make it wait for our circuit */
  conn = test_conn_get_connection(AP_CONN_STATE_CIRCUIT_WAIT,
                                  CONN_TYPE_AP /* ??? */,
                                  0);
  if (is_legacy) {
    /* Legacy: Setup rend_data of stream */
    char service_id[REND_SERVICE_ID_LEN_BASE32+1] = {0};
    TO_EDGE_CONN(conn)->rend_data = mock_rend_data(service_id);
    conn_rend_data = TO_EDGE_CONN(conn)->rend_data;
  } else {
    /* prop224: Setup hs conn identifier on the stream */
    ed25519_secret_key_t sk;
    tt_int_op(0, OP_EQ, ed25519_secret_key_generate(&sk, 0));
    tt_int_op(0, OP_EQ, ed25519_public_key_generate(&service_pk, &sk));

    /* Setup hs_conn_identifier of stream */
    TO_EDGE_CONN(conn)->hs_ident = hs_ident_edge_conn_new(&service_pk);
  }

  /* Make it wait for circuit */
  connection_ap_mark_as_pending_circuit(TO_ENTRY_CONN(conn));

  /* This is needed to silence a BUG warning from
     connection_edge_update_circuit_isolation() */
  TO_ENTRY_CONN(conn)->original_dest_address =
    tor_strdup(TO_ENTRY_CONN(conn)->socks_request->address);

  /****************************************************/

  /* Now make dummy circuit */
  or_circ = origin_circuit_new();

  or_circ->base_.purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED;

  or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
  or_circ->build_state->is_internal = 1;

  if (is_legacy) {
    /* Legacy: Setup rend data and final cpath */
    or_circ->build_state->pending_final_cpath =
      tor_malloc_zero(sizeof(crypt_path_t));
    or_circ->build_state->pending_final_cpath->magic = CRYPT_PATH_MAGIC;
    or_circ->build_state->pending_final_cpath->rend_dh_handshake_state =
      crypto_dh_new(DH_TYPE_REND);
    tt_assert(
         or_circ->build_state->pending_final_cpath->rend_dh_handshake_state);
    retval = crypto_dh_generate_public(
           or_circ->build_state->pending_final_cpath->rend_dh_handshake_state);
    tt_int_op(retval, OP_EQ, 0);
    or_circ->rend_data = rend_data_dup(conn_rend_data);
  } else {
    /* prop224: Setup hs ident on the circuit */
    or_circ->hs_ident = hs_ident_circuit_new(&service_pk,
                                             HS_IDENT_CIRCUIT_RENDEZVOUS);
  }

  TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN;

  /* fake n_chan */
  n_chan = tor_malloc_zero(sizeof(channel_tls_t));
  n_chan->base_.global_identifier = 1;
  or_circ->base_.n_chan = &(n_chan->base_);

  *circ_out = or_circ;
  *conn_out = conn;

  return 0;

 done:
  /* something failed */
  return -1;
}

/* Test: Ensure that setting up legacy e2e rendezvous circuits works
 * correctly. */
static void
test_e2e_rend_circuit_setup_legacy(void *arg)
{
  ssize_t retval;
  origin_circuit_t *or_circ = NULL;
  connection_t *conn = NULL;

  (void) arg;

  /** In this test we create a v2 legacy HS stream and a circuit with the same
   *  hidden service destination. We make the stream wait for circuits to be
   *  established to the hidden service, and then we complete the circuit using
   *  the hs_circuit_setup_e2e_rend_circ_legacy_client() function. We then
   *  check that the end-to-end cpath was setup correctly and that the stream
   *  was attached to the circuit as expected. */

  MOCK(connection_ap_handshake_send_begin,
       mock_connection_ap_handshake_send_begin);

  /* Setup */
  retval = helper_get_circ_and_stream_for_test( &or_circ, &conn, 1);
  tt_int_op(retval, OP_EQ, 0);
  tt_assert(or_circ);
  tt_assert(conn);

  /* Check number of hops */
  retval = cpath_get_n_hops(&or_circ->cpath);
  tt_int_op(retval, OP_EQ, 0);

  /* Check that our stream is not attached on any circuits */
  tt_assert(!TO_EDGE_CONN(conn)->on_circuit);

  /********************************************** */

  /* Make a good RENDEZVOUS1 cell body because it needs to pass key exchange
   * digest verification... */
  uint8_t rend_cell_body[DH_KEY_LEN+DIGEST_LEN] = {2};
  {
    char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN];
    crypto_dh_t *dh_state =
      or_circ->build_state->pending_final_cpath->rend_dh_handshake_state;
    /* compute and overwrite digest of cell body with the right value */
    retval = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh_state,
                                      (char*)rend_cell_body, DH_KEY_LEN,
                                      keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN);
    tt_int_op(retval, OP_GT, 0);
    memcpy(rend_cell_body+DH_KEY_LEN, keys, DIGEST_LEN);
  }

  /* Setup the circuit */
  retval = hs_circuit_setup_e2e_rend_circ_legacy_client(or_circ,
                                                        rend_cell_body);
  tt_int_op(retval, OP_EQ, 0);

  /**********************************************/

  /* See that a hop was added to the circuit's cpath */
  retval = cpath_get_n_hops(&or_circ->cpath);
  tt_int_op(retval, OP_EQ, 1);

  /* Check the digest algo */
  tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->f_digest),
            OP_EQ, DIGEST_SHA1);
  tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->b_digest),
            OP_EQ, DIGEST_SHA1);
  tt_assert(or_circ->cpath->f_crypto);
  tt_assert(or_circ->cpath->b_crypto);

  /* Ensure that circ purpose was changed */
  tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED);

  /* Test that stream got attached */
  tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ));

 done:
  connection_free_(conn);
  tor_free(TO_CIRCUIT(or_circ)->n_chan);
  circuit_free(TO_CIRCUIT(or_circ));
}

/* Test: Ensure that setting up v3 rendezvous circuits works correctly. */
static void
test_e2e_rend_circuit_setup(void *arg)
{
  uint8_t ntor_key_seed[DIGEST256_LEN] = {0};
  origin_circuit_t *or_circ;
  int retval;
  connection_t *conn = NULL;

  (void) arg;

  /** In this test we create a prop224 v3 HS stream and a circuit with the same
   *  hidden service destination. We make the stream wait for circuits to be
   *  established to the hidden service, and then we complete the circuit using
   *  the hs_circuit_setup_e2e_rend_circ() function. We then check that the
   *  end-to-end cpath was setup correctly and that the stream was attached to
   *  the circuit as expected. */

  MOCK(connection_ap_handshake_send_begin,
       mock_connection_ap_handshake_send_begin);

  /* Setup */
  retval = helper_get_circ_and_stream_for_test( &or_circ, &conn, 0);
  tt_int_op(retval, OP_EQ, 0);
  tt_assert(or_circ);
  tt_assert(conn);

  /* Check number of hops: There should be no hops yet to this circ */
  retval = cpath_get_n_hops(&or_circ->cpath);
  tt_int_op(retval, OP_EQ, 0);
  tt_assert(!or_circ->cpath);

  /* Check that our stream is not attached on any circuits */
  tt_assert(!TO_EDGE_CONN(conn)->on_circuit);

  /**********************************************/

  /* Setup the circuit */
  retval = hs_circuit_setup_e2e_rend_circ(or_circ,
                                          ntor_key_seed, sizeof(ntor_key_seed),
                                          0);
  tt_int_op(retval, OP_EQ, 0);

  /**********************************************/

  /* See that a hop was added to the circuit's cpath */
  retval = cpath_get_n_hops(&or_circ->cpath);
  tt_int_op(retval, OP_EQ, 1);

  /* Check that the crypt path has prop224 algorithm parameters */
  tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->f_digest),
            OP_EQ, DIGEST_SHA3_256);
  tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->b_digest),
            OP_EQ, DIGEST_SHA3_256);
  tt_assert(or_circ->cpath->f_crypto);
  tt_assert(or_circ->cpath->b_crypto);

  /* Ensure that circ purpose was changed */
  tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED);

  /* Test that stream got attached */
  tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ));

 done:
  connection_free_(conn);
  tor_free(TO_CIRCUIT(or_circ)->n_chan);
  circuit_free(TO_CIRCUIT(or_circ));
}

struct testcase_t hs_client_tests[] = {
  { "e2e_rend_circuit_setup_legacy", test_e2e_rend_circuit_setup_legacy,
    TT_FORK, NULL, NULL },
  { "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup,
    TT_FORK, NULL, NULL },
  END_OF_TESTCASES
};