aboutsummaryrefslogtreecommitdiff
path: root/src/test/test_hs_cell.c
blob: 5406339276ff36dc487d3e7685d4c5ffa08a9bab (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
/* Copyright (c) 2017-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */

/**
 * \file test_hs_cell.c
 * \brief Test hidden service cell functionality.
 */

#define HS_INTROPOINT_PRIVATE
#define HS_SERVICE_PRIVATE

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

#include "lib/crypt_ops/crypto_ed25519.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "feature/hs/hs_cell.h"
#include "feature/hs/hs_intropoint.h"
#include "feature/hs/hs_service.h"

/* Trunnel. */
#include "trunnel/hs/cell_common.h"
#include "trunnel/hs/cell_establish_intro.h"

/** We simulate the creation of an outgoing ESTABLISH_INTRO cell, and then we
 *  parse it from the receiver side. */
static void
test_gen_establish_intro_cell(void *arg)
{
  (void) arg;
  ssize_t ret;
  char circ_nonce[DIGEST_LEN] = {0};
  uint8_t buf[RELAY_PAYLOAD_SIZE];
  trn_cell_establish_intro_t *cell_in = NULL;

  crypto_rand(circ_nonce, sizeof(circ_nonce));

  /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
     attempt to parse it. */
  {
    hs_service_config_t config;
    memset(&config, 0, sizeof(config));
    /* We only need the auth key pair here. */
    hs_service_intro_point_t *ip = service_intro_point_new(NULL);
    /* Auth key pair is generated in the constructor so we are all set for
     * using this IP object. */
    ret = hs_cell_build_establish_intro(circ_nonce, &config, ip, buf);
    service_intro_point_free(ip);
    tt_u64_op(ret, OP_GT, 0);
  }

  /* Check the contents of the cell */
  {
    /* First byte is the auth key type: make sure its correct */
    tt_int_op(buf[0], OP_EQ, TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_ED25519);
    /* Next two bytes is auth key len */
    tt_int_op(ntohs(get_uint16(buf+1)), OP_EQ, ED25519_PUBKEY_LEN);
    /* Skip to the number of extensions: no extensions */
    tt_int_op(buf[35], OP_EQ, 0);
    /* Skip to the sig len. Make sure it's the size of an ed25519 sig */
    tt_int_op(ntohs(get_uint16(buf+35+1+32)), OP_EQ, ED25519_SIG_LEN);
  }

  /* Parse it as the receiver */
  {
    ret = trn_cell_establish_intro_parse(&cell_in, buf, sizeof(buf));
    tt_u64_op(ret, OP_GT, 0);

    ret = verify_establish_intro_cell(cell_in,
                                      (const uint8_t *) circ_nonce,
                                      sizeof(circ_nonce));
    tt_u64_op(ret, OP_EQ, 0);
  }

 done:
  trn_cell_establish_intro_free(cell_in);
}

/* Mocked ed25519_sign_prefixed() function that always fails :) */
static int
mock_ed25519_sign_prefixed(ed25519_signature_t *signature_out,
                           const uint8_t *msg, size_t msg_len,
                           const char *prefix_str,
                           const ed25519_keypair_t *keypair) {
  (void) signature_out;
  (void) msg;
  (void) msg_len;
  (void) prefix_str;
  (void) keypair;
  return -1;
}

/** We simulate a failure to create an ESTABLISH_INTRO cell */
static void
test_gen_establish_intro_cell_bad(void *arg)
{
  (void) arg;
  ssize_t cell_len = 0;
  trn_cell_establish_intro_t *cell = NULL;
  char circ_nonce[DIGEST_LEN] = {0};
  hs_service_intro_point_t *ip = NULL;
  hs_service_config_t config;

  memset(&config, 0, sizeof(config));

  MOCK(ed25519_sign_prefixed, mock_ed25519_sign_prefixed);

  crypto_rand(circ_nonce, sizeof(circ_nonce));

  setup_full_capture_of_logs(LOG_WARN);
  /* Easiest way to make that function fail is to mock the
     ed25519_sign_prefixed() function and make it fail. */
  cell = trn_cell_establish_intro_new();
  tt_assert(cell);
  ip = service_intro_point_new(NULL);
  cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, NULL);
  service_intro_point_free(ip);
  expect_log_msg_containing("Unable to make signature for "
                            "ESTABLISH_INTRO cell.");
  teardown_capture_of_logs();
  tt_i64_op(cell_len, OP_EQ, -1);

 done:
  trn_cell_establish_intro_free(cell);
  UNMOCK(ed25519_sign_prefixed);
}

