tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

test_hs_pow.c (17709B)


      1 /* Copyright (c) 2020-2023, The Tor Project, Inc. */
      2 /* See LICENSE for licensing information */
      3 
      4 /**
      5 * \file test_hs_pow.c
      6 * \brief Tests for service proof-of-work verification and wire protocol.
      7 */
      8 
      9 #define HS_SERVICE_PRIVATE
     10 #define HS_CIRCUIT_PRIVATE
     11 
     12 #include "lib/cc/compat_compiler.h"
     13 #include "lib/cc/torint.h"
     14 
     15 #include "test/hs_test_helpers.h"
     16 #include "test/log_test_helpers.h"
     17 #include "test/test_helpers.h"
     18 #include "test/test.h"
     19 
     20 #include "app/config/config.h"
     21 #include "core/or/circuitbuild.h"
     22 #include "core/or/circuitlist.h"
     23 #include "core/or/relay.h"
     24 #include "feature/hs/hs_cell.h"
     25 #include "feature/hs/hs_circuit.h"
     26 #include "feature/hs/hs_metrics.h"
     27 #include "feature/hs/hs_pow.h"
     28 #include "feature/hs/hs_service.h"
     29 #include "feature/nodelist/nodelist.h"
     30 
     31 #include "core/or/crypt_path_st.h"
     32 #include "core/or/origin_circuit_st.h"
     33 #include "feature/nodelist/node_st.h"
     34 #include "feature/nodelist/routerinfo_st.h"
     35 
     36 #include "trunnel/hs/cell_introduce1.h"
     37 
     38 static int test_rend_launch_count;
     39 static uint32_t test_rend_launch_expect_effort;
     40 
     41 static void
     42 mock_launch_rendezvous_point_circuit(const hs_service_t *service,
     43                             const ed25519_public_key_t *ip_auth_pubkey,
     44                             const curve25519_keypair_t *ip_enc_key_kp,
     45                             const hs_cell_intro_rdv_data_t *rdv_data,
     46                             time_t now)
     47 {
     48  (void) service;
     49  (void) ip_auth_pubkey;
     50  (void) ip_enc_key_kp;
     51  (void) rdv_data;
     52  (void) now;
     53 
     54  tt_int_op(test_rend_launch_expect_effort, OP_EQ, rdv_data->pow_effort);
     55  test_rend_launch_count++;
     56 
     57 done:
     58  ;
     59 }
     60 
     61 static node_t *fake_node = NULL;
     62 
     63 static const node_t *
     64 mock_build_state_get_exit_node(cpath_build_state_t *state)
     65 {
     66  (void) state;
     67 
     68  if (!fake_node) {
     69    curve25519_secret_key_t seckey;
     70    curve25519_secret_key_generate(&seckey, 0);
     71 
     72    fake_node = tor_malloc_zero(sizeof(node_t));
     73    fake_node->ri = tor_malloc_zero(sizeof(routerinfo_t));
     74    fake_node->ri->onion_curve25519_pkey =
     75      tor_malloc_zero(sizeof(curve25519_public_key_t));
     76    curve25519_public_key_generate(fake_node->ri->onion_curve25519_pkey,
     77                                   &seckey);
     78  }
     79 
     80  return fake_node;
     81 }
     82 
     83 static smartlist_t *
     84 mock_node_get_link_specifier_smartlist(const node_t *node, bool direct_conn)
     85 {
     86  (void) node;
     87  (void) direct_conn;
     88 
     89  smartlist_t *lspecs = smartlist_new();
     90  link_specifier_t *ls_legacy = link_specifier_new();
     91  smartlist_add(lspecs, ls_legacy);
     92 
     93  return lspecs;
     94 }
     95 
     96 static size_t relay_payload_len;
     97 static uint8_t relay_payload[RELAY_PAYLOAD_SIZE];
     98 
     99 static int
    100 mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ,
    101                                  uint8_t relay_command, const char *payload,
    102                                  size_t payload_len,
    103                                  crypt_path_t *cpath_layer,
    104                                  const char *filename, int lineno)
    105 {
    106  (void) stream_id;
    107  (void) circ;
    108  (void) relay_command;
    109  (void) payload;
    110  (void) payload_len;
    111  (void) cpath_layer;
    112  (void) filename;
    113  (void) lineno;
    114 
    115  memcpy(relay_payload, payload, payload_len);
    116  relay_payload_len = payload_len;
    117 
    118  return 0;
    119 }
    120 
    121 typedef struct testing_hs_pow_service_t {
    122  hs_service_t service;
    123  hs_subcredential_t subcred;
    124  hs_service_intro_point_t *service_ip;
    125  hs_desc_intro_point_t *desc_ip;
    126  hs_ntor_intro_cell_keys_t intro_keys;
    127  origin_circuit_t *intro_circ;
    128  origin_circuit_t *rend_circ;
    129 } testing_hs_pow_service_t;
    130 
    131 /* Common test setup */
    132 static testing_hs_pow_service_t *
    133 testing_hs_pow_service_new(void)
    134 {
    135  MOCK(build_state_get_exit_node, mock_build_state_get_exit_node);
    136  MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge);
    137  MOCK(launch_rendezvous_point_circuit, mock_launch_rendezvous_point_circuit);
    138  MOCK(node_get_link_specifier_smartlist,
    139       mock_node_get_link_specifier_smartlist);
    140 
    141  testing_hs_pow_service_t *tsvc = tor_malloc_zero(sizeof *tsvc);
    142  hs_metrics_service_init(&tsvc->service);
    143 
    144  ed25519_keypair_t identity_keypair;
    145  ed25519_keypair_generate(&identity_keypair, 0);
    146  hs_helper_get_subcred_from_identity_keypair(&identity_keypair,
    147                                              &tsvc->subcred);
    148 
    149  curve25519_secret_key_t seckey;
    150  curve25519_public_key_t pkey;
    151  curve25519_secret_key_generate(&seckey, 0);
    152  curve25519_public_key_generate(&pkey, &seckey);
    153 
    154  node_t intro_node;
    155  memset(&intro_node, 0, sizeof(intro_node));
    156  routerinfo_t ri;
    157  memset(&ri, 0, sizeof(routerinfo_t));
    158  ri.onion_curve25519_pkey = &pkey;
    159  intro_node.ri = &ri;
    160 
    161  hs_service_intro_point_t *svc_ip = service_intro_point_new(&intro_node);
    162  const ed25519_public_key_t *ip_auth_pubkey = &svc_ip->auth_key_kp.pubkey;
    163  const curve25519_public_key_t *ip_enc_pubkey = &svc_ip->enc_key_kp.pubkey;
    164  tsvc->service_ip = svc_ip;
    165 
    166  ed25519_keypair_t signing_kp;
    167  ed25519_keypair_generate(&signing_kp, 0);
    168  tsvc->desc_ip = hs_helper_build_intro_point(&signing_kp, 0, "1.2.3.4", 0,
    169                                              &svc_ip->auth_key_kp,
    170                                              &svc_ip->enc_key_kp);
    171 
    172  tsvc->intro_circ = origin_circuit_new();
    173  tsvc->rend_circ = origin_circuit_new();
    174 
    175  tsvc->intro_circ->cpath = tor_malloc_zero(sizeof(crypt_path_t));
    176 
    177  struct hs_ident_circuit_t *hs_ident = tor_malloc_zero(sizeof *hs_ident);
    178  tsvc->rend_circ->hs_ident = hs_ident;
    179  tsvc->intro_circ->hs_ident = hs_ident;
    180  curve25519_keypair_generate(&hs_ident->rendezvous_client_kp, 0);
    181  tt_int_op(0, OP_EQ,
    182            hs_ntor_client_get_introduce1_keys(ip_auth_pubkey,
    183                                               ip_enc_pubkey,
    184                                               &hs_ident->rendezvous_client_kp,
    185                                               &tsvc->subcred,
    186                                               &tsvc->intro_keys));
    187 done:
    188  return tsvc;
    189 }
    190 
    191 static void
    192 testing_hs_pow_service_free(testing_hs_pow_service_t *tsvc)
    193 {
    194  hs_metrics_service_free(&tsvc->service);
    195  service_intro_point_free(tsvc->service_ip);
    196  hs_desc_intro_point_free(tsvc->desc_ip);
    197  hs_pow_free_service_state(tsvc->service.state.pow_state);
    198  tor_free(tsvc);
    199 
    200  if (fake_node) {
    201    tor_free(fake_node->ri->onion_curve25519_pkey);
    202    tor_free(fake_node->ri);
    203    tor_free(fake_node);
    204  }
    205 
    206  UNMOCK(build_state_get_exit_node);
    207  UNMOCK(relay_send_command_from_edge_);
    208  UNMOCK(launch_rendezvous_point_circuit);
    209  UNMOCK(node_get_link_specifier_smartlist);
    210 }
    211 
    212 /* Make sure we can send a PoW extension to a service without PoW enabled */
    213 static void
    214 test_hs_pow_unsolicited(void *arg)
    215 {
    216  (void)arg;
    217 
    218  testing_hs_pow_service_t *tsvc = testing_hs_pow_service_new();
    219 
    220  /* Try this twice, changing only the presence or lack of PoW solution */
    221  for (int test_variant = 0; test_variant < 2; test_variant++) {
    222 
    223    relay_payload_len = 0;
    224    test_rend_launch_count = 0;
    225    test_rend_launch_expect_effort = 0;
    226    memset(relay_payload, 0, sizeof relay_payload);
    227 
    228    hs_pow_solution_t solution = { 0 };
    229    int retval;
    230 
    231    retval = hs_circ_send_introduce1(tsvc->intro_circ, tsvc->rend_circ,
    232                                     tsvc->desc_ip, &tsvc->subcred,
    233                                     test_variant == 0 ? &solution : NULL);
    234 
    235    tt_int_op(retval, OP_EQ, 0);
    236    tt_assert(!fast_mem_is_zero((const char*)relay_payload,
    237                                sizeof relay_payload));
    238    tt_int_op(relay_payload_len, OP_NE, 0);
    239 
    240    retval = hs_circ_handle_introduce2(&tsvc->service, tsvc->intro_circ,
    241                                       tsvc->service_ip, &tsvc->subcred,
    242                                       relay_payload,
    243                                       relay_payload_len);
    244 
    245    tt_int_op(retval, OP_EQ, test_variant == 0 ? -1 : 0);
    246    tt_int_op(test_rend_launch_count, OP_EQ, test_variant == 0 ? 0 : 1);
    247  }
    248 
    249 done:
    250  testing_hs_pow_service_free(tsvc);
    251 }
    252 
    253 static void
    254 test_hs_pow_vectors(void *arg)
    255 {
    256  (void)arg;
    257 
    258  /* This covers encoding, wire protocol, and verification for PoW-extended
    259   * introduction cells. The solutions here can be generated using the
    260   * setup in test_hs_pow_slow.
    261   */
    262  static const struct {
    263    uint32_t claimed_effort;
    264    uint32_t validated_effort;
    265    int expected_retval;
    266    const char *seed_hex;
    267    const char *service_blinded_id_hex;
    268    const char *nonce_hex;
    269    const char *sol_hex;
    270    const char *encoded_hex;
    271  } vectors[] = {
    272    {
    273      /* All zero, expect invalid */
    274      1, 0, -1,
    275      "0000000000000000000000000000000000000000000000000000000000000000",
    276      "1111111111111111111111111111111111111111111111111111111111111111",
    277      "00000000000000000000000000000000", "00000000000000000000000000000000",
    278      "01"
    279      "00000000000000000000000000000000"
    280      "00000001" "00000000"
    281      "00000000000000000000000000000000"
    282    },
    283    {
    284      /* Valid zero-effort solution */
    285      0, 0, 0,
    286      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    287      "1111111111111111111111111111111111111111111111111111111111111111",
    288      "55555555555555555555555555555555", "4312f87ceab844c78e1c793a913812d7",
    289      "01"
    290      "55555555555555555555555555555555"
    291      "00000000" "aaaaaaaa"
    292      "4312f87ceab844c78e1c793a913812d7"
    293    },
    294    {
    295      /* Valid high-effort solution */
    296      1000000, 1000000, 0,
    297      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    298      "1111111111111111111111111111111111111111111111111111111111111111",
    299      "59217255555555555555555555555555", "0f3db97b9cac20c1771680a1a34848d3",
    300      "01"
    301      "59217255555555555555555555555555"
    302      "000f4240" "aaaaaaaa"
    303      "0f3db97b9cac20c1771680a1a34848d3"
    304    },
    305    {
    306      /* Reject replays */
    307      1000000, 0, -1,
    308      "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    309      "1111111111111111111111111111111111111111111111111111111111111111",
    310      "59217255555555555555555555555555", "0f3db97b9cac20c1771680a1a34848d3",
    311      "01"
    312      "59217255555555555555555555555555"
    313      "000f4240" "aaaaaaaa"
    314      "0f3db97b9cac20c1771680a1a34848d3"
    315    },
    316    {
    317      /* The claimed effort must exactly match what's in the challenge */
    318      99999, 0, -1,
    319      "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f",
    320      "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed",
    321      "2eff9fdbc34326d9d2f18ed277469c63", "400cb091139f86b352119f6e131802d6",
    322      "01"
    323      "2eff9fdbc34326d9d2f18ed277469c63"
    324      "0001869f" "86fb0acf"
    325      "400cb091139f86b352119f6e131802d6"
    326    },
    327    {
    328      /* Otherwise good solution but with a corrupted nonce */
    329      100000, 0, -1,
    330      "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f",
    331      "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed",
    332      "2eff9fdbc34326d9a2f18ed277469c63", "400cb091139f86b352119f6e131802d6",
    333      "01"
    334      "2eff9fdbc34326d9a2f18ed277469c63"
    335      "000186a0" "86fb0acf"
    336      "400cb091139f86b352119f6e131802d6"
    337    },
    338    {
    339      /* Corrected version of above */
    340      100000, 100000, 0,
    341      "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f",
    342      "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed",
    343      "2eff9fdbc34326d9d2f18ed277469c63", "400cb091139f86b352119f6e131802d6",
    344      "01"
    345      "2eff9fdbc34326d9d2f18ed277469c63"
    346      "000186a0" "86fb0acf"
    347      "400cb091139f86b352119f6e131802d6"
    348    }
    349  };
    350 
    351  testing_hs_pow_service_t *tsvc = testing_hs_pow_service_new();
    352  hs_pow_service_state_t *pow_state = tor_malloc_zero(sizeof *pow_state);
    353  tsvc->service.state.pow_state = pow_state;
    354  tsvc->service.desc_current = service_descriptor_new();
    355  pow_state->rend_request_pqueue = smartlist_new();
    356 
    357  char *mem_op_hex_tmp = NULL;
    358  uint8_t *decrypted = NULL;
    359  trn_cell_introduce_encrypted_t *enc_cell = NULL;
    360  trn_cell_introduce1_t *cell = NULL;
    361 
    362  const unsigned num_vectors = sizeof vectors / sizeof vectors[0];
    363  for (unsigned vec_i = 0; vec_i < num_vectors; vec_i++) {
    364    const int expected_retval = vectors[vec_i].expected_retval;
    365    const char *service_blinded_id_hex = vectors[vec_i].service_blinded_id_hex;
    366    const char *seed_hex = vectors[vec_i].seed_hex;
    367    const char *nonce_hex = vectors[vec_i].nonce_hex;
    368    const char *sol_hex = vectors[vec_i].sol_hex;
    369    const char *encoded_hex = vectors[vec_i].encoded_hex;
    370 
    371    relay_payload_len = 0;
    372    test_rend_launch_count = 0;
    373    test_rend_launch_expect_effort = vectors[vec_i].validated_effort;
    374    memset(relay_payload, 0, sizeof relay_payload);
    375 
    376    hs_pow_solution_t solution = {
    377      .effort = vectors[vec_i].claimed_effort,
    378    };
    379    int retval;
    380 
    381    tt_int_op(strlen(service_blinded_id_hex), OP_EQ, 2 * HS_POW_ID_LEN);
    382    tt_int_op(strlen(seed_hex), OP_EQ, 2 * HS_POW_SEED_LEN);
    383    tt_int_op(strlen(nonce_hex), OP_EQ, 2 * sizeof solution.nonce);
    384    tt_int_op(strlen(sol_hex), OP_EQ, 2 * sizeof solution.equix_solution);
    385 
    386    tt_assert(tsvc->service.desc_current);
    387    ed25519_public_key_t *desc_blinded_pubkey =
    388        &tsvc->service.desc_current->desc->plaintext_data.blinded_pubkey;
    389 
    390    tt_int_op(base16_decode((char*)desc_blinded_pubkey->pubkey,
    391                            HS_POW_ID_LEN, service_blinded_id_hex,
    392                            2 * HS_POW_ID_LEN),
    393                            OP_EQ, HS_POW_ID_LEN);
    394    tt_int_op(base16_decode((char*)pow_state->seed_previous, HS_POW_SEED_LEN,
    395                            seed_hex, 2 * HS_POW_SEED_LEN),
    396                            OP_EQ, HS_POW_SEED_LEN);
    397    tt_int_op(base16_decode((char*)solution.nonce, HS_POW_NONCE_LEN,
    398                            nonce_hex, 2 * HS_POW_NONCE_LEN),
    399                            OP_EQ, HS_POW_NONCE_LEN);
    400    tt_int_op(base16_decode((char*)solution.equix_solution, HS_POW_EQX_SOL_LEN,
    401                            sol_hex, 2 * HS_POW_EQX_SOL_LEN),
    402                            OP_EQ, HS_POW_EQX_SOL_LEN);
    403 
    404    ed25519_pubkey_copy(&tsvc->service_ip->blinded_id, desc_blinded_pubkey);
    405    memcpy(solution.seed_head, pow_state->seed_previous, HS_POW_SEED_HEAD_LEN);
    406 
    407    /* Try to encode 'solution' into a relay cell */
    408 
    409    retval = hs_circ_send_introduce1(tsvc->intro_circ, tsvc->rend_circ,
    410                                     tsvc->desc_ip, &tsvc->subcred,
    411                                     &solution);
    412 
    413    tt_int_op(retval, OP_EQ, 0);
    414    tt_assert(!fast_mem_is_zero((const char*)relay_payload,
    415                                sizeof relay_payload));
    416    tt_int_op(relay_payload_len, OP_NE, 0);
    417 
    418    /* Check the service's response to this introduction */
    419 
    420    retval = hs_circ_handle_introduce2(&tsvc->service, tsvc->intro_circ,
    421                                       tsvc->service_ip, &tsvc->subcred,
    422                                       relay_payload,
    423                                       relay_payload_len);
    424    tt_int_op(retval, OP_EQ, expected_retval);
    425    tt_int_op(test_rend_launch_count, OP_EQ, expected_retval == 0 ? 1 : 0);
    426 
    427    /* Start unpacking the cell ourselves so we can check the PoW data */
    428 
    429    trn_cell_introduce1_free(cell);
    430    cell = NULL;
    431    tt_int_op(trn_cell_introduce1_parse(&cell, relay_payload,
    432                                        relay_payload_len), OP_GT, 0);
    433 
    434    size_t encrypted_section_len;
    435    const uint8_t *encrypted_section;
    436    encrypted_section = trn_cell_introduce1_getconstarray_encrypted(cell);
    437    encrypted_section_len = trn_cell_introduce1_getlen_encrypted(cell);
    438    tt_int_op(encrypted_section_len, OP_GT,
    439              DIGEST256_LEN + CURVE25519_PUBKEY_LEN);
    440 
    441    /* Decrypt the encrypted portion of the INTRODUCE1 */
    442 
    443    crypto_cipher_t *cipher = NULL;
    444    cipher = crypto_cipher_new_with_bits((char *) tsvc->intro_keys.enc_key,
    445                                         CURVE25519_PUBKEY_LEN * 8);
    446    tt_ptr_op(cipher, OP_NE, NULL);
    447 
    448    size_t decrypted_len = encrypted_section_len
    449                             - DIGEST256_LEN - CURVE25519_PUBKEY_LEN;
    450    tor_free(decrypted);
    451    decrypted = tor_malloc_zero(decrypted_len);
    452    retval = crypto_cipher_decrypt(cipher, (char *) decrypted,
    453                                   (const char *) encrypted_section
    454                                     + CURVE25519_PUBKEY_LEN,
    455                                   decrypted_len);
    456    crypto_cipher_free(cipher);
    457    tt_int_op(retval, OP_EQ, 0);
    458 
    459    /* Parse the outer layer of the encrypted payload */
    460 
    461    trn_cell_introduce_encrypted_free(enc_cell);
    462    enc_cell = NULL;
    463    tt_int_op(trn_cell_introduce_encrypted_parse(&enc_cell, decrypted,
    464                                                 decrypted_len), OP_GT, 0);
    465 
    466    /* Check for the expected single extension */
    467 
    468    const trn_extension_t *extensions =
    469      trn_cell_introduce_encrypted_get_extensions(enc_cell);
    470    tt_int_op(trn_extension_get_num(extensions), OP_EQ, 1);
    471 
    472    const trn_extension_field_t *field =
    473      trn_extension_getconst_fields(extensions, 0);
    474    tt_int_op(trn_extension_field_get_field_type(field),
    475              OP_EQ, TRUNNEL_EXT_TYPE_POW);
    476 
    477    const uint8_t *field_data = trn_extension_field_getconstarray_field(field);
    478    size_t field_len = trn_extension_field_getlen_field(field);
    479 
    480    /* Our test vectors cover the packed data in the single extension */
    481 
    482    tt_int_op(field_len * 2, OP_EQ, strlen(encoded_hex));
    483    test_memeq_hex(field_data, encoded_hex);
    484  }
    485 
    486 done:
    487  tor_free(mem_op_hex_tmp);
    488  tor_free(decrypted);
    489  trn_cell_introduce1_free(cell);
    490  trn_cell_introduce_encrypted_free(enc_cell);
    491  service_descriptor_free(tsvc->service.desc_current);
    492  testing_hs_pow_service_free(tsvc);
    493  hs_pow_remove_seed_from_cache(NULL);
    494 }
    495 
    496 struct testcase_t hs_pow_tests[] = {
    497  { "unsolicited", test_hs_pow_unsolicited, TT_FORK, NULL, NULL },
    498  { "vectors", test_hs_pow_vectors, TT_FORK, NULL, NULL },
    499  END_OF_TESTCASES
    500 };