static size_t new_establish_intro_cell(const char *circ_nonce, trn_cell_establish_intro_t **cell_out) { ssize_t cell_len = 0; uint8_t buf[RELAY_PAYLOAD_SIZE] = {0}; trn_cell_establish_intro_t *cell = NULL; hs_service_intro_point_t *ip = NULL; /* Ensure that *cell_out is NULL such that we can use to check if we need to * free `cell` in case of an error. */ *cell_out = NULL; /* Auth key pair is generated in the constructor so we are all set for * using this IP object. */ ip = service_intro_point_new(NULL); tt_assert(ip); cell_len = hs_cell_build_establish_intro(circ_nonce, ip, buf); tt_i64_op(cell_len, OP_GT, 0); cell_len = trn_cell_establish_intro_parse(&cell, buf, sizeof(buf)); tt_i64_op(cell_len, OP_GT, 0); tt_assert(cell); *cell_out = cell; done: if (*cell_out == NULL) trn_cell_establish_intro_free(cell); service_intro_point_free(ip); return cell_len; }
static void test_crypto_rng_range(void *arg) { int got_smallest = 0, got_largest = 0; int i; (void)arg; for (i = 0; i < 1000; ++i) { int x = crypto_rand_int_range(5,9); tt_int_op(x, OP_GE, 5); tt_int_op(x, OP_LT, 9); if (x == 5) got_smallest = 1; if (x == 8) got_largest = 1; } /* These fail with probability 1/10^603. */ tt_assert(got_smallest); tt_assert(got_largest); got_smallest = got_largest = 0; const uint64_t ten_billion = 10 * ((uint64_t)1000000000000); for (i = 0; i < 1000; ++i) { uint64_t x = crypto_rand_uint64_range(ten_billion, ten_billion+10); tt_u64_op(x, OP_GE, ten_billion); tt_u64_op(x, OP_LT, ten_billion+10); if (x == ten_billion) got_smallest = 1; if (x == ten_billion+9) got_largest = 1; } tt_assert(got_smallest); tt_assert(got_largest); const time_t now = time(NULL); for (i = 0; i < 2000; ++i) { time_t x = crypto_rand_time_range(now, now+60); tt_i64_op(x, OP_GE, now); tt_i64_op(x, OP_LT, now+60); if (x == now) got_smallest = 1; if (x == now+59) got_largest = 1; } tt_assert(got_smallest); tt_assert(got_largest); done: ; }
/** 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; 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, 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); }
/* Helper function: Send a well-formed v3 ESTABLISH_INTRO cell to * <b>intro_circ</b>. Return the cell. */ static trn_cell_establish_intro_t * helper_establish_intro_v3(or_circuit_t *intro_circ) { int retval; char circ_nonce[DIGEST_LEN] = {0}; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; ssize_t cell_len = 0; trn_cell_establish_intro_t *cell = NULL; tt_assert(intro_circ); /* Prepare the circuit for the incoming ESTABLISH_INTRO */ crypto_rand(circ_nonce, sizeof(circ_nonce)); helper_prepare_circ_for_intro(intro_circ, circ_nonce); /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we * attempt to parse it. */ cell_len = new_establish_intro_cell(circ_nonce, &cell); tt_i64_op(cell_len, OP_GT, 0); tt_assert(cell); cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body), cell); tt_int_op(cell_len, OP_GT, 0); /* Receive the cell */ retval = hs_intro_received_establish_intro(intro_circ, cell_body, (size_t) cell_len); tt_int_op(retval, OP_EQ, 0); done: return cell; }
/* Send a legit ESTABLISH_INTRO cell but slightly change the signature. Should * fail. */ static void test_establish_intro_wrong_sig(void *arg) { int retval; char circ_nonce[DIGEST_LEN] = {0}; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; ssize_t cell_len = 0; or_circuit_t *intro_circ = or_circuit_new(0,NULL); (void) arg; /* Get the auth key of the intro point */ crypto_rand(circ_nonce, sizeof(circ_nonce)); helper_prepare_circ_for_intro(intro_circ, circ_nonce); /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we attempt to parse it. */ cell_len = new_establish_intro_encoded_cell(circ_nonce, cell_body); tt_i64_op(cell_len, OP_GT, 0); /* Mutate the last byte (signature)! :) */ cell_body[cell_len - 1]++; /* Receive the cell. Should fail. */ setup_full_capture_of_logs(LOG_INFO); retval = hs_intro_received_establish_intro(intro_circ, cell_body, (size_t)cell_len); expect_log_msg_containing("Failed to verify ESTABLISH_INTRO cell."); teardown_capture_of_logs(); tt_int_op(retval, OP_EQ, -1); done: circuit_free_(TO_CIRCUIT(intro_circ)); }
/* Try sending an ESTABLISH_INTRO cell on a circuit that is already an intro * point. Should fail. */ static void test_establish_intro_wrong_purpose(void *arg) { int retval; ssize_t cell_len = 0; char circ_nonce[DIGEST_LEN] = {0}; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; or_circuit_t *intro_circ = or_circuit_new(0,NULL); (void)arg; /* Get the auth key of the intro point */ crypto_rand(circ_nonce, sizeof(circ_nonce)); memcpy(intro_circ->rend_circ_nonce, circ_nonce, DIGEST_LEN); /* Set a bad circuit purpose!! :) */ circuit_change_purpose(TO_CIRCUIT(intro_circ), CIRCUIT_PURPOSE_INTRO_POINT); /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we attempt to parse it. */ cell_len = new_establish_intro_encoded_cell(circ_nonce, cell_body); tt_i64_op(cell_len, OP_GT, 0); /* Receive the cell. Should fail. */ setup_full_capture_of_logs(LOG_INFO); retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); expect_log_msg_containing("Rejecting ESTABLISH_INTRO on non-OR circuit."); teardown_capture_of_logs(); tt_int_op(retval, OP_EQ, -1); done: circuit_free_(TO_CIRCUIT(intro_circ)); }
static ssize_t new_establish_intro_encoded_cell(const char *circ_nonce, uint8_t *cell_out) { ssize_t cell_len = 0; hs_service_intro_point_t *ip = NULL; /* Auth key pair is generated in the constructor so we are all set for * using this IP object. */ ip = service_intro_point_new(NULL); tt_assert(ip); cell_len = hs_cell_build_establish_intro(circ_nonce, ip, cell_out); tt_i64_op(cell_len, OP_GT, 0); done: service_intro_point_free(ip); return cell_len; }
/* Send a legit ESTABLISH_INTRO cell but with a wrong sig length. Should * fail. */ static void test_establish_intro_wrong_sig_len(void *arg) { int retval; char circ_nonce[DIGEST_LEN] = {0}; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; ssize_t cell_len = 0; size_t bad_sig_len = ED25519_SIG_LEN - 1; trn_cell_establish_intro_t *cell = NULL; or_circuit_t *intro_circ = or_circuit_new(0,NULL); (void) arg; /* Get the auth key of the intro point */ crypto_rand(circ_nonce, sizeof(circ_nonce)); helper_prepare_circ_for_intro(intro_circ, circ_nonce); /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we * attempt to parse it. */ cell_len = new_establish_intro_cell(circ_nonce, &cell); tt_i64_op(cell_len, OP_GT, 0); tt_assert(cell); /* Mangle the signature length. */ trn_cell_establish_intro_set_sig_len(cell, bad_sig_len); trn_cell_establish_intro_setlen_sig(cell, bad_sig_len); /* Encode cell. */ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body), cell); tt_int_op(cell_len, OP_GT, 0); /* Receive the cell. Should fail. */ setup_full_capture_of_logs(LOG_INFO); retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); expect_log_msg_containing("ESTABLISH_INTRO sig len is invalid"); teardown_capture_of_logs(); tt_int_op(retval, OP_EQ, -1); done: trn_cell_establish_intro_free(cell); circuit_free_(TO_CIRCUIT(intro_circ)); }
/* Test outbound cell. The callstack is: * channel_flush_some_cells() * -> channel_flush_from_first_active_circuit() * -> channel_write_packed_cell() * -> write_packed_cell() * -> chan->write_packed_cell() fct ptr. * * This test goes from a cell in a circuit up to the channel write handler * that should put them on the connection outbuf. */ static void test_channel_outbound_cell(void *arg) { int old_count; channel_t *chan = NULL; packed_cell_t *p_cell = NULL, *p_cell2 = NULL; origin_circuit_t *circ = NULL; cell_queue_t *queue; (void) arg; /* Set the test time to be mocked, since this test assumes that no * time will pass, ewma values will not need to be re-scaled, and so on */ monotime_enable_test_mocking(); monotime_set_mock_time_nsec(U64_LITERAL(1000000000) * 12345); cmux_ewma_set_options(NULL,NULL); /* The channel will be freed so we need to hijack this so the scheduler * doesn't get confused. */ MOCK(scheduler_release_channel, scheduler_release_channel_mock); /* Accept cells to lower layer */ test_chan_accept_cells = 1; /* Setup a valid circuit to queue a cell. */ circ = origin_circuit_new(); tt_assert(circ); /* Circuit needs an origin purpose to be considered origin. */ TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; TO_CIRCUIT(circ)->n_circ_id = 42; /* This is the outbound test so use the next channel queue. */ queue = &TO_CIRCUIT(circ)->n_chan_cells; /* Setup packed cell to queue on the circuit. */ p_cell = packed_cell_new(); tt_assert(p_cell); p_cell2 = packed_cell_new(); tt_assert(p_cell2); /* Setup a channel to put the circuit on. */ chan = new_fake_channel(); tt_assert(chan); chan->state = CHANNEL_STATE_OPENING; channel_change_state_open(chan); /* Outbound channel. */ channel_mark_outgoing(chan); /* Try to register it so we can clean it through the channel cleanup * process. */ channel_register(chan); tt_int_op(chan->registered, OP_EQ, 1); /* Set EWMA policy so we can pick it when flushing. */ circuitmux_set_policy(chan->cmux, &ewma_policy); tt_ptr_op(circuitmux_get_policy(chan->cmux), OP_EQ, &ewma_policy); /* Register circuit to the channel circid map which will attach the circuit * to the channel's cmux as well. */ circuit_set_n_circid_chan(TO_CIRCUIT(circ), 42, chan); tt_int_op(channel_num_circuits(chan), OP_EQ, 1); /* Test the cmux state. */ tt_ptr_op(TO_CIRCUIT(circ)->n_mux, OP_EQ, chan->cmux); tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 1); /* Flush the channel without any cell on it. */ old_count = test_cells_written; ssize_t flushed = channel_flush_some_cells(chan, 1); tt_i64_op(flushed, OP_EQ, 0); tt_int_op(test_cells_written, OP_EQ, old_count); tt_int_op(channel_more_to_flush(chan), OP_EQ, 0); tt_int_op(circuitmux_num_active_circuits(chan->cmux), OP_EQ, 0); tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 0); tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 0); tt_u64_op(chan->n_cells_xmitted, OP_EQ, 0); tt_u64_op(chan->n_bytes_xmitted, OP_EQ, 0); /* Queue cell onto the next queue that is the outbound direction. Than * update its cmux so the circuit can be picked when flushing cells. */ cell_queue_append(queue, p_cell); p_cell = NULL; tt_int_op(queue->n, OP_EQ, 1); cell_queue_append(queue, p_cell2); p_cell2 = NULL; tt_int_op(queue->n, OP_EQ, 2); update_circuit_on_cmux(TO_CIRCUIT(circ), CELL_DIRECTION_OUT); tt_int_op(circuitmux_num_active_circuits(chan->cmux), OP_EQ, 1); tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 2); tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 1); /* From this point on, we have a queued cell on an active circuit attached * to the channel's cmux. */ /* Flush the first cell. This is going to go down the call stack. */ old_count = test_cells_written; flushed = channel_flush_some_cells(chan, 1); tt_i64_op(flushed, OP_EQ, 1); tt_int_op(test_cells_written, OP_EQ, old_count + 1); tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 1); tt_int_op(channel_more_to_flush(chan), OP_EQ, 1); /* Circuit should remain active because there is a second cell queued. */ tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 1); /* Should still be attached. */ tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 1); tt_u64_op(chan->n_cells_xmitted, OP_EQ, 1); tt_u64_op(chan->n_bytes_xmitted, OP_EQ, get_cell_network_size(0)); /* Flush second cell. This is going to go down the call stack. */ old_count = test_cells_written; flushed = channel_flush_some_cells(chan, 1); tt_i64_op(flushed, OP_EQ, 1); tt_int_op(test_cells_written, OP_EQ, old_count + 1); tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 0); tt_int_op(channel_more_to_flush(chan), OP_EQ, 0); /* No more cells should make the circuit inactive. */ tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 0); /* Should still be attached. */ tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 1); tt_u64_op(chan->n_cells_xmitted, OP_EQ, 2); tt_u64_op(chan->n_bytes_xmitted, OP_EQ, get_cell_network_size(0) * 2); done: if (circ) { circuit_free_(TO_CIRCUIT(circ)); } tor_free(p_cell); channel_free_all(); UNMOCK(scheduler_release_channel); monotime_disable_test_mocking(); }
static void test_router_mark_if_too_old(void *arg) { (void)arg; time_t now = approx_time(); MOCK(networkstatus_get_live_consensus, mock_networkstatus_get_live_consensus); MOCK(networkstatus_vote_find_entry, mock_networkstatus_vote_find_entry); routerstatus_t rs; networkstatus_t ns; memset(&rs, 0, sizeof(rs)); memset(&ns, 0, sizeof(ns)); mock_ns = &ns; mock_ns->valid_after = now-3600; mock_rs = &rs; mock_rs->published_on = now - 10; // no reason to mark this time. desc_clean_since = now-10; desc_dirty_reason = NULL; mark_my_descriptor_dirty_if_too_old(now); tt_i64_op(desc_clean_since, OP_EQ, now-10); // Doesn't appear in consensus? Still don't mark it. mock_ns = NULL; mark_my_descriptor_dirty_if_too_old(now); tt_i64_op(desc_clean_since, OP_EQ, now-10); mock_ns = &ns; // No new descriptor in a long time? Mark it. desc_clean_since = now - 3600 * 96; mark_my_descriptor_dirty_if_too_old(now); tt_i64_op(desc_clean_since, OP_EQ, 0); tt_str_op(desc_dirty_reason, OP_EQ, "time for new descriptor"); // Version in consensus published a long time ago? We won't mark it // if it's been clean for only a short time. desc_clean_since = now - 10; desc_dirty_reason = NULL; mock_rs->published_on = now - 3600 * 96; mark_my_descriptor_dirty_if_too_old(now); tt_i64_op(desc_clean_since, OP_EQ, now - 10); // ... but if it's been clean a while, we mark. desc_clean_since = now - 2 * 3600; mark_my_descriptor_dirty_if_too_old(now); tt_i64_op(desc_clean_since, OP_EQ, 0); tt_str_op(desc_dirty_reason, OP_EQ, "version listed in consensus is quite old"); // same deal if we're marked stale. desc_clean_since = now - 2 * 3600; desc_dirty_reason = NULL; mock_rs->published_on = now - 10; mock_rs->is_staledesc = 1; mark_my_descriptor_dirty_if_too_old(now); tt_i64_op(desc_clean_since, OP_EQ, 0); tt_str_op(desc_dirty_reason, OP_EQ, "listed as stale in consensus"); // same deal if we're absent from the consensus. desc_clean_since = now - 2 * 3600; desc_dirty_reason = NULL; mock_rs = NULL; mark_my_descriptor_dirty_if_too_old(now); tt_i64_op(desc_clean_since, OP_EQ, 0); tt_str_op(desc_dirty_reason, OP_EQ, "not listed in consensus"); done: UNMOCK(networkstatus_get_live_consensus); UNMOCK(networkstatus_vote_find_entry); }
/* Send a legit ESTABLISH_INTRO cell but with a wrong MAC. Should fail. */ static void test_establish_intro_wrong_mac(void *arg) { int retval; char circ_nonce[DIGEST_LEN] = {0}; ssize_t cell_len = 0; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; trn_cell_establish_intro_t *cell = NULL; or_circuit_t *intro_circ = or_circuit_new(0,NULL); (void) arg; /* Get the auth key of the intro point */ crypto_rand(circ_nonce, sizeof(circ_nonce)); helper_prepare_circ_for_intro(intro_circ, circ_nonce); /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we * attempt to parse it. */ cell_len = new_establish_intro_cell(circ_nonce, &cell); tt_i64_op(cell_len, OP_GT, 0); tt_assert(cell); /* Mangle one byte of the MAC. */ uint8_t *handshake_ptr = trn_cell_establish_intro_getarray_handshake_mac(cell); handshake_ptr[TRUNNEL_SHA3_256_LEN - 1]++; /* We need to resign the payload with that change. */ { ed25519_signature_t sig; ed25519_keypair_t key_struct; /* New keypair for the signature since we don't have access to the private * key material generated earlier when creating the cell. */ retval = ed25519_keypair_generate(&key_struct, 0); tt_int_op(retval, OP_EQ, 0); uint8_t *auth_key_ptr = trn_cell_establish_intro_getarray_auth_key(cell); memcpy(auth_key_ptr, key_struct.pubkey.pubkey, ED25519_PUBKEY_LEN); /* Encode payload so we can sign it. */ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body), cell); tt_i64_op(cell_len, OP_GT, 0); retval = ed25519_sign_prefixed(&sig, cell_body, cell_len - (ED25519_SIG_LEN + sizeof(cell->sig_len)), ESTABLISH_INTRO_SIG_PREFIX, &key_struct); tt_int_op(retval, OP_EQ, 0); /* And write the signature to the cell */ uint8_t *sig_ptr = trn_cell_establish_intro_getarray_sig(cell); memcpy(sig_ptr, sig.sig, cell->sig_len); /* Re-encode with the new signature. */ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body), cell); tt_i64_op(cell_len, OP_GT, 0); } /* Receive the cell. Should fail because our MAC is wrong. */ setup_full_capture_of_logs(LOG_INFO); retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); expect_log_msg_containing("ESTABLISH_INTRO handshake_auth not as expected"); teardown_capture_of_logs(); tt_int_op(retval, OP_EQ, -1); done: trn_cell_establish_intro_free(cell); circuit_free_(TO_CIRCUIT(intro_circ)); }