static void
test_gen_establish_intro_dos_ext(void *arg)
{
  ssize_t ret;
  hs_service_config_t config;
  hs_service_intro_point_t *ip = NULL;
  trn_cell_extension_t *extensions = NULL;
  trn_cell_extension_dos_t *dos = NULL;

  (void) arg;

  memset(&config, 0, sizeof(config));
  ip = service_intro_point_new(NULL);
  tt_assert(ip);
  ip->support_intro2_dos_defense = 1;

  /* Case 1: No DoS parameters so no extension to be built. */
  extensions = build_establish_intro_extensions(&config, ip);
  tt_int_op(trn_cell_extension_get_num(extensions), OP_EQ, 0);
  trn_cell_extension_free(extensions);
  extensions = NULL;

  /* Case 2: Enable the DoS extension. Parameter set to 0 should indicate to
   * disable the defense on the intro point but there should be an extension
   * nonetheless in the cell. */
  config.has_dos_defense_enabled = 1;
  extensions = build_establish_intro_extensions(&config, ip);
  tt_int_op(trn_cell_extension_get_num(extensions), OP_EQ, 1);
  /* Validate the extension. */
  const trn_cell_extension_field_t *field =
    trn_cell_extension_getconst_fields(extensions, 0);
  tt_int_op(trn_cell_extension_field_get_field_type(field), OP_EQ,
            TRUNNEL_CELL_EXTENSION_TYPE_DOS);
  ret = trn_cell_extension_dos_parse(&dos,
                 trn_cell_extension_field_getconstarray_field(field),
                 trn_cell_extension_field_getlen_field(field));
  tt_int_op(ret, OP_EQ, 19);
  /* Rate per sec param. */
  const trn_cell_extension_dos_param_t *param =
    trn_cell_extension_dos_getconst_params(dos, 0);
  tt_int_op(trn_cell_extension_dos_param_get_type(param), OP_EQ,
            TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC);
  tt_u64_op(trn_cell_extension_dos_param_get_value(param), OP_EQ, 0);
  /* Burst per sec param. */
  param = trn_cell_extension_dos_getconst_params(dos, 1);
  tt_int_op(trn_cell_extension_dos_param_get_type(param), OP_EQ,
            TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC);
  tt_u64_op(trn_cell_extension_dos_param_get_value(param), OP_EQ, 0);
  trn_cell_extension_dos_free(dos); dos = NULL;
  trn_cell_extension_free(extensions); extensions = NULL;

  /* Case 3: Enable the DoS extension. Parameter set to some normal values. */
  config.has_dos_defense_enabled = 1;
  config.intro_dos_rate_per_sec = 42;
  config.intro_dos_burst_per_sec = 250;
  extensions = build_establish_intro_extensions(&config, ip);
  tt_int_op(trn_cell_extension_get_num(extensions), OP_EQ, 1);
  /* Validate the extension. */
  field = trn_cell_extension_getconst_fields(extensions, 0);
  tt_int_op(trn_cell_extension_field_get_field_type(field), OP_EQ,
            TRUNNEL_CELL_EXTENSION_TYPE_DOS);
  ret = trn_cell_extension_dos_parse(&dos,
                 trn_cell_extension_field_getconstarray_field(field),
                 trn_cell_extension_field_getlen_field(field));
  tt_int_op(ret, OP_EQ, 19);
  /* Rate per sec param. */
  param = trn_cell_extension_dos_getconst_params(dos, 0);
  tt_int_op(trn_cell_extension_dos_param_get_type(param), OP_EQ,
            TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC);
  tt_u64_op(trn_cell_extension_dos_param_get_value(param), OP_EQ, 42);
  /* Burst per sec param. */
  param = trn_cell_extension_dos_getconst_params(dos, 1);
  tt_int_op(trn_cell_extension_dos_param_get_type(param), OP_EQ,
            TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC);
  tt_u64_op(trn_cell_extension_dos_param_get_value(param), OP_EQ, 250);
  trn_cell_extension_dos_free(dos); dos = NULL;
  trn_cell_extension_free(extensions); extensions = NULL;

 done:
  service_intro_point_free(ip);
  trn_cell_extension_dos_free(dos);
  trn_cell_extension_free(extensions);
}

struct testcase_t hs_cell_tests[] = {
  { "gen_establish_intro_cell", test_gen_establish_intro_cell, TT_FORK,
    NULL, NULL },
  { "gen_establish_intro_cell_bad", test_gen_establish_intro_cell_bad, TT_FORK,
    NULL, NULL },
  { "gen_establish_intro_dos_ext", test_gen_establish_intro_dos_ext, TT_FORK,
    NULL, NULL },

  END_OF_TESTCASES
};