diff options
Diffstat (limited to 'src/test')
50 files changed, 3601 insertions, 227 deletions
diff --git a/src/test/Makefile.nmake b/src/test/Makefile.nmake deleted file mode 100644 index ca6a84cf8a..0000000000 --- a/src/test/Makefile.nmake +++ /dev/null @@ -1,35 +0,0 @@ -all: test.exe bench.exe - -CFLAGS = /I ..\win32 /I ..\..\..\build-alpha\include /I ..\common /I ..\or \ - /I ..\ext - -LIBS = ..\..\..\build-alpha\lib\libevent.lib \ - ..\..\..\build-alpha\lib\libcrypto.lib \ - ..\..\..\build-alpha\lib\libssl.lib \ - ..\..\..\build-alpha\lib\libz.lib \ - ..\or\libtor.lib \ - ws2_32.lib advapi32.lib shell32.lib \ - crypt32.lib gdi32.lib user32.lib - -TEST_OBJECTS = test.obj test_addr.obj test_channel.obj test_channeltls.obj \ - test_consdiff.obj test_containers.obj \ - test_controller_events.obj test_crypto.obj test_data.obj test_dir.obj \ - test_checkdir.obj test_microdesc.obj test_pt.obj test_util.obj \ - test_config.obj test_connection.obj \ - test_cell_formats.obj test_relay.obj test_replay.obj \ - test_channelpadding.obj \ - test_circuitstats.obj \ - test_circuitpadding.obj \ - test_scheduler.obj test_introduce.obj test_hs.obj tinytest.obj - -tinytest.obj: ..\ext\tinytest.c - $(CC) $(CFLAGS) /D snprintf=_snprintf /c ..\ext\tinytest.c - -test.exe: $(TEST_OBJECTS) - $(CC) $(CFLAGS) $(LIBS) ..\common\*.lib $(TEST_OBJECTS) /Fe$@ - -bench.exe: bench.obj - $(CC) $(CFLAGS) bench.obj $(LIBS) ..\common\*.lib /Fe$@ - -clean: - del *.obj *.lib test.exe bench.exe diff --git a/src/test/bench.c b/src/test/bench.c index a76ea67eb8..a76e600cfa 100644 --- a/src/test/bench.c +++ b/src/test/bench.c @@ -380,6 +380,7 @@ bench_rand_len(int len) uint32_t t=0; for (i = 0; i < N; ++i) { t += tor_weak_random(&weak); + (void) t; } end = perftime(); printf("weak_rand(4): %f nsec.\n", NANOCOUNT(start,end,N)); diff --git a/src/test/conf_examples/large_1/expected b/src/test/conf_examples/large_1/expected index fcd19db3df..85f96a24c9 100644 --- a/src/test/conf_examples/large_1/expected +++ b/src/test/conf_examples/large_1/expected @@ -19,7 +19,6 @@ ClientPreferIPv6DirPort 1 ClientPreferIPv6ORPort 1 ClientRejectInternalAddresses 0 ClientUseIPv4 0 -ClientUseIPv6 1 ConnDirectionStatistics 1 ConnectionPadding 1 ConnLimit 64 @@ -126,7 +125,6 @@ ReachableORAddresses 128.0.0.0/8 RejectPlaintextPorts 23 RelayBandwidthBurst 10000 RelayBandwidthRate 1000 -RendPostPeriod 600 RephistTrackTime 600 SafeLogging 0 Schedulers Vanilla,KISTLite,Kist diff --git a/src/test/conf_examples/large_1/expected_no_dirauth b/src/test/conf_examples/large_1/expected_no_dirauth index 4a19bc546c..e4e811caaf 100644 --- a/src/test/conf_examples/large_1/expected_no_dirauth +++ b/src/test/conf_examples/large_1/expected_no_dirauth @@ -19,7 +19,6 @@ ClientPreferIPv6DirPort 1 ClientPreferIPv6ORPort 1 ClientRejectInternalAddresses 0 ClientUseIPv4 0 -ClientUseIPv6 1 ConnDirectionStatistics 1 ConnectionPadding 1 ConnLimit 64 @@ -125,7 +124,6 @@ ReachableORAddresses 128.0.0.0/8 RejectPlaintextPorts 23 RelayBandwidthBurst 10000 RelayBandwidthRate 1000 -RendPostPeriod 600 RephistTrackTime 600 SafeLogging 0 Schedulers Vanilla,KISTLite,Kist diff --git a/src/test/conf_examples/large_1/torrc b/src/test/conf_examples/large_1/torrc index 3f5b1e179f..ebf4a3fb96 100644 --- a/src/test/conf_examples/large_1/torrc +++ b/src/test/conf_examples/large_1/torrc @@ -133,7 +133,6 @@ ReachableORAddresses 128.0.0.0/8 RejectPlaintextPorts 23 RelayBandwidthBurst 10000 RelayBandwidthRate 1000 -RendPostPeriod 10 minutes RephistTrackTime 10 minutes SafeLogging 0 SafeSocks 0 diff --git a/src/test/fakecircs.c b/src/test/fakecircs.c index cca3b43483..e52df022d0 100644 --- a/src/test/fakecircs.c +++ b/src/test/fakecircs.c @@ -17,6 +17,9 @@ #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" #include "core/or/circuitpadding.h" +#include "core/or/congestion_control_common.h" +#include "core/or/conflux_pool.h" +#include "core/or/conflux.h" #include "core/or/crypt_path.h" #include "core/or/relay.h" #include "core/or/relay_crypto_st.h" @@ -41,8 +44,8 @@ new_fake_orcirc(channel_t *nchan, channel_t *pchan) cell_queue_init(&(circ->n_chan_cells)); circ->n_hop = NULL; - circ->streams_blocked_on_n_chan = 0; - circ->streams_blocked_on_p_chan = 0; + circ->circuit_blocked_on_n_chan = 0; + circ->circuit_blocked_on_p_chan = 0; circ->n_delete_pending = 0; circ->p_delete_pending = 0; circ->received_destroy = 0; @@ -87,5 +90,8 @@ free_fake_orcirc(or_circuit_t *orcirc) circuitmux_detach_circuit(circ->n_chan->cmux, circ); } + conflux_circuit_about_to_free(circ); + congestion_control_free(circ->ccontrol); + tor_free_(circ); } diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c index 20b225ba4a..4ef2398095 100644 --- a/src/test/hs_test_helpers.c +++ b/src/test/hs_test_helpers.c @@ -354,6 +354,18 @@ hs_helper_desc_equal(const hs_descriptor_t *desc1, } } + /* Proof of Work DoS mitigation options */ + tt_int_op(!!desc1->encrypted_data.pow_params, OP_EQ, + !!desc2->encrypted_data.pow_params); + if (desc1->encrypted_data.pow_params && desc2->encrypted_data.pow_params) { + hs_pow_desc_params_t *params1 = desc1->encrypted_data.pow_params; + hs_pow_desc_params_t *params2 = desc2->encrypted_data.pow_params; + tt_int_op(params1->type, OP_EQ, params2->type); + tt_mem_op(params1->seed, OP_EQ, params2->seed, HS_POW_SEED_LEN); + tt_int_op(params1->suggested_effort, OP_EQ, params2->suggested_effort); + tt_int_op(params1->expiration_time, OP_EQ, params2->expiration_time); + } + /* Introduction points. */ { tt_assert(desc1->encrypted_data.intro_points); diff --git a/src/test/include.am b/src/test/include.am index 2765cf27d0..b3e3f8bbf2 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -147,8 +147,11 @@ src_test_test_SOURCES += \ src/test/test_circuitstats.c \ src/test/test_compat_libevent.c \ src/test/test_config.c \ + src/test/test_conflux_cell.c \ + src/test/test_conflux_pool.c \ src/test/test_confmgr.c \ src/test/test_confparse.c \ + src/test/test_congestion_control.c \ src/test/test_connection.c \ src/test/test_conscache.c \ src/test/test_consdiff.c \ @@ -186,6 +189,7 @@ src_test_test_SOURCES += \ src/test/test_hs_descriptor.c \ src/test/test_hs_dos.c \ src/test/test_hs_metrics.c \ + src/test/test_hs_pow.c \ src/test/test_keypin.c \ src/test/test_link_handshake.c \ src/test/test_logging.c \ @@ -264,6 +268,7 @@ src_test_test_slow_SOURCES += \ src/test/test_slow.c \ src/test/test_crypto_slow.c \ src/test/test_process_slow.c \ + src/test/test_hs_pow_slow.c \ src/test/test_prob_distr.c \ src/test/ptr_helpers.c \ src/test/test_ptr_slow.c \ diff --git a/src/test/test.c b/src/test/test.c index 6b7e0b6442..2030a8336e 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -778,6 +778,9 @@ struct testgroup_t testgroups[] = { { "config/", config_tests }, { "config/mgr/", confmgr_tests }, { "config/parse/", confparse_tests }, + { "conflux/cell/", conflux_cell_tests }, + { "conflux/pool/", conflux_pool_tests }, + { "congestion_control/", congestion_control_tests }, { "connection/", connection_tests }, { "conscache/", conscache_tests }, { "consdiff/", consdiff_tests }, @@ -822,6 +825,7 @@ struct testgroup_t testgroups[] = { { "hs_metrics/", hs_metrics_tests }, { "hs_ntor/", hs_ntor_tests }, { "hs_ob/", hs_ob_tests }, + { "hs_pow/", hs_pow_tests }, { "hs_service/", hs_service_tests }, { "keypin/", keypin_tests }, { "link-handshake/", link_handshake_tests }, diff --git a/src/test/test.h b/src/test/test.h index e17bce427c..7a405b649e 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -107,8 +107,11 @@ extern struct testcase_t circuitstats_tests[]; extern struct testcase_t circuituse_tests[]; extern struct testcase_t compat_libevent_tests[]; extern struct testcase_t config_tests[]; +extern struct testcase_t conflux_cell_tests[]; +extern struct testcase_t conflux_pool_tests[]; extern struct testcase_t confmgr_tests[]; extern struct testcase_t confparse_tests[]; +extern struct testcase_t congestion_control_tests[]; extern struct testcase_t connection_tests[]; extern struct testcase_t conscache_tests[]; extern struct testcase_t consdiff_tests[]; @@ -145,6 +148,7 @@ extern struct testcase_t hs_intropoint_tests[]; extern struct testcase_t hs_metrics_tests[]; extern struct testcase_t hs_ntor_tests[]; extern struct testcase_t hs_ob_tests[]; +extern struct testcase_t hs_pow_tests[]; extern struct testcase_t hs_service_tests[]; extern struct testcase_t keypin_tests[]; extern struct testcase_t link_handshake_tests[]; @@ -205,6 +209,7 @@ extern struct testcase_t voting_schedule_tests[]; extern struct testcase_t x509_tests[]; extern struct testcase_t slow_crypto_tests[]; +extern struct testcase_t slow_hs_pow_tests[]; extern struct testcase_t slow_process_tests[]; extern struct testcase_t slow_ptr_tests[]; diff --git a/src/test/test_bwmgt.c b/src/test/test_bwmgt.c index a034c369d1..51ad8be59f 100644 --- a/src/test/test_bwmgt.c +++ b/src/test/test_bwmgt.c @@ -243,10 +243,30 @@ test_bwmgt_token_buf_refill(void *arg) tt_int_op(b.read_bucket.bucket, OP_GT, 8*KB-400); tt_int_op(b.read_bucket.bucket, OP_LT, 8*KB+400); - // A ridiculous amount of time passes. - tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, INT32_MAX)); + /* A large amount of time passes, but less than the threshold at which + * we start detecting an assumed rollover event. This might be about 20 + * days on a system with stamp units equal to 1ms. */ + uint32_t ts_stamp = START_TS + UINT32_MAX / 5; + tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, ts_stamp)); tt_int_op(b.read_bucket.bucket, OP_EQ, b.cfg.burst); + /* Fully empty the bucket and make sure it's filling once again */ + token_bucket_rw_dec_read(&b, b.cfg.burst); + tt_int_op(b.read_bucket.bucket, OP_EQ, 0); + tt_int_op(1, OP_EQ, token_bucket_rw_refill(&b, ts_stamp += BW_SEC)); + tt_int_op(b.read_bucket.bucket, OP_GT, 16*KB - 300); + tt_int_op(b.read_bucket.bucket, OP_LT, 16*KB + 300); + + /* An even larger amount of time passes, which we take to be a 32-bit + * rollover event. The individual update is ignored, but the timestamp + * is still updated and the very next update should be accounted properly. */ + tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, ts_stamp += UINT32_MAX/2)); + tt_int_op(b.read_bucket.bucket, OP_GT, 16*KB - 600); + tt_int_op(b.read_bucket.bucket, OP_LT, 16*KB + 600); + tt_int_op(0, OP_EQ, token_bucket_rw_refill(&b, ts_stamp += BW_SEC)); + tt_int_op(b.read_bucket.bucket, OP_GT, 32*KB - 600); + tt_int_op(b.read_bucket.bucket, OP_LT, 32*KB + 600); + done: ; } diff --git a/src/test/test_channel.c b/src/test/test_channel.c index a74d69fefc..a6f403a25c 100644 --- a/src/test/test_channel.c +++ b/src/test/test_channel.c @@ -1264,8 +1264,9 @@ test_channel_duplicates(void *arg) /* Try a flat call with channel nor connections. */ channel_check_for_duplicates(); expect_log_msg_containing( - "Found 0 connections to 0 relays. Found 0 current canonical " - "connections, in 0 of which we were a non-canonical peer. " + "Found 0 connections to authorities, 0 connections to 0 relays. " + "Found 0 current canonical connections, " + "in 0 of which we were a non-canonical peer. " "0 relays had more than 1 connection, 0 had more than 2, and " "0 had more than 4 connections."); @@ -1285,8 +1286,9 @@ test_channel_duplicates(void *arg) /* No relay has been associated with this channel. */ channel_check_for_duplicates(); expect_log_msg_containing( - "Found 0 connections to 0 relays. Found 0 current canonical " - "connections, in 0 of which we were a non-canonical peer. " + "Found 0 connections to authorities, 0 connections to 0 relays. " + "Found 0 current canonical connections, " + "in 0 of which we were a non-canonical peer. " "0 relays had more than 1 connection, 0 had more than 2, and " "0 had more than 4 connections."); @@ -1299,24 +1301,27 @@ test_channel_duplicates(void *arg) chan->state = CHANNEL_STATE_CLOSING; channel_check_for_duplicates(); expect_log_msg_containing( - "Found 0 connections to 0 relays. Found 0 current canonical " - "connections, in 0 of which we were a non-canonical peer. " + "Found 0 connections to authorities, 0 connections to 0 relays. " + "Found 0 current canonical connections, " + "in 0 of which we were a non-canonical peer. " "0 relays had more than 1 connection, 0 had more than 2, and " "0 had more than 4 connections."); chan->state = CHANNEL_STATE_OPEN; channel_check_for_duplicates(); expect_log_msg_containing( - "Found 1 connections to 1 relays. Found 0 current canonical " - "connections, in 0 of which we were a non-canonical peer. " + "Found 0 connections to authorities, 1 connections to 1 relays. " + "Found 0 current canonical connections, " + "in 0 of which we were a non-canonical peer. " "0 relays had more than 1 connection, 0 had more than 2, and " "0 had more than 4 connections."); test_chan_should_be_canonical = 1; channel_check_for_duplicates(); expect_log_msg_containing( - "Found 1 connections to 1 relays. Found 1 current canonical " - "connections, in 1 of which we were a non-canonical peer. " + "Found 0 connections to authorities, 1 connections to 1 relays. " + "Found 1 current canonical connections, " + "in 1 of which we were a non-canonical peer. " "0 relays had more than 1 connection, 0 had more than 2, and " "0 had more than 4 connections."); teardown_capture_of_logs(); diff --git a/src/test/test_channelpadding.c b/src/test/test_channelpadding.c index 261e1f8a37..63c9a3b753 100644 --- a/src/test/test_channelpadding.c +++ b/src/test/test_channelpadding.c @@ -862,7 +862,7 @@ test_channelpadding_decide_to_pad_channel(void *arg) * 2. Channel that has not "sent a packet" before the timeout: * 2a. Not within 1.1s of the timeout. * + We should decide to pad later - * 2b. Within 1.1s of the timemout. + * 2b. Within 1.1s of the timeout. * + We should schedule padding * + We should get feedback that we wrote a cell * 2c. Within 0.1s of the timeout. diff --git a/src/test/test_config.c b/src/test/test_config.c index 3ebe095a6a..a53d0b8227 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -6435,7 +6435,7 @@ test_config_include_opened_file_list(void *data) tt_int_op(smartlist_len(opened_files), OP_EQ, 4); tt_int_op(smartlist_contains_string(opened_files, torrcd), OP_EQ, 1); tt_int_op(smartlist_contains_string(opened_files, subfolder), OP_EQ, 1); - // files inside subfolders are not opended, only the subfolder is opened + // files inside subfolders are not opened, only the subfolder is opened tt_int_op(smartlist_contains_string(opened_files, empty), OP_EQ, 1); tt_int_op(smartlist_contains_string(opened_files, file), OP_EQ, 1); // dot files are not opened as we ignore them when we get their name from diff --git a/src/test/test_conflux_cell.c b/src/test/test_conflux_cell.c new file mode 100644 index 0000000000..bb440d0d0a --- /dev/null +++ b/src/test/test_conflux_cell.c @@ -0,0 +1,60 @@ +/* Copyright (c) 2023, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_conflux_cell. + * \brief Test conflux cells. + */ + +#include "test/test.h" +#include "test/test_helpers.h" +#include "test/log_test_helpers.h" + +#include "core/or/conflux_cell.h" +#include "core/or/conflux_st.h" +#include "trunnel/conflux.h" + +#include "lib/crypt_ops/crypto_rand.h" + +static void +test_link(void *arg) +{ + cell_t cell; + conflux_cell_link_t link; + conflux_cell_link_t *decoded_link = NULL; + + (void) arg; + + memset(&link, 0, sizeof(link)); + + link.desired_ux = CONFLUX_UX_HIGH_THROUGHPUT; + link.last_seqno_recv = 0; + link.last_seqno_sent = 0; + link.version = 0x01; + + crypto_rand((char *) link.nonce, sizeof(link.nonce)); + + ssize_t cell_len = build_link_cell(&link, cell.payload+RELAY_HEADER_SIZE); + tt_int_op(cell_len, OP_GT, 0); + + decoded_link = conflux_cell_parse_link(&cell, cell_len); + tt_assert(decoded_link); + + uint8_t buf[RELAY_PAYLOAD_SIZE]; + ssize_t enc_cell_len = build_link_cell(decoded_link, buf); + tt_int_op(cell_len, OP_EQ, enc_cell_len); + + /* Validate the original link object with the decoded one. */ + tt_mem_op(&link, OP_EQ, decoded_link, sizeof(link)); + tor_free(decoded_link); + + done: + tor_free(decoded_link); +} + +struct testcase_t conflux_cell_tests[] = { + { "link", test_link, TT_FORK, NULL, NULL }, + + END_OF_TESTCASES +}; + diff --git a/src/test/test_conflux_pool.c b/src/test/test_conflux_pool.c new file mode 100644 index 0000000000..fc30677377 --- /dev/null +++ b/src/test/test_conflux_pool.c @@ -0,0 +1,1339 @@ +#define CHANNEL_OBJECT_PRIVATE +#define TOR_TIMERS_PRIVATE +#define TOR_CONFLUX_PRIVATE +#define CIRCUITLIST_PRIVATE +#define NETWORKSTATUS_PRIVATE +#define CRYPT_PATH_PRIVATE +#define RELAY_PRIVATE +#define CONNECTION_PRIVATE +#define TOR_CONGESTION_CONTROL_COMMON_PRIVATE +#define TOR_CONGESTION_CONTROL_PRIVATE + +#include "core/or/or.h" +#include "test/test.h" +#include "test/log_test_helpers.h" +#include "lib/testsupport/testsupport.h" +#include "core/or/connection_or.h" +#include "core/or/channel.h" +#include "core/or/channeltls.h" +#include "core/or/crypt_path.h" +#include <event.h> +#include "lib/evloop/compat_libevent.h" +#include "lib/time/compat_time.h" +#include "lib/defs/time.h" +#include "core/or/relay.h" +#include "core/or/circuitlist.h" +#include "core/or/circuitstats.h" +#include "core/or/circuitbuild.h" +#include "core/or/circuituse.h" +#include "core/or/congestion_control_st.h" +#include "core/or/congestion_control_common.h" +#include "core/or/extendinfo.h" +#include "core/mainloop/netstatus.h" +#include "core/crypto/relay_crypto.h" +#include "core/or/protover.h" +#include "feature/nodelist/nodelist.h" +#include "app/config/config.h" + +#include "feature/nodelist/routerstatus_st.h" +#include "feature/nodelist/networkstatus_st.h" +#include "feature/nodelist/node_st.h" +#include "core/or/cell_st.h" +#include "core/or/crypt_path_st.h" +#include "core/or/or_circuit_st.h" +#include "core/or/origin_circuit_st.h" + +#include "core/mainloop/connection.h" +#include "core/or/connection_edge.h" +#include "core/or/edge_connection_st.h" + +#include "test/fakecircs.h" +#include "test/rng_test_helpers.h" +#include "core/or/conflux_pool.h" +#include "core/or/conflux_util.h" +#include "core/or/conflux_params.h" +#include "core/or/conflux.h" +#include "core/or/conflux_st.h" +#include "trunnel/conflux.h" +#include "lib/crypt_ops/crypto_rand.h" + +/* Start our monotime mocking at 1 second past whatever monotime_init() + * thought the actual wall clock time was, for platforms with bad resolution + * and weird timevalues during monotime_init() before mocking. */ +#define MONOTIME_MOCK_START (monotime_absolute_nsec()+\ + TOR_NSEC_PER_USEC*TOR_USEC_PER_SEC) + +extern smartlist_t *connection_array; +void circuit_expire_old_circuits_clientside(void); + +circid_t get_unique_circ_id_by_chan(channel_t *chan); + +channel_t *new_fake_channel(void); + +static void simulate_single_hop_extend(origin_circuit_t *client, int exit); +static void free_fake_origin_circuit(origin_circuit_t *circ); +static circuit_t * get_exit_circ(circuit_t *client_circ); +static circuit_t * get_client_circ(circuit_t *exit_circ); +static void simulate_circuit_build(circuit_t *client_circ); + +static int64_t curr_mocked_time; + +static channel_t dummy_channel; + +static void +timers_advance_and_run(int64_t msec_update) +{ + curr_mocked_time += msec_update*TOR_NSEC_PER_MSEC; + monotime_coarse_set_mock_time_nsec(curr_mocked_time); + monotime_set_mock_time_nsec(curr_mocked_time); +} + +/* These lists of circuit endpoints send to eachother via + * circuit_package_relay_cell_mocked */ +static smartlist_t *client_circs; +static smartlist_t *exit_circs; +static smartlist_t *client_streams; +static smartlist_t *exit_streams; + +typedef struct { + circuit_t *client; + circuit_t *exit; +} circ_pair_t; +static smartlist_t *circ_pairs; + +static void +simulate_circuit_built(circuit_t *client, circuit_t *exit) +{ + circ_pair_t *pair = tor_malloc_zero(sizeof(circ_pair_t)); + pair->client = client; + pair->exit = exit; + smartlist_add(circ_pairs, pair); +} + +static origin_circuit_t * +circuit_establish_circuit_conflux_mock(const uint8_t *conflux_nonce, + uint8_t purpose, extend_info_t *exit_ei, + int flags) +{ + (void)exit_ei; + (void)flags; + origin_circuit_t *circ = origin_circuit_init(purpose, flags); + circ->base_.conflux_pending_nonce = tor_memdup(conflux_nonce, DIGEST256_LEN); + circ->base_.purpose = CIRCUIT_PURPOSE_CONFLUX_UNLINKED; + smartlist_add(client_circs, circ); + + // This also moves the clock forward as if these hops were opened.. + // Not a problem, unless we want to accurately test buildtimeouts + simulate_single_hop_extend(circ, 0); + simulate_single_hop_extend(circ, 0); + simulate_single_hop_extend(circ, 1); + circ->cpath->prev->ccontrol = tor_malloc_zero(sizeof(congestion_control_t)); + circ->cpath->prev->ccontrol->sendme_pending_timestamps = smartlist_new(); + circ->cpath->prev->ccontrol->sendme_inc = 31; + + return circ; +} + +static void +free_fake_origin_circuit(origin_circuit_t *circ) +{ + circuit_clear_cpath(circ); + tor_free(circ); +} + +void dummy_nop_timer(void); + +static int +circuit_package_relay_cell_mock(cell_t *cell, circuit_t *circ, + cell_direction_t cell_direction, + crypt_path_t *layer_hint, streamid_t on_stream, + const char *filename, int lineno); + +static void +circuitmux_attach_circuit_mock(circuitmux_t *cmux, circuit_t *circ, + cell_direction_t direction); + +static void +circuitmux_attach_circuit_mock(circuitmux_t *cmux, circuit_t *circ, + cell_direction_t direction) +{ + (void)cmux; + (void)circ; + (void)direction; + + return; +} +/* For use in the mock_net smartlist queue: + * this struct contains a circuit and a cell to + * deliver on it. */ +typedef struct { + circuit_t *circ; + cell_t *cell; +} cell_delivery_t; + +static smartlist_t *mock_cell_delivery = NULL; + +static int +circuit_package_relay_cell_mock(cell_t *cell, circuit_t *circ, + cell_direction_t cell_direction, + crypt_path_t *layer_hint, streamid_t on_stream, + const char *filename, int lineno) +{ + (void)cell; (void)on_stream; (void)filename; (void)lineno; + (void)cell_direction; + circuit_t *dest_circ = NULL; + + // If we have a layer hint, we are sending to the exit. Look + // up the exit circ based on our circuit index in the smartlist + if (layer_hint) { + tor_assert(CIRCUIT_IS_ORIGIN(circ)); + tt_int_op(cell_direction, OP_EQ, CELL_DIRECTION_OUT); + + // Search the circ pairs list for the pair whose client is this circ, + // and set dest_circ to the exit circ in that pair + SMARTLIST_FOREACH_BEGIN(circ_pairs, circ_pair_t *, pair) { + if (pair->client == circ) { + dest_circ = pair->exit; + break; + } + } SMARTLIST_FOREACH_END(pair); + } else { + tt_int_op(cell_direction, OP_EQ, CELL_DIRECTION_IN); + + // Search the circ pairs list for the pair whose exit is this circ, + // and set dest_circ to the client circ in that pair + SMARTLIST_FOREACH_BEGIN(circ_pairs, circ_pair_t *, pair) { + if (pair->exit == circ) { + dest_circ = pair->client; + break; + } + } SMARTLIST_FOREACH_END(pair); + } + + cell_delivery_t *delivery = tor_malloc_zero(sizeof(cell_delivery_t)); + delivery->circ = dest_circ; + delivery->cell = tor_memdup(cell, sizeof(cell_t)); + smartlist_add(mock_cell_delivery, delivery); + done: + return 0; +} + +/** Pull the next cell from the mock delivery queue and deliver it. */ +static void +process_mock_cell_delivery(void) +{ + relay_header_t rh; + + cell_delivery_t *delivery = smartlist_pop_last(mock_cell_delivery); + tor_assert(delivery); + cell_t *cell = delivery->cell; + circuit_t *dest_circ = delivery->circ; + relay_header_unpack(&rh, cell->payload); + + timers_advance_and_run(1); + + switch (cell->payload[0]) { + case RELAY_COMMAND_CONFLUX_LINK: + tor_assert(!CIRCUIT_IS_ORIGIN(dest_circ)); + conflux_process_link(dest_circ, cell, rh.length); + break; + case RELAY_COMMAND_CONFLUX_LINKED: + tor_assert(CIRCUIT_IS_ORIGIN(dest_circ)); + conflux_process_linked(dest_circ, + TO_ORIGIN_CIRCUIT(dest_circ)->cpath->prev, + cell, rh.length); + break; + case RELAY_COMMAND_CONFLUX_LINKED_ACK: + tor_assert(!CIRCUIT_IS_ORIGIN(dest_circ)); + conflux_process_linked_ack(dest_circ); + break; + case RELAY_COMMAND_CONFLUX_SWITCH: + // We only test the case where the switch is initiated by the client. + // It is symmetric, so this should not matter. If we ever want to test + // the case where the switch is initiated by the exit, we will need to + // get the cpath layer hint for the client. + tor_assert(!CIRCUIT_IS_ORIGIN(dest_circ)); + conflux_process_switch_command(dest_circ, NULL, cell, &rh); + break; + } + + tor_free(delivery); + tor_free(cell); + return; +} + +static uint64_t +mock_monotime_absolute_usec(void) +{ + return 100; +} + +static int +channel_get_addr_if_possible_mock(const channel_t *chan, tor_addr_t *addr_out) +{ + (void)chan; + tt_int_op(AF_INET,OP_EQ, tor_addr_parse(addr_out, "18.0.0.1")); + return 1; + + done: + return 0; +} + +static void +circuit_mark_for_close_mock(circuit_t *circ, int reason, + int line, const char *file) +{ + (void)circ; + (void)reason; + (void)line; + (void)file; + + log_info(LD_CIRC, "Marking circuit for close at %s:%d", file, line); + + if (BUG(circ->marked_for_close)) { + log_warn(LD_BUG, + "Duplicate call to circuit_mark_for_close at %s:%d" + " (first at %s:%d)", file, line, + circ->marked_for_close_file, circ->marked_for_close); + return; + } + + circ->marked_for_close = line; + circ->marked_for_close_file = file; + circ->marked_for_close_reason = reason; + + if (CIRCUIT_IS_CONFLUX(circ)) { + conflux_circuit_has_closed(circ); + } + + // Mark the other side for close too. No idea if this even improves things; + // We might also want to make this go through the cell queue as a destroy + if (CIRCUIT_IS_ORIGIN(circ)) { + circuit_t *exit_circ = get_exit_circ(circ); + if (!exit_circ->marked_for_close) + circuit_mark_for_close_mock(get_exit_circ(circ), reason, line, file); + } else { + circuit_t *client_circ = get_client_circ(circ); + if (!client_circ->marked_for_close) + circuit_mark_for_close_mock(get_client_circ(circ), reason, line, file); + } + + // XXX: Should we do this? + //if (circuits_pending_close == NULL) + // circuits_pending_close = smartlist_new(); + //smartlist_add(circuits_pending_close, circ); +} + +static void +simulate_single_hop_extend(origin_circuit_t *client, int exit) +{ + char whatevs_key[CPATH_KEY_MATERIAL_LEN]; + char digest[DIGEST_LEN]; + tor_addr_t addr; + + // Advance time a tiny bit so we can calculate an RTT + curr_mocked_time += 10 * TOR_NSEC_PER_MSEC; + monotime_coarse_set_mock_time_nsec(curr_mocked_time); + monotime_set_mock_time_nsec(curr_mocked_time); + + // Add a hop to cpath + crypt_path_t *hop = tor_malloc_zero(sizeof(crypt_path_t)); + cpath_extend_linked_list(&client->cpath, hop); + + hop->magic = CRYPT_PATH_MAGIC; + hop->state = CPATH_STATE_OPEN; + + // add an extend info to indicate if this node supports padding or not. + // (set the first byte of the digest for our mocked node_get_by_id) + digest[0] = exit; + + hop->extend_info = extend_info_new( + exit ? "exit" : "non-exit", + digest, NULL, NULL, NULL, + &addr, exit, NULL, exit); + + cpath_init_circuit_crypto(hop, whatevs_key, sizeof(whatevs_key), 0, 0); + + hop->package_window = circuit_initial_package_window(); + hop->deliver_window = CIRCWINDOW_START; +} + +static void +test_setup(void) +{ + int64_t actual_mocked_monotime_start; + + MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock); + MOCK(channel_get_addr_if_possible, channel_get_addr_if_possible_mock); + MOCK(circuit_establish_circuit_conflux, + circuit_establish_circuit_conflux_mock); + MOCK(circuit_package_relay_cell, + circuit_package_relay_cell_mock); + MOCK(circuit_mark_for_close_, + circuit_mark_for_close_mock); + MOCK(monotime_absolute_usec, mock_monotime_absolute_usec); + testing_enable_reproducible_rng(); + + monotime_init(); + monotime_enable_test_mocking(); + actual_mocked_monotime_start = MONOTIME_MOCK_START; + monotime_set_mock_time_nsec(actual_mocked_monotime_start); + monotime_coarse_set_mock_time_nsec(actual_mocked_monotime_start); + curr_mocked_time = actual_mocked_monotime_start; + + client_circs = smartlist_new(); + exit_circs = smartlist_new(); + circ_pairs = smartlist_new(); + mock_cell_delivery = smartlist_new(); + dummy_channel.cmux = circuitmux_alloc(); + + get_circuit_build_times_mutable()->timeout_ms = 1000; + + congestion_control_set_cc_enabled(); + max_unlinked_leg_retry = UINT32_MAX; +} + +static void +test_clear_circs(void) +{ + SMARTLIST_FOREACH(circ_pairs, circ_pair_t *, circ_pair, { + tor_free(circ_pair); + }); + SMARTLIST_FOREACH(client_circs, circuit_t *, client_side, { + conflux_circuit_about_to_free(client_side); + circuit_free(client_side); + }); + SMARTLIST_FOREACH(exit_circs, or_circuit_t *, relay_side, { + free_fake_orcirc(relay_side); + }); + + smartlist_clear(circ_pairs); + smartlist_clear(client_circs); + smartlist_clear(exit_circs); + + if (client_streams) { + // Free each edge connection + SMARTLIST_FOREACH(client_streams, edge_connection_t *, edge_conn, { + connection_free_minimal(TO_CONN(edge_conn)); + }); + smartlist_free(client_streams); + } + + if (exit_streams) { + // Free each edge connection + SMARTLIST_FOREACH(exit_streams, edge_connection_t *, edge_conn, { + connection_free_minimal(TO_CONN(edge_conn)); + }); + smartlist_free(exit_streams); + } + + tor_assert(smartlist_len(mock_cell_delivery) == 0); + + (void)free_fake_origin_circuit; +} + +static void +test_teardown(void) +{ + conflux_pool_free_all(); + smartlist_free(client_circs); + smartlist_free(exit_circs); + smartlist_free(mock_cell_delivery); + circuitmux_detach_all_circuits(dummy_channel.cmux, NULL); + circuitmux_free(dummy_channel.cmux); + testing_disable_reproducible_rng(); +} + +/* Test linking a conflux circuit */ +static void +test_conflux_link(void *arg) +{ + (void) arg; + test_setup(); + + launch_new_set(2); + + // For each circuit in the client_circs list, we need to create an + // exit side circuit and simulate two extends + SMARTLIST_FOREACH(client_circs, circuit_t *, client_side, { + simulate_circuit_build(client_side); + + /* Handle network activity*/ + while (smartlist_len(mock_cell_delivery) > 0) { + process_mock_cell_delivery(); + } + }); + + tt_int_op(smartlist_len(client_circs), OP_EQ, 2); + tt_int_op(smartlist_len(exit_circs), OP_EQ, 2); + + // Test that the cells have all been delivered + tt_int_op(smartlist_len(mock_cell_delivery), OP_EQ, 0); + + // Test that the client side circuits are linked + conflux_t *cfx = ((circuit_t*)smartlist_get(client_circs, 0))->conflux; + SMARTLIST_FOREACH_BEGIN(client_circs, circuit_t *, client_side) { + tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED); + tt_ptr_op(client_side->conflux, OP_EQ, cfx); + tt_ptr_op(client_side->conflux_pending_nonce, OP_EQ, NULL); + } SMARTLIST_FOREACH_END(client_side); + + // Test circuit teardown + SMARTLIST_FOREACH_BEGIN(client_circs, circuit_t *, client_side) { + circuit_mark_for_close(client_side, END_CIRC_REASON_FINISHED); + } SMARTLIST_FOREACH_END(client_side); + + done: + test_clear_circs(); + test_teardown(); +} + +static void +simulate_circuit_build(circuit_t *client_circ) +{ + // Create a relay circuit, and simulate the extend and open + circuit_t *relay_side = NULL; + + relay_side = (circuit_t*)new_fake_orcirc(&dummy_channel, &dummy_channel); + relay_side->purpose = CIRCUIT_PURPOSE_OR; + relay_side->n_chan = NULL; // No next hop + relay_side->ccontrol = tor_malloc_zero(sizeof(congestion_control_t)); + relay_side->ccontrol->sendme_pending_timestamps = smartlist_new(); + relay_side->ccontrol->sendme_inc = 31; + smartlist_add(exit_circs, relay_side); + simulate_circuit_built(client_circ, relay_side); + conflux_circuit_has_opened(TO_ORIGIN_CIRCUIT(client_circ)); +} + +static circuit_t * +simulate_close_retry(circuit_t *close, bool manual_launch) +{ + // Find the dest pair for the circuit in the circ pair list, + // and close it too + circuit_t *dest = NULL; + uint8_t *nonce = NULL; + + if (manual_launch) { + nonce = tor_memdup(close->conflux->nonce, DIGEST256_LEN); + } + + SMARTLIST_FOREACH_BEGIN(circ_pairs, circ_pair_t *, pair) { + if (pair->client == close) { + dest = pair->exit; + SMARTLIST_DEL_CURRENT_KEEPORDER(circ_pairs, pair); + tor_free(pair); + } else if (pair->exit == close) { + // This function should not be called on the exit side.. + tor_assert(0); + } + } SMARTLIST_FOREACH_END(pair); + + tor_assert(dest); + log_info(LD_CIRC, "Simulating close of %p->%p, dest %p->%p", + close, close->conflux, dest, dest->conflux); + + // Free all pending cells related to this close in mock_cell_delivery + SMARTLIST_FOREACH(mock_cell_delivery, cell_delivery_t *, cd, { + if (cd->circ == close || cd->circ == dest) { + SMARTLIST_DEL_CURRENT_KEEPORDER(mock_cell_delivery, cd); + tor_free(cd->cell); + tor_free(cd); + } + }); + + // When a circuit closes, both ends get notification, + // and the client will launch a new circuit. We need to find + // that circuit at the end of the list, and then simulate + // building it, and creating a relay circuit for it. + conflux_circuit_has_closed(close); + conflux_circuit_has_closed(dest); + + //tor_assert(digest256map_size(get_unlinked_pool(true)) != 0); + + // Find these legs in our circuit lists, and free them + tor_assert(CIRCUIT_IS_ORIGIN(close)); + tor_assert(!CIRCUIT_IS_ORIGIN(dest)); + SMARTLIST_FOREACH_BEGIN(client_circs, circuit_t *, client_side) { + if (client_side == close) { + SMARTLIST_DEL_CURRENT_KEEPORDER(client_circs, client_side); + conflux_circuit_about_to_free(client_side); + circuit_free(client_side); + } + } SMARTLIST_FOREACH_END(client_side); + SMARTLIST_FOREACH_BEGIN(exit_circs, or_circuit_t *, exit_side) { + if (exit_side == (or_circuit_t *)dest) { + SMARTLIST_DEL_CURRENT_KEEPORDER(exit_circs, exit_side); + free_fake_orcirc(exit_side); + } + } SMARTLIST_FOREACH_END(exit_side); + + if (manual_launch) { + // Launch a new leg for this nonce + tor_assert(nonce); + conflux_launch_leg(nonce); + tor_free(nonce); + } + + if (smartlist_len(client_circs) == 0) { + // No new circuit was launched + return NULL; + } + + // At this point, a new circuit will have launched on the client + // list. Get that circuit from the end of the list and return it + circuit_t * circ = smartlist_get(client_circs, + smartlist_len(client_circs) - 1); + + //tor_assert(circ->purpose == CIRCUIT_PURPOSE_CONFLUX_UNLINKED); + + return circ; +} + +static void +test_retry(void) +{ + log_info(LD_CIRC, "==========NEW RUN ==========="); + launch_new_set(2); + + tt_int_op(smartlist_len(client_circs), OP_EQ, 2); + circuit_t *client1 = smartlist_get(client_circs, 0); + circuit_t *client2 = smartlist_get(client_circs, 1); + + get_circuit_build_times_mutable()->timeout_ms = 1000; + + // Dice roll on which leg builds first + if (crypto_rand_int(2) == 0) { + simulate_circuit_build(client1); + simulate_circuit_build(client2); + } else { + simulate_circuit_build(client2); + simulate_circuit_build(client1); + } + + while (smartlist_len(mock_cell_delivery) > 0) { + tt_int_op(smartlist_len(client_circs), OP_EQ, 2); + tt_int_op(smartlist_len(exit_circs), OP_EQ, 2); + tt_int_op(smartlist_len(circ_pairs), OP_EQ, 2); + + if (crypto_rand_int(2) == 0) { + if (crypto_rand_int(2) == 0) { + if (client1->purpose != CIRCUIT_PURPOSE_CONFLUX_LINKED) { + client1 = simulate_close_retry(client1, false); + simulate_circuit_build(client1); + } + } else { + if (client2->purpose != CIRCUIT_PURPOSE_CONFLUX_LINKED) { + client2 = simulate_close_retry(client2, false); + simulate_circuit_build(client2); + } + } + } + + process_mock_cell_delivery(); + } + + // Test that the cells have all been delivered + tt_int_op(smartlist_len(mock_cell_delivery), OP_EQ, 0); + tt_int_op(smartlist_len(client_circs), OP_EQ, 2); + tt_int_op(smartlist_len(exit_circs), OP_EQ, 2); + tt_int_op(smartlist_len(circ_pairs), OP_EQ, 2); + + conflux_t *cfx = ((circuit_t *)smartlist_get(client_circs, 0))->conflux; + SMARTLIST_FOREACH_BEGIN(client_circs, circuit_t *, client_side) { + tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED); + tt_ptr_op(client_side->conflux, OP_EQ, cfx); + tt_ptr_op(client_side->conflux_pending_nonce, OP_EQ, NULL); + } SMARTLIST_FOREACH_END(client_side); + + tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 1); + tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0); + tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0); + tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 1); + + cfx = ((circuit_t *)smartlist_get(exit_circs, 0))->conflux; + SMARTLIST_FOREACH_BEGIN(exit_circs, circuit_t *, exit_side) { + tt_ptr_op(exit_side->conflux, OP_EQ, cfx); + tt_ptr_op(exit_side->conflux_pending_nonce, OP_EQ, NULL); + } SMARTLIST_FOREACH_END(exit_side); + + // Test circuit teardown + SMARTLIST_FOREACH_BEGIN(client_circs, circuit_t *, client_side) { + tt_int_op(client_side->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED); + circuit_mark_for_close(client_side, END_CIRC_REASON_FINISHED); + } SMARTLIST_FOREACH_END(client_side); + + tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 0); + tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0); + tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0); + tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 0); + + test_clear_circs(); + + done: + return; +} + +/* Test linking a conflux circuit with build failures */ +static void +test_conflux_link_retry(void *arg) +{ + (void) arg; + test_setup(); + + for (int i = 0; i < 500; i++) { + test_retry(); + } + + test_teardown(); +} + +#if 0 +/* Test closing both circuits in the set before the link handshake completes + * on either leg, by closing circuits before process_mock_cell_delivery. + * + * XXX: This test currently fails because conflux keeps relaunching closed + * circuits. We need to set a limit on the number of times we relaunch a + * circuit before we can fix this test. + */ +static void +test_conflux_link_fail(void *arg) +{ + (void) arg; + test_setup(); + + launch_new_set(2); + + tt_int_op(smartlist_len(client_circs), OP_EQ, 2); + circuit_t *client1 = smartlist_get(client_circs, 0); + circuit_t *client2 = smartlist_get(client_circs, 1); + + get_circuit_build_times_mutable()->timeout_ms = 1000; + + // Close both circuits before the link handshake completes + conflux_circuit_has_closed(client1); + conflux_circuit_has_closed(client2); + + tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 0); + tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0); + tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0); + tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 0); + done: + test_clear_circs(); + test_teardown(); +} +#endif + +// - Relink test: +// - More than 2 legs +// - Close one linked leg; relink +// - Test mismatching sequence numbers for link and data +// - This should destroy the whole set +// - RTT timeout relinking test +// - Three circuits; close 1; retry and link +static void +test_conflux_link_relink(void *arg) +{ + (void) arg; + test_setup(); + + launch_new_set(3); + + tt_int_op(smartlist_len(client_circs), OP_EQ, 3); + circuit_t *client1 = smartlist_get(client_circs, 0); + circuit_t *client2 = smartlist_get(client_circs, 1); + circuit_t *client3 = smartlist_get(client_circs, 2); + + get_circuit_build_times_mutable()->timeout_ms = 1000; + + simulate_circuit_build(client1); + simulate_circuit_build(client2); + simulate_circuit_build(client3); + + while (smartlist_len(mock_cell_delivery) > 0) { + tt_int_op(smartlist_len(client_circs), OP_EQ, 3); + tt_int_op(smartlist_len(exit_circs), OP_EQ, 3); + tt_int_op(smartlist_len(circ_pairs), OP_EQ, 3); + + process_mock_cell_delivery(); + } + + // Now test closing and relinking the third leg + client3 = simulate_close_retry(client3, true); + simulate_circuit_build(client3); + while (smartlist_len(mock_cell_delivery) > 0) { + tt_int_op(smartlist_len(client_circs), OP_EQ, 3); + tt_int_op(smartlist_len(exit_circs), OP_EQ, 3); + tt_int_op(smartlist_len(circ_pairs), OP_EQ, 3); + + process_mock_cell_delivery(); + } + + tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 1); + tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0); + tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0); + tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 1); + + // Now test closing all circuits and verify the conflux object is gone + simulate_close_retry(client1, false); + simulate_close_retry(client2, false); + simulate_close_retry(client3, false); + + tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 0); + tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0); + tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0); + tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 0); + + done: + test_clear_circs(); + test_teardown(); +} + +#if 0 +static void +test_conflux_close(void *arg) +{ + (void) arg; + test_setup(); + + launch_new_set(2); + + tt_int_op(smartlist_len(client_circs), OP_EQ, 2); + circuit_t *client1 = smartlist_get(client_circs, 0); + circuit_t *client2 = smartlist_get(client_circs, 1); + + get_circuit_build_times_mutable()->timeout_ms = 1000; + + simulate_circuit_build(client1); + simulate_circuit_build(client2); + + while (smartlist_len(mock_cell_delivery) > 0) { + tt_int_op(smartlist_len(client_circs), OP_EQ, 2); + tt_int_op(smartlist_len(exit_circs), OP_EQ, 2); + tt_int_op(smartlist_len(circ_pairs), OP_EQ, 2); + + process_mock_cell_delivery(); + } + + // There are actually 3 kinds of close: mark, mark+free, + // and purpose change. We need to test these in link_retry, but + // here our focus is on after the set is linked. + + // Additionally, we can close on an unlinked leg, or a non-critical linked + // leg, or a critical linked leg that causes teardown + // And we can close on linked legs when there are unlinked legs, or not. + + // And we can do this at the client, or the exit. + // And we can do this with a circuit that has streams, or not. + + tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 0); + tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0); + tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0); + tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 0); + done: + test_clear_circs(); + test_teardown(); +} +#endif + +// Test launching a new set and closing the first leg, but +// with mismatched sequence numbers (missing data) +// - Test this teardown with only linked circs, and with some +// unlinked circs +// Test mismatching sequence numbers for link and data: +// Test missing sent data from client (link cell mismatch): +// Test missing sent data from relay (linked cell mismatch): + +static circuit_t * +get_exit_circ(circuit_t *client_circ) +{ + circuit_t *exit_circ = NULL; + SMARTLIST_FOREACH_BEGIN(circ_pairs, circ_pair_t *, pair) { + if (pair->client == client_circ) { + exit_circ = pair->exit; + break; + } + } SMARTLIST_FOREACH_END(pair); + tor_assert(exit_circ); + return exit_circ; +} + +static circuit_t * +get_client_circ(circuit_t *exit_circ) +{ + circuit_t *client_circ = NULL; + SMARTLIST_FOREACH_BEGIN(circ_pairs, circ_pair_t *, pair) { + if (pair->exit == exit_circ) { + client_circ = pair->client; + break; + } + } SMARTLIST_FOREACH_END(pair); + tor_assert(client_circ); + return client_circ; +} + +static edge_connection_t * +new_client_stream(origin_circuit_t *on_circ) +{ + edge_connection_t *stream = edge_connection_new(CONN_TYPE_EXIT, AF_INET); + + stream->stream_id = get_unique_stream_id_by_circ(on_circ); + stream->on_circuit = TO_CIRCUIT(on_circ); + stream->cpath_layer = on_circ->cpath->prev; + + stream->next_stream = on_circ->p_streams; + on_circ->p_streams = stream; + conflux_update_p_streams(on_circ, stream); + + smartlist_add(client_streams, stream); + + return stream; +} + +static edge_connection_t * +new_exit_stream(circuit_t *on_circ, streamid_t stream_id) +{ + edge_connection_t *stream = edge_connection_new(CONN_TYPE_EXIT, AF_INET); + + stream->stream_id = stream_id; + stream->on_circuit = on_circ; + + stream->next_stream = TO_OR_CIRCUIT(on_circ)->n_streams; + conflux_update_n_streams(TO_OR_CIRCUIT(on_circ), stream); + + smartlist_add(exit_streams, stream); + + return stream; +} + +static void +validate_stream_counts(circuit_t *circ, int expected) +{ + int count = 0; + + conflux_validate_stream_lists(circ->conflux); + + if (CIRCUIT_IS_ORIGIN(circ)) { + origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); + tor_assert_nonfatal(circ->purpose == CIRCUIT_PURPOSE_CONFLUX_LINKED); + /* Iterate over stream list using next_stream pointer, until null */ + for (edge_connection_t *stream = ocirc->p_streams; stream; + stream = stream->next_stream) { + count++; + } + } else { + or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); + /* Iterate over stream list using next_stream pointer, until null */ + for (edge_connection_t *stream = orcirc->n_streams; stream; + stream = stream->next_stream) { + count++; + } + } + tt_int_op(count, OP_EQ, expected); + + done: + return; +} + +// - Streams test +// - Attach streams +// - Fail one leg, free it, attach new leg, new stream +// - Fail both legs +// - Shutdown +// - With streams attached +static void +test_conflux_link_streams(void *arg) +{ + (void) arg; + test_setup(); + + launch_new_set(2); + + client_streams = smartlist_new(); + exit_streams = smartlist_new(); + + tt_int_op(smartlist_len(client_circs), OP_EQ, 2); + circuit_t *client1 = smartlist_get(client_circs, 0); + circuit_t *client2 = smartlist_get(client_circs, 1); + + get_circuit_build_times_mutable()->timeout_ms = 1000; + + simulate_circuit_build(client1); + simulate_circuit_build(client2); + + while (smartlist_len(mock_cell_delivery) > 0) { + tt_int_op(smartlist_len(client_circs), OP_EQ, 2); + tt_int_op(smartlist_len(exit_circs), OP_EQ, 2); + tt_int_op(smartlist_len(circ_pairs), OP_EQ, 2); + + process_mock_cell_delivery(); + } + + // Attach a stream to the client1 circuit + new_client_stream(TO_ORIGIN_CIRCUIT(client1)); + new_client_stream(TO_ORIGIN_CIRCUIT(client2)); + new_client_stream(TO_ORIGIN_CIRCUIT(client1)); + new_exit_stream(get_exit_circ(client2), 1); + new_exit_stream(get_exit_circ(client1), 1); + new_exit_stream(get_exit_circ(client1), 1); + + // Test that we can close the first leg, and attach a new one + // with a new stream + client1 = simulate_close_retry(client1, true); + simulate_circuit_build(client1); + + while (smartlist_len(mock_cell_delivery) > 0) { + tt_int_op(smartlist_len(client_circs), OP_EQ, 2); + tt_int_op(smartlist_len(exit_circs), OP_EQ, 2); + tt_int_op(smartlist_len(circ_pairs), OP_EQ, 2); + + process_mock_cell_delivery(); + } + + tt_ptr_op(client1->conflux, OP_EQ, client2->conflux); + + new_client_stream(TO_ORIGIN_CIRCUIT(client1)); + new_exit_stream(get_exit_circ(client2), 1); + + // Use Ensure that there are four streams on each circuit + validate_stream_counts(client1, 4); + validate_stream_counts(client2, 4); + validate_stream_counts(get_exit_circ(client1), 4); + validate_stream_counts(get_exit_circ(client2), 4); + + // Test that we can close all streams on either circuit, + // in any order + circuit_detach_stream(get_exit_circ(client1), + TO_OR_CIRCUIT(get_exit_circ(client1))->n_streams); + validate_stream_counts(get_exit_circ(client2), 3); + circuit_detach_stream(get_exit_circ(client2), + TO_OR_CIRCUIT(get_exit_circ(client2))->n_streams->next_stream); + validate_stream_counts(get_exit_circ(client1), 2); + circuit_detach_stream(get_exit_circ(client1), + TO_OR_CIRCUIT(get_exit_circ(client1))->n_streams); + validate_stream_counts(get_exit_circ(client1), 1); + circuit_detach_stream(get_exit_circ(client1), + TO_OR_CIRCUIT(get_exit_circ(client1))->n_streams); + validate_stream_counts(get_exit_circ(client1), 0); + + circuit_detach_stream(client1, + TO_ORIGIN_CIRCUIT(client1)->p_streams->next_stream-> + next_stream->next_stream); + circuit_detach_stream(client2, + TO_ORIGIN_CIRCUIT(client2)->p_streams); + circuit_detach_stream(client2, + TO_ORIGIN_CIRCUIT(client2)->p_streams->next_stream); + circuit_detach_stream(client2, + TO_ORIGIN_CIRCUIT(client2)->p_streams); + validate_stream_counts(client1, 0); + validate_stream_counts(client2, 0); + + done: + test_clear_circs(); + test_teardown(); +} + +// Right now this does not involve congestion control.. But it could, +// if we actually build and send real RELAY_DATA cells (and also handle them +// and SENDME cells in the mocked cell delivery) +static void +send_fake_cell(circuit_t *client_circ) +{ + circuit_t *exit_circ = get_exit_circ(client_circ); + conflux_leg_t *exit_leg = conflux_get_leg(exit_circ->conflux, + exit_circ); + + TO_ORIGIN_CIRCUIT(client_circ)->cpath->prev->ccontrol->inflight++; + conflux_note_cell_sent(client_circ->conflux, client_circ, + RELAY_COMMAND_DATA); + + exit_leg->last_seq_recv++; + exit_circ->conflux->last_seq_delivered++; +} + +static circuit_t * +send_until_switch(circuit_t *client_circ) +{ + conflux_leg_t *client_leg = conflux_get_leg(client_circ->conflux, + client_circ); + circuit_t *exit_circ = get_exit_circ(client_circ); + conflux_leg_t *exit_leg = conflux_get_leg(exit_circ->conflux, + exit_circ); + circuit_t *next_circ = client_circ; + int i = 0; + + // XXX: This is a hack so the tests pass using cc->sendme_inc + // (There is another hack in circuit_ready_to_send() that causes + // us to block early below, and return NULL for next_circ) + TO_ORIGIN_CIRCUIT(client_circ)->cpath->prev->ccontrol->sendme_inc = 0; + + while (client_circ == next_circ) { + next_circ = conflux_decide_circ_for_send(client_circ->conflux, client_circ, + RELAY_COMMAND_DATA); + tor_assert(next_circ); + send_fake_cell(next_circ); + i++; + } + + // XXX: This too: + TO_ORIGIN_CIRCUIT(client_circ)->cpath->prev->ccontrol->sendme_inc = 31; + + log_info(LD_CIRC, "Sent %d cells on client circ", i-1); + process_mock_cell_delivery(); + + circuit_t *new_client = + (circuit_t*)conflux_decide_next_circ(client_circ->conflux); + tt_ptr_op(new_client, OP_NE, client_circ); + conflux_leg_t *new_client_leg = conflux_get_leg(new_client->conflux, + new_client); + circuit_t *new_exit = get_exit_circ(new_client); + conflux_leg_t *new_exit_leg = conflux_get_leg(new_exit->conflux, + new_exit); + + // Verify sequence numbers make sense + tt_int_op(new_client_leg->last_seq_sent, OP_EQ, client_leg->last_seq_sent+1); + tt_int_op(new_client_leg->last_seq_recv, OP_EQ, client_leg->last_seq_recv); + tt_int_op(exit_leg->last_seq_sent, OP_EQ, new_exit_leg->last_seq_sent); + tt_int_op(exit_leg->last_seq_recv+1, OP_EQ, new_exit_leg->last_seq_recv); + + tt_int_op(client_leg->last_seq_sent+1, OP_EQ, new_exit_leg->last_seq_recv); + tt_int_op(client_leg->last_seq_recv, OP_EQ, new_exit_leg->last_seq_sent); + + done: + return new_client; +} + +/** + * This tests switching as well as the UDP optimization that attaches + * a third circuit and closes the slowest one. (This optimization is not + * implemented in C-Tor but must be supported at exits, for arti). + */ +static void +test_conflux_switch(void *arg) +{ + (void) arg; + test_setup(); + DEFAULT_EXIT_UX = CONFLUX_UX_HIGH_THROUGHPUT; + + launch_new_set(2); + + tt_int_op(smartlist_len(client_circs), OP_EQ, 2); + circuit_t *client1 = smartlist_get(client_circs, 0); + circuit_t *client2 = smartlist_get(client_circs, 1); + get_circuit_build_times_mutable()->timeout_ms = 1000; + + simulate_circuit_build(client1); + simulate_circuit_build(client2); + + circuit_t *exit1 = get_exit_circ(client1); + circuit_t *exit2 = get_exit_circ(client2); + circuit_t *next_circ = client1; + + while (smartlist_len(mock_cell_delivery) > 0) { + tt_int_op(smartlist_len(client_circs), OP_EQ, 2); + tt_int_op(smartlist_len(exit_circs), OP_EQ, 2); + tt_int_op(smartlist_len(circ_pairs), OP_EQ, 2); + + process_mock_cell_delivery(); + } + + // Check to make sure everything is linked`up + tt_ptr_op(client1->conflux, OP_EQ, client2->conflux); + tt_ptr_op(exit1->conflux, OP_EQ, exit2->conflux); + tt_ptr_op(client1->conflux, OP_NE, NULL); + tt_ptr_op(exit1->conflux, OP_NE, NULL); + tt_int_op(smartlist_len(client1->conflux->legs), OP_EQ, 2); + tt_int_op(smartlist_len(exit1->conflux->legs), OP_EQ, 2); + tt_int_op(client1->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED); + tt_int_op(client2->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED); + + tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 1); + tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0); + tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0); + tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 1); + tt_ptr_op(get_exit_circ(client1), OP_EQ, exit1); + tt_ptr_op(get_exit_circ(client2), OP_EQ, exit2); + + // Give circuits approximately equal RTT: + conflux_update_rtt(client1->conflux, client1, 100); + conflux_update_rtt(client2->conflux, client2, 125); + + client1->conflux->params.alg = CONFLUX_ALG_LOWRTT; + get_exit_circ(client1)->conflux->params.alg = CONFLUX_ALG_LOWRTT; + TO_ORIGIN_CIRCUIT(client1)->cpath->prev->ccontrol->cwnd = 300; + TO_ORIGIN_CIRCUIT(client2)->cpath->prev->ccontrol->cwnd = 300; + + // Keep sending fake cells until we decide to switch four times + for (int i = 0; i < 4; i++) { + next_circ = send_until_switch(next_circ); + + // XXX: This can't be set to 0 or we will decide we can switch immediately, + // because the client1 has a lower RTT + TO_ORIGIN_CIRCUIT(client1)->cpath->prev->ccontrol->inflight = 1; + + // Check to make sure everything is linked`up + tt_ptr_op(client1->conflux, OP_EQ, client2->conflux); + tt_ptr_op(exit1->conflux, OP_EQ, exit2->conflux); + tt_ptr_op(client1->conflux, OP_NE, NULL); + tt_ptr_op(exit1->conflux, OP_NE, NULL); + tt_int_op(smartlist_len(client1->conflux->legs), OP_EQ, 2); + tt_int_op(smartlist_len(exit1->conflux->legs), OP_EQ, 2); + tt_int_op(client1->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED); + tt_int_op(client2->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED); + + tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 1); + tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0); + tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0); + tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 1); + + tt_ptr_op(get_exit_circ(client1), OP_EQ, exit1); + tt_ptr_op(get_exit_circ(client2), OP_EQ, exit2); + tt_ptr_op(next_circ, OP_EQ, client2); + + next_circ = send_until_switch(next_circ); + + // Check to make sure everything is linked`up + tt_ptr_op(client1->conflux, OP_EQ, client2->conflux); + tt_ptr_op(exit1->conflux, OP_EQ, exit2->conflux); + tt_ptr_op(client1->conflux, OP_NE, NULL); + tt_ptr_op(exit1->conflux, OP_NE, NULL); + tt_int_op(smartlist_len(client1->conflux->legs), OP_EQ, 2); + tt_int_op(smartlist_len(exit1->conflux->legs), OP_EQ, 2); + tt_int_op(client1->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED); + tt_int_op(client2->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED); + + tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 1); + tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0); + tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0); + tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 1); + + tt_ptr_op(get_exit_circ(client1), OP_EQ, exit1); + tt_ptr_op(get_exit_circ(client2), OP_EQ, exit2); + tt_ptr_op(next_circ, OP_EQ, client1); + + TO_ORIGIN_CIRCUIT(client2)->cpath->prev->ccontrol->inflight = 0; + } + + // Try the UDP minRTT reconnect optimization a few times + for (int i = 0; i < 500; i++) { + tt_int_op(smartlist_len(client_circs), OP_EQ, 2); + client1 = smartlist_get(client_circs, 0); + client2 = smartlist_get(client_circs, 1); + exit1 = get_exit_circ(client1); + exit2 = get_exit_circ(client2); + + // Attach a third leg + conflux_launch_leg(client1->conflux->nonce); + + // It should be added to the end of the local test list + circuit_t *client3 = smartlist_get(client_circs, + smartlist_len(client_circs)-1); + simulate_circuit_build(client3); + + while (smartlist_len(mock_cell_delivery) > 0) { + tt_int_op(smartlist_len(client_circs), OP_EQ, 3); + tt_int_op(smartlist_len(exit_circs), OP_EQ, 3); + tt_int_op(smartlist_len(circ_pairs), OP_EQ, 3); + + process_mock_cell_delivery(); + } + + circuit_t *exit3 = get_exit_circ(client3); + + // Check to make sure everything is linked`up + tt_ptr_op(client3->conflux, OP_EQ, client2->conflux); + tt_ptr_op(exit3->conflux, OP_EQ, exit2->conflux); + tt_ptr_op(client3->conflux, OP_NE, NULL); + tt_ptr_op(exit3->conflux, OP_NE, NULL); + tt_int_op(smartlist_len(client1->conflux->legs), OP_EQ, 3); + tt_int_op(smartlist_len(exit1->conflux->legs), OP_EQ, 3); + tt_int_op(client3->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED); + + tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 1); + tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0); + tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0); + tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 1); + + conflux_update_rtt(client3->conflux, client3, + crypto_rand_int_range(90, 200)); + TO_ORIGIN_CIRCUIT(client3)->cpath->prev->ccontrol->cwnd = 300; + + circuit_t *circ_close = NULL; + uint64_t max_rtt = 0; + // Pick the leg with the highest RTT and close it + tor_assert(client3); + tor_assert(client3->conflux); + tor_assert(client3->conflux->legs); + CONFLUX_FOR_EACH_LEG_BEGIN(client3->conflux, leg) { + if (client3->conflux->curr_leg == leg) + continue; + + if (leg->circ_rtts_usec > max_rtt) { + max_rtt = leg->circ_rtts_usec; + circ_close = leg->circ; + } + } CONFLUX_FOR_EACH_LEG_END(leg); + + // Let the second leg "send" all data and close it. + tor_assert(circ_close); + tor_assert(circ_close->conflux); + tor_assert(circ_close->conflux->legs); + CONFLUX_FOR_EACH_LEG_BEGIN(circ_close->conflux, leg) { + TO_ORIGIN_CIRCUIT(leg->circ)->cpath->prev->ccontrol->inflight = 0; + } CONFLUX_FOR_EACH_LEG_END(leg); + + // Close without manual launch (code will not relaunch for linked) + simulate_close_retry(circ_close, false); + + tt_int_op(smartlist_len(mock_cell_delivery), OP_EQ, 0); + tt_int_op(smartlist_len(client_circs), OP_EQ, 2); + tt_int_op(smartlist_len(exit_circs), OP_EQ, 2); + tt_int_op(smartlist_len(circ_pairs), OP_EQ, 2); + + // Send until we switch to the third leg + next_circ = send_until_switch(next_circ); + + // Check to make sure everything is linked`up + tt_ptr_op(next_circ->conflux, OP_NE, NULL); + tt_int_op(smartlist_len(next_circ->conflux->legs), OP_EQ, 2); + tt_int_op(next_circ->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED); + + tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 1); + tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0); + tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0); + tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 1); + + CONFLUX_FOR_EACH_LEG_BEGIN(next_circ->conflux, leg) { + TO_ORIGIN_CIRCUIT(leg->circ)->cpath->prev->ccontrol->inflight = 0; + } CONFLUX_FOR_EACH_LEG_END(leg); + + // send until we switch back to the first leg + next_circ = send_until_switch(next_circ); + + // Check to make sure everything is linked`up + tt_ptr_op(next_circ->conflux, OP_NE, NULL); + tt_int_op(smartlist_len(next_circ->conflux->legs), OP_EQ, 2); + tt_int_op(next_circ->purpose, OP_EQ, CIRCUIT_PURPOSE_CONFLUX_LINKED); + + tt_int_op(digest256map_size(get_linked_pool(true)), OP_EQ, 1); + tt_int_op(digest256map_size(get_unlinked_pool(true)), OP_EQ, 0); + tt_int_op(digest256map_size(get_unlinked_pool(false)), OP_EQ, 0); + tt_int_op(digest256map_size(get_linked_pool(false)), OP_EQ, 1); + } + + done: + test_clear_circs(); + test_teardown(); + return; + } + +struct testcase_t conflux_pool_tests[] = { + { "link", test_conflux_link, TT_FORK, NULL, NULL }, + { "link_retry", test_conflux_link_retry, TT_FORK, NULL, NULL }, + { "link_relink", test_conflux_link_relink, TT_FORK, NULL, NULL }, + { "link_streams", test_conflux_link_streams, TT_FORK, NULL, NULL }, + { "switch", test_conflux_switch, TT_FORK, NULL, NULL }, + // XXX: These two currently fail, because they are not finished: + //{ "link_fail", test_conflux_link_fail, TT_FORK, NULL, NULL }, + //{ "close", test_conflux_close, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; diff --git a/src/test/test_congestion_control.c b/src/test/test_congestion_control.c new file mode 100644 index 0000000000..26095d310a --- /dev/null +++ b/src/test/test_congestion_control.c @@ -0,0 +1,390 @@ +#include "core/or/or.h" +#include "test/test.h" +#include "test/log_test_helpers.h" +#include "lib/testsupport/testsupport.h" +#include "test/fakecircs.h" +#include "test/rng_test_helpers.h" + +#include "lib/time/compat_time.h" + +#include "core/or/circuitlist.h" +#include "core/or/circuitmux.h" +#include "core/or/channel.h" + +#define TOR_CONGESTION_CONTROL_COMMON_PRIVATE +#define TOR_CONGESTION_CONTROL_PRIVATE +#include "core/or/congestion_control_st.h" +#include "core/or/congestion_control_common.h" +#include "core/or/congestion_control_vegas.h" + +void test_congestion_control_rtt(void *arg); +void test_congestion_control_clock(void *arg); +void test_congestion_control_vegas_cwnd(void *arg); + +static void +circuitmux_attach_circuit_mock(circuitmux_t *cmux, circuit_t *circ, + cell_direction_t direction); + +static void +circuitmux_attach_circuit_mock(circuitmux_t *cmux, circuit_t *circ, + cell_direction_t direction) +{ + (void)cmux; + (void)circ; + (void)direction; + + return; +} + +/* =============== Clock Heuristic Test Vectors =============== */ + +typedef struct clock_vec +{ + uint64_t old_delta_in; + uint64_t new_delta_in; + bool in_slow_start_in; + bool cached_result_out; + bool result_out; +} clock_vec_t; + +static void +run_clock_test_vec(congestion_control_t *cc, + clock_vec_t *vec, size_t vec_len) +{ + for (size_t i = 0; i < vec_len; i++) { + cc->in_slow_start = vec[i].in_slow_start_in; + cc->ewma_rtt_usec = vec[i].old_delta_in*1000; + bool ret = time_delta_stalled_or_jumped(cc, + vec[i].old_delta_in, + vec[i].new_delta_in); + + tt_int_op(ret, OP_EQ, vec[i].result_out); + tt_int_op(is_monotime_clock_broken, OP_EQ, vec[i].cached_result_out); + } + + done: + is_monotime_clock_broken = false; +} + +/** + * This test verifies the behavior of Section 2.1.1 of + * Prop#324 (CLOCK_HEURISTICS). + * + * It checks that we declare the clock value stalled, + * and cache that value, on various time deltas. + * + * It also verifies that our heuristics behave correctly + * with respect to slow start and large clock jumps/stalls. + */ +void +test_congestion_control_clock(void *arg) +{ + (void)arg; + clock_vec_t vect1[] = + { + {0, 1, 1, 0, 0}, // old delta 0, slow start -> false + {0, 0, 1, 1, 1}, // New delta 0 -> cache true, return true + {1, 1, 1, 1, 0}, // In slow start -> keep cache, but return false + {1, 4999, 0, 0, 0}, // Not slow start, edge -> update cache, and false + {4999, 1, 0, 0, 0}, // Not slow start, other edge -> false + {5001, 1, 0, 0, 0}, // Not slow start w/ -5000x -> use cache (false) + {5001, 0, 0, 1, 1}, // New delta 0 -> cache true, return true + {5001, 1, 0, 1, 1}, // Not slow start w/ -5000x -> use cache (true) + {5001, 1, 1, 1, 0}, // In slow start w/ -5000x -> false + {0, 5001, 0, 1, 0}, // Not slow start w/ no EWMA -> false + {1, 5001, 1, 1, 0}, // In slow start w/ +5000x -> false + {1, 1, 0, 0, 0}, // Not slow start -> update cache to false + {5001, 1, 0, 0, 0}, // Not slow start w/ -5000x -> use cache (false) + {1, 5001, 0, 0, 1}, // Not slow start w/ +5000x -> true + {0, 5001, 0, 0, 0}, // Not slow start w/ no EWMA -> false + {5001, 1, 1, 0, 0}, // In slow start w/ -5000x change -> false + {1, 1, 0, 0, 0} // Not slow start -> false + }; + + circuit_params_t params; + + params.cc_enabled = 1; + params.sendme_inc_cells = TLS_RECORD_MAX_CELLS; + cc_alg = CC_ALG_VEGAS; + congestion_control_t *cc = congestion_control_new(¶ms, CC_PATH_EXIT); + + run_clock_test_vec(cc, vect1, sizeof(vect1)/sizeof(clock_vec_t)); + + congestion_control_free(cc); +} + +/* =========== RTT Test Vectors ================== */ + +typedef struct rtt_vec { + uint64_t sent_usec_in; + uint64_t got_sendme_usec_in; + uint64_t cwnd_in; + bool ss_in; + uint64_t curr_rtt_usec_out; + uint64_t ewma_rtt_usec_out; + uint64_t min_rtt_usec_out; +} rtt_vec_t; + +static void +run_rtt_test_vec(congestion_control_t *cc, + rtt_vec_t *vec, size_t vec_len) +{ + for (size_t i = 0; i < vec_len; i++) { + enqueue_timestamp(cc->sendme_pending_timestamps, + vec[i].sent_usec_in); + } + + for (size_t i = 0; i < vec_len; i++) { + cc->cwnd = vec[i].cwnd_in; + cc->in_slow_start = vec[i].ss_in; + uint64_t curr_rtt_usec = congestion_control_update_circuit_rtt(cc, + vec[i].got_sendme_usec_in); + + tt_int_op(curr_rtt_usec, OP_EQ, vec[i].curr_rtt_usec_out); + tt_int_op(cc->min_rtt_usec, OP_EQ, vec[i].min_rtt_usec_out); + tt_int_op(cc->ewma_rtt_usec, OP_EQ, vec[i].ewma_rtt_usec_out); + } + done: + is_monotime_clock_broken = false; +} + +/** + * This test validates current, EWMA, and minRTT calculation + * from Sections 2.1 of Prop#324. + * + * We also do NOT exercise the sendme pacing code here. See + * test_sendme_is_next() for that, in test_sendme.c. + */ +void +test_congestion_control_rtt(void *arg) +{ + (void)arg; + rtt_vec_t vect1[] = { + {100000, 200000, 124, 1, 100000, 100000, 100000}, + {200000, 300000, 124, 1, 100000, 100000, 100000}, + {350000, 500000, 124, 1, 150000, 133333, 100000}, + {500000, 550000, 124, 1, 50000, 77777, 77777}, + {600000, 700000, 124, 1, 100000, 92592, 77777}, + {700000, 750000, 124, 1, 50000, 64197, 64197}, + {750000, 875000, 124, 0, 125000, 104732, 104732}, + {875000, 900000, 124, 0, 25000, 51577, 104732}, + {900000, 950000, 200, 0, 50000, 50525, 50525} + }; + + circuit_params_t params; + congestion_control_t *cc = NULL; + + params.cc_enabled = 1; + params.sendme_inc_cells = TLS_RECORD_MAX_CELLS; + cc_alg = CC_ALG_VEGAS; + + cc = congestion_control_new(¶ms, CC_PATH_EXIT); + run_rtt_test_vec(cc, vect1, sizeof(vect1)/sizeof(rtt_vec_t)); + congestion_control_free(cc); + + return; +} + +/* =========== Vegas CWND Test Vectors ============== */ + +typedef struct cwnd_vec { + uint64_t sent_usec_in; + uint64_t got_sendme_usec_in; + bool or_conn_blocked_in; + uint64_t inflight_in; + uint64_t ewma_rtt_usec_out; + uint64_t min_rtt_usec_out; + uint64_t cwnd_out; + bool in_slow_start_out; + bool cwnd_full_out; + bool blocked_chan_out; +} cwnd_vec_t; + +static void +run_vegas_cwnd_test_vec(congestion_control_t *cc, + circuit_t *circ, + cwnd_vec_t *vec, size_t vec_len) +{ + for (size_t i = 0; i < vec_len; i++) { + enqueue_timestamp(cc->sendme_pending_timestamps, + vec[i].sent_usec_in); + } + + for (size_t i = 0; i < vec_len; i++) { + log_notice(LD_CIRC, "Step %d", (int)i); + monotime_set_mock_time_nsec(vec[i].got_sendme_usec_in*1000); + circ->circuit_blocked_on_p_chan = vec[i].or_conn_blocked_in; + cc->inflight = vec[i].inflight_in; + + congestion_control_vegas_process_sendme(cc, circ); + + /* If the or conn was blocked, ensure we updated our + * CC state */ + if (vec[i].or_conn_blocked_in) { + tt_int_op(cc->next_cc_event, OP_EQ, CWND_UPDATE_RATE(cc)); + } + + tt_int_op(cc->ewma_rtt_usec, OP_EQ, vec[i].ewma_rtt_usec_out); + tt_int_op(cc->min_rtt_usec, OP_EQ, vec[i].min_rtt_usec_out); + tt_int_op(cc->cwnd, OP_EQ, vec[i].cwnd_out); + + tt_int_op(cc->in_slow_start, OP_EQ, vec[i].in_slow_start_out); + tt_int_op(cc->cwnd_full, OP_EQ, vec[i].cwnd_full_out); + tt_int_op(cc->blocked_chan, OP_EQ, vec[i].blocked_chan_out); + } + + done: + is_monotime_clock_broken = false; +} + +/** + * This test validates congestion window updates for the + * TOR_VEGAS congestion control algorithm, from Section 3.3 + * of Prop#324. + * + * It tests updates as a function of the timestamp of the + * cell that would trigger a sendme and the sendme arrival + * timestamp, and as a function of orconn blocking. + * + * It ensures that at least one test vector caused a cwnd update + * due to a blocked OR connection. The orconn blocking logic is + * simulated -- we do NOT actually exercise the orconn code here. + * + * We also do NOT exercise the sendme pacing code here. See + * test_sendme_is_next() for that, in test_sendme.c. + * + * We also do NOT exercise the negotiation code here. See + * test_ntor3_handshake() for that, in test_ntor_v3.c. + */ +void +test_congestion_control_vegas_cwnd(void *arg) +{ + (void)arg; + circuit_params_t params; + /* Replay of RTT edge case checks, plus some extra to exit + * slow start via RTT, and exercise full/not full */ + cwnd_vec_t vect1[] = { + {100000, 200000, 0, 124, 100000, 100000, 155, 1, 0, 0}, + {200000, 300000, 0, 155, 100000, 100000, 186, 1, 1, 0}, + {350000, 500000, 0, 186, 133333, 100000, 217, 1, 1, 0}, + {500000, 550000, 0, 217, 77777, 77777, 248, 1, 1, 0}, + {600000, 700000, 0, 248, 92592, 77777, 279, 1, 1, 0}, + {700000, 750000, 0, 279, 64197, 64197, 310, 1, 0, 0}, // Fullness expiry + {750000, 875000, 0, 310, 104732, 64197, 341, 1, 1, 0}, + {875000, 900000, 0, 341, 51577, 51577, 372, 1, 1, 0}, + {900000, 950000, 0, 279, 50525, 50525, 403, 1, 1, 0}, + {950000, 1000000, 0, 279, 50175, 50175, 434, 1, 1, 0}, + {1000000, 1050000, 0, 279, 50058, 50058, 465, 1, 1, 0}, + {1050000, 1100000, 0, 279, 50019, 50019, 496, 1, 1, 0}, + {1100000, 1150000, 0, 279, 50006, 50006, 527, 1, 1, 0}, + {1150000, 1200000, 0, 279, 50002, 50002, 558, 1, 1, 0}, + {1200000, 1250000, 0, 550, 50000, 50000, 589, 1, 1, 0}, + {1250000, 1300000, 0, 550, 50000, 50000, 620, 1, 0, 0}, // Fullness expiry + {1300000, 1350000, 0, 550, 50000, 50000, 635, 1, 1, 0}, + {1350000, 1400000, 0, 550, 50000, 50000, 650, 1, 1, 0}, + {1400000, 1450000, 0, 150, 50000, 50000, 650, 1, 0, 0}, // cwnd not full + {1450000, 1500000, 0, 150, 50000, 50000, 650, 1, 0, 0}, // cwnd not full + {1500000, 1550000, 0, 550, 50000, 50000, 664, 1, 1, 0}, // cwnd full + {1500000, 1600000, 0, 550, 83333, 50000, 584, 0, 1, 0}, // gamma exit + {1600000, 1650000, 0, 550, 61111, 50000, 585, 0, 1, 0}, // alpha + {1650000, 1700000, 0, 550, 53703, 50000, 586, 0, 1, 0}, + {1700000, 1750000, 0, 100, 51234, 50000, 586, 0, 0, 0}, // alpha, not full + {1750000, 1900000, 0, 100, 117078, 50000, 559, 0, 0, 0}, // delta, not full + {1900000, 2000000, 0, 100, 105692, 50000, 558, 0, 0, 0}, // beta, not full + {2000000, 2075000, 0, 500, 85230, 50000, 558, 0, 1, 0}, // no change + {2075000, 2125000, 1, 500, 61743, 50000, 557, 0, 1, 1}, // beta, blocked + {2125000, 2150000, 0, 500, 37247, 37247, 558, 0, 1, 0}, // alpha + {2150000, 2350000, 0, 500, 145749, 37247, 451, 0, 1, 0} // delta + }; + /* Test exiting slow start via blocked orconn */ + cwnd_vec_t vect2[] = { + {100000, 200000, 0, 124, 100000, 100000, 155, 1, 0, 0}, + {200000, 300000, 0, 155, 100000, 100000, 186, 1, 1, 0}, + {350000, 500000, 0, 186, 133333, 100000, 217, 1, 1, 0}, + {500000, 550000, 1, 217, 77777, 77777, 403, 0, 1, 1}, // ss exit, blocked + {600000, 700000, 0, 248, 92592, 77777, 404, 0, 1, 0}, // alpha + {700000, 750000, 1, 404, 64197, 64197, 403, 0, 0, 1}, // blocked beta + {750000, 875000, 0, 403, 104732, 64197, 404, 0, 1, 0} + }; + /* Real upload 1 */ + cwnd_vec_t vect3[] = { + { 18258527, 19002938, 0, 83, 744411, 744411, 155, 1, 0, 0 }, + { 18258580, 19254257, 0, 52, 911921, 744411, 186, 1, 1, 0 }, + { 20003224, 20645298, 0, 164, 732023, 732023, 217, 1, 1, 0 }, + { 20003367, 21021444, 0, 133, 922725, 732023, 248, 1, 1, 0 }, + { 20003845, 21265508, 0, 102, 1148683, 732023, 279, 1, 1, 0 }, + { 20003975, 21429157, 0, 71, 1333015, 732023, 310, 1, 0, 0 }, + { 20004309, 21707677, 0, 40, 1579917, 732023, 310, 1, 0, 0 } + }; + /* Real upload 2 */ + cwnd_vec_t vect4[] = { + { 358297091, 358854163, 0, 83, 557072, 557072, 155, 1, 0, 0 }, + { 358297649, 359123845, 0, 52, 736488, 557072, 186, 1, 1, 0 }, + { 359492879, 359995330, 0, 186, 580463, 557072, 217, 1, 1, 0 }, + { 359493043, 360489243, 0, 217, 857621, 557072, 248, 1, 1, 0 }, + { 359493232, 360489673, 0, 248, 950167, 557072, 279, 1, 1, 0 }, + { 359493795, 360489971, 0, 279, 980839, 557072, 310, 1, 0, 0 }, + { 359493918, 360490248, 0, 310, 991166, 557072, 341, 1, 1, 0 }, + { 359494029, 360716465, 0, 341, 1145346, 557072, 372, 1, 1, 0 }, + { 359996888, 360948867, 0, 372, 1016434, 557072, 403, 1, 1, 0 }, + { 359996979, 360949330, 0, 403, 973712, 557072, 434, 1, 1, 0 }, + { 360489528, 361113615, 0, 434, 740628, 557072, 465, 1, 1, 0 }, + { 360489656, 361281604, 0, 465, 774841, 557072, 496, 1, 1, 0 }, + { 360489837, 361500461, 0, 496, 932029, 557072, 482, 0, 1, 0 }, + { 360489963, 361500631, 0, 482, 984455, 557072, 482, 0, 1, 0 }, + { 360490117, 361842481, 0, 482, 1229727, 557072, 481, 0, 1, 0 } + }; + + congestion_control_t *cc = NULL; + channel_t dummy_channel = {0}; + + MOCK(circuitmux_attach_circuit, circuitmux_attach_circuit_mock); + testing_enable_reproducible_rng(); + + monotime_init(); + monotime_enable_test_mocking(); + monotime_set_mock_time_nsec(0); + + dummy_channel.cmux = circuitmux_alloc(); + circuit_t *circ = TO_CIRCUIT(new_fake_orcirc(&dummy_channel, + &dummy_channel)); + circ->purpose = CIRCUIT_PURPOSE_OR; + + params.cc_enabled = 1; + params.sendme_inc_cells = TLS_RECORD_MAX_CELLS; + cc_alg = CC_ALG_VEGAS; + + cc = congestion_control_new(¶ms, CC_PATH_EXIT); + run_vegas_cwnd_test_vec(cc, circ, vect1, + sizeof(vect1)/sizeof(cwnd_vec_t)); + congestion_control_free(cc); + + cc = congestion_control_new(¶ms, CC_PATH_EXIT); + run_vegas_cwnd_test_vec(cc, circ, vect2, + sizeof(vect2)/sizeof(cwnd_vec_t)); + congestion_control_free(cc); + + cc = congestion_control_new(¶ms, CC_PATH_EXIT); + run_vegas_cwnd_test_vec(cc, circ, vect3, + sizeof(vect3)/sizeof(cwnd_vec_t)); + congestion_control_free(cc); + + cc = congestion_control_new(¶ms, CC_PATH_EXIT); + run_vegas_cwnd_test_vec(cc, circ, vect4, + sizeof(vect4)/sizeof(cwnd_vec_t)); + congestion_control_free(cc); + + //done: + circuitmux_free(dummy_channel.cmux); + return; +} + +#define TEST_CONGESTION_CONTROL(name, flags) \ + { #name, test_##name, (flags), NULL, NULL } + +struct testcase_t congestion_control_tests[] = { + TEST_CONGESTION_CONTROL(congestion_control_clock, TT_FORK), + TEST_CONGESTION_CONTROL(congestion_control_rtt, TT_FORK), + TEST_CONGESTION_CONTROL(congestion_control_vegas_cwnd, TT_FORK), + END_OF_TESTCASES +}; diff --git a/src/test/test_connection.c b/src/test/test_connection.c index fbf9d6a5ab..ed94fe8aaa 100644 --- a/src/test/test_connection.c +++ b/src/test/test_connection.c @@ -22,6 +22,7 @@ #include "feature/dircommon/directory.h" #include "core/or/connection_or.h" #include "lib/net/resolve.h" +#include "lib/evloop/compat_libevent.h" #include "test/test_connection.h" #include "test/test_helpers.h" @@ -113,14 +114,8 @@ test_conn_get_basic_teardown(const struct testcase_t *tc, void *arg) /* We didn't call tor_libevent_initialize(), so event_base was NULL, * so we can't rely on connection_unregister_events() use of event_del(). */ - if (conn->linked_conn->read_event) { - tor_free(conn->linked_conn->read_event); - conn->linked_conn->read_event = NULL; - } - if (conn->linked_conn->write_event) { - tor_free(conn->linked_conn->write_event); - conn->linked_conn->write_event = NULL; - } + tor_event_free(conn->linked_conn->read_event); + tor_event_free(conn->linked_conn->write_event); if (!conn->linked_conn->marked_for_close) { connection_close_immediate(conn->linked_conn); @@ -142,14 +137,8 @@ test_conn_get_basic_teardown(const struct testcase_t *tc, void *arg) /* We didn't set the events up properly, so we can't use event_del() in * close_closeable_connections() > connection_free() * > connection_unregister_events() */ - if (conn->read_event) { - tor_free(conn->read_event); - conn->read_event = NULL; - } - if (conn->write_event) { - tor_free(conn->write_event); - conn->write_event = NULL; - } + tor_event_free(conn->read_event); + tor_event_free(conn->write_event); if (!conn->marked_for_close) { connection_close_immediate(conn); diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 87e309f25a..db5a2db650 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -10,6 +10,8 @@ #include "test/test.h" #include "lib/crypt_ops/aes.h" #include "siphash.h" +#include "ext/compat_blake2.h" +#include "ext/equix/hashx/include/hashx.h" #include "lib/crypt_ops/crypto_curve25519.h" #include "lib/crypt_ops/crypto_dh.h" #include "lib/crypt_ops/crypto_ed25519.h" @@ -1043,6 +1045,7 @@ test_crypto_mac_sha3(void *arg) { const char msg[] = "i am in a library somewhere using my computer"; const char key[] = "i'm from the past talking to the future."; + char *mem_op_hex_tmp = NULL; uint8_t hmac_test[DIGEST256_LEN]; char hmac_manual[DIGEST256_LEN]; @@ -1077,7 +1080,12 @@ test_crypto_mac_sha3(void *arg) /* Now compare the two results */ tt_mem_op(hmac_test, OP_EQ, hmac_manual, DIGEST256_LEN); - done: ; + /* Check against a known correct value (computed from python) */ + test_memeq_hex(hmac_test, + "753fba6d87d49497238a512a3772dd29" + "1e55f7d1cd332c9fb5c967c7a10a13ca"); + done: + tor_free(mem_op_hex_tmp); } /** Run unit tests for our public key crypto functions */ @@ -2844,6 +2852,181 @@ test_crypto_siphash(void *arg) ; } +static void +test_crypto_blake2b(void *arg) +{ + (void)arg; + + /* There is no official blake2b test vector set, but these are inspired + * by RFC7693 and OpenSSL. Note that we need to test shorter hash lengths + * separately even though they are implemented by truncating a 512-bit + * hash, because the requested length is included in the hash initial state. + */ + static const struct { + const char *in_literal; + const char *out_hex; + } vectors[] = { + { "", + "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419" + "d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce" + }, + { "a", + "333fcb4ee1aa7c115355ec66ceac917c8bfd815bf7587d325aec1864edd24e34" + "d5abe2c6b1b5ee3face62fed78dbef802f2a85cb91d455a8f5249d330853cb3c" + }, + { "ab", + "b32c0573d242b3a987d8f66bd43266b7925cefab3a854950641a81ef6a3f4b97" + "928443850545770f64abac2a75f18475653fa3d9a52c66a840da3b8617ae9607" + }, + { "abc", + "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1" + "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923" + }, + { "", "2e" }, + { "a", "de" }, + { "ab", "0e" }, + { "abc", "6b" }, + { "", "1271cf25" }, + { "a", "ca234c55" }, + { "ab", "3ae897a7" }, + { "abc", "63906248" }, + { "A somewhat longer test vector for blake2b xxxxxxxxxxxxxxxxxxxxxx" + "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" + "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.", + "1d27b0988061a82ff7563a55f9289ff3d878783e688d9e001b3c4b99b675c7f7" + "1d4ae57805c6a8e670eb8145ba97960a7859451ab7b1558a60e5b7660d2f4639" + }, + { "A somewhat longer test vector for blake2b xxxxxxxxxxxxxxxxxxxxxx" + "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" + "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.", + "48600bb0" + } + }; + + static const struct { + int update_size; + } variations[] = { + {BLAKE2B_BLOCKBYTES*2}, + {BLAKE2B_BLOCKBYTES}, + {BLAKE2B_BLOCKBYTES-1}, + {1}, + {2}, + {3} + }; + + const size_t num_vectors = sizeof vectors / sizeof vectors[0]; + const size_t num_variations = sizeof variations / sizeof variations[0]; + + for (unsigned vec_i = 0; vec_i < num_vectors; vec_i++) { + const char *in_literal = vectors[vec_i].in_literal; + const char *out_hex = vectors[vec_i].out_hex; + const size_t in_len = strlen(in_literal); + const size_t out_hex_len = strlen(out_hex); + const size_t hash_size = out_hex_len / 2; + + int retval = -1; + uint8_t out_expected[BLAKE2B_OUTBYTES] = { 0 }; + tt_int_op(out_hex_len, OP_EQ, 2 * hash_size); + tt_int_op(hash_size, OP_LE, sizeof out_expected); + retval = base16_decode((char*)out_expected, hash_size, + out_hex, out_hex_len); + tt_int_op(retval, OP_EQ, hash_size); + + for (size_t vari_i = 0; vari_i < num_variations; vari_i++) { + const size_t update_size = variations[vari_i].update_size; + uint8_t out_actual[BLAKE2B_OUTBYTES] = { 0 }; + + blake2b_state b2_state; + retval = blake2b_init(&b2_state, hash_size); + tt_int_op(retval, OP_EQ, 0); + + for (size_t in_off = 0; in_off < in_len;) { + const size_t this_update = MIN(update_size, in_len - in_off); + blake2b_update(&b2_state, (uint8_t*)in_literal + in_off, this_update); + in_off += this_update; + } + + memset(out_actual, 0xa5, sizeof out_actual); + blake2b_final(&b2_state, out_actual, hash_size); + tt_mem_op(out_actual, OP_EQ, out_expected, hash_size); + } + } + + done: + ; +} + +static void +test_crypto_hashx(void *arg) +{ + (void)arg; + + /* Specifically test the embedded instance of HashX inside Equi-X. + * It uses a non-default setting of HASHX_SIZE=8 */ + static const struct { + const char *seed_literal; + uint64_t hash_input; + const char *out_hex; + } vectors[] = { + { "", 0, "466cc2021c268560" }, + { "a", 0, "b2a110ee695c475c" }, + { "ab", 0, "57c77f7e0d2c1727" }, + { "abc", 0, "ef560991338086d1" }, + { "", 999, "304068b62bc4874e" }, + { "a", 999, "c8b66a8eb4bba304" }, + { "ab", 999, "26c1f7031f0b3645" }, + { "abc", 999, "de84f9d286b39ab5" }, + { "abc", UINT64_MAX, "f756c266a3cb3b5a" } + }; + + static const struct { + hashx_type type; + } variations[] = { + { HASHX_TYPE_INTERPRETED }, +#if defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) + { HASHX_TYPE_COMPILED }, +#endif + }; + + const unsigned num_vectors = sizeof vectors / sizeof vectors[0]; + const unsigned num_variations = sizeof variations / sizeof variations[0]; + hashx_ctx *ctx = NULL; + + for (unsigned vec_i = 0; vec_i < num_vectors; vec_i++) { + const char *seed_literal = vectors[vec_i].seed_literal; + const uint64_t hash_input = vectors[vec_i].hash_input; + const char *out_hex = vectors[vec_i].out_hex; + const size_t seed_len = strlen(seed_literal); + const size_t out_hex_len = strlen(out_hex); + + int retval = -1; + uint8_t out_expected[HASHX_SIZE] = { 0 }; + tt_int_op(out_hex_len, OP_EQ, 2 * HASHX_SIZE); + retval = base16_decode((char*)out_expected, HASHX_SIZE, + out_hex, out_hex_len); + tt_int_op(retval, OP_EQ, HASHX_SIZE); + + for (unsigned vari_i = 0; vari_i < num_variations; vari_i++) { + uint8_t out_actual[HASHX_SIZE] = { 0 }; + + hashx_free(ctx); + ctx = hashx_alloc(variations[vari_i].type); + + tt_ptr_op(ctx, OP_NE, NULL); + retval = hashx_make(ctx, seed_literal, seed_len); + tt_int_op(retval, OP_EQ, HASHX_OK); + + memset(out_actual, 0xa5, sizeof out_actual); + retval = hashx_exec(ctx, hash_input, out_actual); + tt_int_op(retval, OP_EQ, HASHX_OK); + tt_mem_op(out_actual, OP_EQ, out_expected, sizeof out_actual); + } + } + + done: + hashx_free(ctx); +} + /* We want the likelihood that the random buffer exhibits any regular pattern * to be far less than the memory bit error rate in the int return value. * Using 2048 bits provides a failure rate of 1/(3 * 10^616), and we call @@ -3073,6 +3256,8 @@ struct testcase_t crypto_tests[] = { ED25519_TEST(validation, 0), { "ed25519_storage", test_crypto_ed25519_storage, 0, NULL, NULL }, { "siphash", test_crypto_siphash, 0, NULL, NULL }, + { "blake2b", test_crypto_blake2b, 0, NULL, NULL }, + { "hashx", test_crypto_hashx, 0, NULL, NULL }, { "failure_modes", test_crypto_failure_modes, TT_FORK, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_crypto_slow.c b/src/test/test_crypto_slow.c index bcfea10cf6..52091f190c 100644 --- a/src/test/test_crypto_slow.c +++ b/src/test/test_crypto_slow.c @@ -7,6 +7,7 @@ #define CRYPTO_S2K_PRIVATE #include "core/or/or.h" #include "test/test.h" +#include "ext/equix/include/equix.h" #include "lib/crypt_ops/crypto_curve25519.h" #include "lib/crypt_ops/crypto_ed25519.h" #include "lib/crypt_ops/crypto_s2k.h" @@ -584,6 +585,139 @@ test_crypto_ed25519_fuzz_donna(void *arg) ; } +static void +test_crypto_equix(void *arg) +{ + (void)arg; + + static const struct { + const char *challenge_literal; + size_t num_solutions; + equix_solution solutions[EQUIX_MAX_SOLS]; + } vectors[] = { + { "zzz", 1, { + {{ 0xae21, 0xd392, 0x3215, 0xdd9c, 0x2f08, 0x93df, 0x232c, 0xe5dc }}, + }}, + { "rrr", 1, { + {{ 0x0873, 0x57a8, 0x73e0, 0x912e, 0x1ca8, 0xad96, 0x9abd, 0xd7de }}, + }}, + { "qqq", 0, {{{ 0 }}} }, + { "0123456789", 0, {{{ 0 }}} }, + { "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz", 0, {{{ 0 }}} }, + { "", 3, { + {{ 0x0098, 0x3a4d, 0xc489, 0xcfba, 0x7ef3, 0xa498, 0xa00f, 0xec20 }}, + {{ 0x78d8, 0x8611, 0xa4df, 0xec19, 0x0927, 0xa729, 0x842f, 0xf771 }}, + {{ 0x54b5, 0xcc11, 0x1593, 0xe624, 0x9357, 0xb339, 0xb138, 0xed99 }}, + }}, + { "a", 3, { + {{ 0x4b38, 0x8c81, 0x9255, 0xad99, 0x5ce7, 0xeb3e, 0xc635, 0xee38 }}, + {{ 0x3f9e, 0x659b, 0x9ae6, 0xb891, 0x63ae, 0x777c, 0x06ca, 0xc593 }}, + {{ 0x2227, 0xa173, 0x365a, 0xb47d, 0x1bb2, 0xa077, 0x0d5e, 0xf25f }}, + }}, + { "abc", 2, { + {{ 0x371f, 0x8865, 0x8189, 0xfbc3, 0x26df, 0xe4c0, 0xab39, 0xfe5a }}, + {{ 0x2101, 0xb88f, 0xc525, 0xccb3, 0x5785, 0xa41e, 0x4fba, 0xed18 }}, + }}, + { "abce", 4, { + {{ 0x4fca, 0x72eb, 0x101f, 0xafab, 0x1add, 0x2d71, 0x75a3, 0xc978 }}, + {{ 0x17f1, 0x7aa6, 0x23e3, 0xab00, 0x7e2f, 0x917e, 0x16da, 0xda9e }}, + {{ 0x70ee, 0x7757, 0x8a54, 0xbd2b, 0x90e4, 0xe31e, 0x2085, 0xe47e }}, + {{ 0x62c5, 0x86d1, 0x5752, 0xe1f0, 0x12da, 0x8f33, 0x7336, 0xf161 }}, + }}, + { "01234567890123456789", 5, { + {{ 0x4803, 0x6775, 0xc5c9, 0xd1b0, 0x1bc3, 0xe4f6, 0x4027, 0xf5ad }}, + {{ 0x5a8a, 0x9542, 0xef99, 0xf0b9, 0x4905, 0x4e29, 0x2da5, 0xfbd5 }}, + {{ 0x4c79, 0xc935, 0x2bcb, 0xcd0f, 0x0362, 0x9fa9, 0xa62e, 0xf83a }}, + {{ 0x5878, 0x6edf, 0x1e00, 0xf5e3, 0x43de, 0x9212, 0xd01e, 0xfd11 }}, + {{ 0x0b69, 0x2d17, 0x01be, 0x6cb4, 0x0fba, 0x4a9e, 0x8d75, 0xa50f }}, + }}, + }; + + static const struct { + equix_ctx_flags flags; + equix_result expected; + equix_solution_flags sol_flags; + } variations[] = { + {0, EQUIX_OK, 0}, + {0, EQUIX_FAIL_ORDER, 0}, + {0, EQUIX_FAIL_PARTIAL_SUM, 0}, +#if defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) + { EQUIX_CTX_MUST_COMPILE, EQUIX_OK, + EQUIX_SOLVER_DID_USE_COMPILER + }, + { EQUIX_CTX_MUST_COMPILE, EQUIX_FAIL_ORDER, + EQUIX_SOLVER_DID_USE_COMPILER + }, + { EQUIX_CTX_MUST_COMPILE, EQUIX_FAIL_PARTIAL_SUM, + EQUIX_SOLVER_DID_USE_COMPILER + }, +#endif + }; + + const unsigned num_vectors = sizeof vectors / sizeof vectors[0]; + const unsigned num_variations = sizeof variations / sizeof variations[0]; + + for (unsigned vec_i = 0; vec_i < num_vectors; vec_i++) { + const char *challenge_literal = vectors[vec_i].challenge_literal; + const size_t challenge_len = strlen(challenge_literal); + + const size_t num_sols = vectors[vec_i].num_solutions; + const equix_solution *sols_expected = vectors[vec_i].solutions; + + for (unsigned vari_i = 0; vari_i < num_variations; vari_i++) { + const equix_ctx_flags flags = variations[vari_i].flags; + const equix_solution_flags sol_flags = variations[vari_i].sol_flags; + const equix_result expected = variations[vari_i].expected; + + equix_solutions_buffer output; + equix_ctx *solve_ctx = NULL, *verify_ctx = NULL; + + solve_ctx = equix_alloc(EQUIX_CTX_SOLVE | flags); + tt_ptr_op(solve_ctx, OP_NE, NULL); + + /* Solve phase: Make sure the test vector matches */ + memset(&output, 0xa5, sizeof output); + equix_result result; + result = equix_solve(solve_ctx, challenge_literal, + challenge_len, &output); + equix_free(solve_ctx); + tt_int_op(result, OP_EQ, EQUIX_OK); + tt_int_op(output.count, OP_EQ, num_sols); + tt_int_op(output.flags, OP_EQ, sol_flags); + tt_mem_op(output.sols, OP_EQ, sols_expected, + num_sols * sizeof(equix_solution)); + + verify_ctx = equix_alloc(EQUIX_CTX_VERIFY | flags); + tt_ptr_op(verify_ctx, OP_NE, NULL); + + /* Use each solution for positive and negative tests of verify */ + for (size_t sol_i = 0; sol_i < num_sols; sol_i++) { + equix_idx tmp_idx; + equix_solution *sol = &output.sols[sol_i]; + + if (expected == EQUIX_FAIL_ORDER) { + /* Swap two otherwise valid indices, to trigger an order error */ + tmp_idx = sol->idx[0]; + sol->idx[0] = sol->idx[1]; + sol->idx[1] = tmp_idx; + } else if (expected == EQUIX_FAIL_PARTIAL_SUM) { + /* Most changes to the solution will cause a partial sum error */ + sol->idx[0]++; + } + + result = equix_verify(verify_ctx, challenge_literal, + challenge_len, sol); + tt_int_op(expected, OP_EQ, result); + } + + equix_free(verify_ctx); + } + } + + done: + ; +} + #ifndef COCCI #define CRYPTO_LEGACY(name) \ { #name, test_crypto_ ## name , 0, NULL, NULL } @@ -619,5 +753,6 @@ struct testcase_t slow_crypto_tests[] = { { "pbkdf2_vectors", test_crypto_pbkdf2_vectors, 0, NULL, NULL }, { "pwbox", test_crypto_pwbox, 0, NULL, NULL }, ED25519_TEST(fuzz_donna, TT_FORK), + { "equix", test_crypto_equix, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 186e09f236..248fd8ab5d 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -2971,7 +2971,7 @@ test_vrs_for_v3ns(vote_routerstatus_t *vrs, int voter, time_t now) (voter == 1)) { /* Check the first routerstatus. */ tt_str_op(vrs->version,OP_EQ, "0.1.2.14"); - tt_int_op(rs->published_on,OP_EQ, now-1500); + tt_int_op(vrs->published_on,OP_EQ, now-1500); tt_str_op(rs->nickname,OP_EQ, "router2"); tt_mem_op(rs->identity_digest,OP_EQ, "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3" @@ -2996,7 +2996,7 @@ test_vrs_for_v3ns(vote_routerstatus_t *vrs, int voter, time_t now) if (voter == 1) { /* Check the second routerstatus. */ tt_str_op(vrs->version,OP_EQ, "0.2.0.5"); - tt_int_op(rs->published_on,OP_EQ, now-1000); + tt_int_op(vrs->published_on,OP_EQ, now-1000); tt_str_op(rs->nickname,OP_EQ, "router1"); } tt_mem_op(rs->descriptor_digest,OP_EQ, "MMMMMMMMMMMMMMMMMMMM", DIGEST_LEN); @@ -3057,6 +3057,7 @@ test_consensus_for_v3ns(networkstatus_t *con, time_t now) static void test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now) { + (void)now; tor_addr_t addr_ipv6; tt_assert(rs); @@ -3093,7 +3094,6 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now) DIGEST_LEN); tt_str_op(rs->nickname,OP_EQ, "router1"); tt_mem_op(rs->descriptor_digest,OP_EQ, "MMMMMMMMMMMMMMMMMMMM", DIGEST_LEN); - tt_int_op(rs->published_on,OP_EQ, now-1000); tt_assert(tor_addr_eq_ipv4h(&rs->ipv4_addr, 0x99009901)); tt_int_op(rs->ipv4_orport,OP_EQ, 443); tt_int_op(rs->ipv4_dirport,OP_EQ, 0); @@ -3968,7 +3968,7 @@ gen_routerstatus_for_umbw(int idx, time_t now) vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); rs = &vrs->status; vrs->version = tor_strdup("0.1.2.14"); - rs->published_on = now-1500; + vrs->published_on = now-1500; strlcpy(rs->nickname, "router2", sizeof(rs->nickname)); memset(rs->identity_digest, 3, DIGEST_LEN); memset(rs->descriptor_digest, 78, DIGEST_LEN); @@ -3993,7 +3993,7 @@ gen_routerstatus_for_umbw(int idx, time_t now) vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); rs = &vrs->status; vrs->version = tor_strdup("0.2.0.5"); - rs->published_on = now-1000; + vrs->published_on = now-1000; strlcpy(rs->nickname, "router1", sizeof(rs->nickname)); memset(rs->identity_digest, 5, DIGEST_LEN); memset(rs->descriptor_digest, 77, DIGEST_LEN); @@ -4020,7 +4020,7 @@ gen_routerstatus_for_umbw(int idx, time_t now) vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); rs = &vrs->status; vrs->version = tor_strdup("0.1.0.3"); - rs->published_on = now-1000; + vrs->published_on = now-1000; strlcpy(rs->nickname, "router3", sizeof(rs->nickname)); memset(rs->identity_digest, 0x33, DIGEST_LEN); memset(rs->descriptor_digest, 79, DIGEST_LEN); @@ -4046,7 +4046,7 @@ gen_routerstatus_for_umbw(int idx, time_t now) vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); rs = &vrs->status; vrs->version = tor_strdup("0.1.6.3"); - rs->published_on = now-1000; + vrs->published_on = now-1000; strlcpy(rs->nickname, "router4", sizeof(rs->nickname)); memset(rs->identity_digest, 0x34, DIGEST_LEN); memset(rs->descriptor_digest, 47, DIGEST_LEN); @@ -4146,7 +4146,7 @@ test_vrs_for_umbw(vote_routerstatus_t *vrs, int voter, time_t now) * cutoff. */ tt_str_op(vrs->version,OP_EQ, "0.1.2.14"); - tt_int_op(rs->published_on,OP_EQ, now-1500); + tt_int_op(vrs->published_on,OP_EQ, now-1500); tt_str_op(rs->nickname,OP_EQ, "router2"); tt_mem_op(rs->identity_digest,OP_EQ, "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3" @@ -4170,7 +4170,7 @@ test_vrs_for_umbw(vote_routerstatus_t *vrs, int voter, time_t now) * cutoff. */ tt_str_op(vrs->version,OP_EQ, "0.2.0.5"); - tt_int_op(rs->published_on,OP_EQ, now-1000); + tt_int_op(vrs->published_on,OP_EQ, now-1000); tt_str_op(rs->nickname,OP_EQ, "router1"); tt_mem_op(rs->identity_digest,OP_EQ, "\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5" @@ -4245,6 +4245,7 @@ test_consensus_for_umbw(networkstatus_t *con, time_t now) static void test_routerstatus_for_umbw(routerstatus_t *rs, time_t now) { + (void)now; tor_addr_t addr_ipv6; uint32_t max_unmeasured_bw_kb = (alternate_clip_bw > 0) ? alternate_clip_bw : DEFAULT_MAX_UNMEASURED_BW_KB; @@ -4285,7 +4286,6 @@ test_routerstatus_for_umbw(routerstatus_t *rs, time_t now) DIGEST_LEN); tt_str_op(rs->nickname,OP_EQ, "router1"); tt_mem_op(rs->descriptor_digest,OP_EQ, "MMMMMMMMMMMMMMMMMMMM", DIGEST_LEN); - tt_int_op(rs->published_on,OP_EQ, now-1000); tt_assert(tor_addr_eq_ipv4h(&rs->ipv4_addr, 0x99009901)); tt_int_op(rs->ipv4_orport,OP_EQ, 443); tt_int_op(rs->ipv4_dirport,OP_EQ, 0); @@ -4385,7 +4385,6 @@ test_dir_fmt_control_ns(void *arg) (void)arg; memset(&rs, 0, sizeof(rs)); - rs.published_on = 1364925198; strlcpy(rs.nickname, "TetsuoMilk", sizeof(rs.nickname)); memcpy(rs.identity_digest, "Stately, plump Buck ", DIGEST_LEN); memcpy(rs.descriptor_digest, "Mulligan came up fro", DIGEST_LEN); @@ -4403,7 +4402,7 @@ test_dir_fmt_control_ns(void *arg) tt_assert(s); tt_str_op(s, OP_EQ, "r TetsuoMilk U3RhdGVseSwgcGx1bXAgQnVjayA " - "TXVsbGlnYW4gY2FtZSB1cCBmcm8 2013-04-02 17:53:18 " + "TXVsbGlnYW4gY2FtZSB1cCBmcm8 2038-01-01 00:00:00 " "32.48.64.80 9001 9002\n" "s Exit Fast Running V2Dir\n" "w Bandwidth=1000\n"); diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c index 201ea900ff..50ba32b562 100644 --- a/src/test/test_dir_common.c +++ b/src/test/test_dir_common.c @@ -93,7 +93,7 @@ dir_common_gen_routerstatus_for_v3ns(int idx, time_t now) vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); rs = &vrs->status; vrs->version = tor_strdup("0.1.2.14"); - rs->published_on = now-1500; + vrs->published_on = now-1500; strlcpy(rs->nickname, "router2", sizeof(rs->nickname)); memset(rs->identity_digest, TEST_DIR_ROUTER_ID_1, DIGEST_LEN); memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_1, DIGEST_LEN); @@ -111,7 +111,7 @@ dir_common_gen_routerstatus_for_v3ns(int idx, time_t now) vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); rs = &vrs->status; vrs->version = tor_strdup("0.2.0.5"); - rs->published_on = now-1000; + vrs->published_on = now-1000; strlcpy(rs->nickname, "router1", sizeof(rs->nickname)); memset(rs->identity_digest, TEST_DIR_ROUTER_ID_2, DIGEST_LEN); memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_2, DIGEST_LEN); @@ -130,7 +130,7 @@ dir_common_gen_routerstatus_for_v3ns(int idx, time_t now) vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); rs = &vrs->status; vrs->version = tor_strdup("0.1.0.3"); - rs->published_on = now-1000; + vrs->published_on = now-1000; strlcpy(rs->nickname, "router3", sizeof(rs->nickname)); memset(rs->identity_digest, TEST_DIR_ROUTER_ID_3, DIGEST_LEN); memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_3, DIGEST_LEN); @@ -147,7 +147,7 @@ dir_common_gen_routerstatus_for_v3ns(int idx, time_t now) vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); rs = &vrs->status; vrs->version = tor_strdup("0.1.6.3"); - rs->published_on = now-1000; + vrs->published_on = now-1000; strlcpy(rs->nickname, "router4", sizeof(rs->nickname)); memset(rs->identity_digest, TEST_DIR_ROUTER_ID_4, DIGEST_LEN); memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_4, DIGEST_LEN); diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index a7f9fa1d7b..5f93b04c96 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -2073,7 +2073,7 @@ test_dir_handle_get_status_vote_next_bandwidth_not_found(void* data) conn = new_dir_conn(); tt_int_op(0, OP_EQ, directory_handle_command_get(conn, - GET("/tor/status-vote/next/bandwdith"), NULL, 0)); + GET("/tor/status-vote/next/bandwidth"), NULL, 0)); fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, NULL, NULL, 1, 0); diff --git a/src/test/test_dos.c b/src/test/test_dos.c index a34420024f..6c57e85cb8 100644 --- a/src/test/test_dos.c +++ b/src/test/test_dos.c @@ -9,6 +9,7 @@ #include "core/or/dos.h" #include "core/or/circuitlist.h" #include "lib/crypt_ops/crypto_rand.h" +#include "lib/time/compat_time.h" #include "feature/stats/geoip_stats.h" #include "core/or/channel.h" #include "feature/nodelist/microdesc.h" @@ -23,6 +24,8 @@ #include "test/test.h" #include "test/log_test_helpers.h" +static const uint64_t BILLION = 1000000000; + static networkstatus_t *dummy_ns = NULL; static networkstatus_t * mock_networkstatus_get_latest_consensus(void) @@ -58,14 +61,19 @@ mock_enable_dos_protection(const networkstatus_t *ns) static void test_dos_conn_creation(void *arg) { + uint64_t monotime_now = 0xfffffffe; + (void) arg; + monotime_enable_test_mocking(); + monotime_coarse_set_mock_time_nsec(monotime_now); MOCK(get_param_cc_enabled, mock_enable_dos_protection); MOCK(get_param_conn_enabled, mock_enable_dos_protection); /* Initialize test data */ or_connection_t or_conn; - time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */ + memset(&or_conn, 0, sizeof or_conn); + time_t wallclock_now = 1281533250; /* 2010-08-11 13:27:30 UTC */ tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&TO_CONN(&or_conn)->addr, "18.0.0.1")); tor_addr_t *addr = &TO_CONN(&or_conn)->addr; @@ -75,13 +83,15 @@ test_dos_conn_creation(void *arg) uint32_t max_concurrent_conns = get_param_conn_max_concurrent_count(NULL); /* Introduce new client */ - geoip_note_client_seen(GEOIP_CLIENT_CONNECT, addr, NULL, now); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, addr, NULL, wallclock_now); { /* Register many conns from this client but not enough to get it blocked */ unsigned int i; for (i = 0; i < max_concurrent_conns; i++) { /* Don't trigger the connect() rate limitation so advance the clock 1 * second for each connection. */ - update_approx_time(++now); + monotime_coarse_set_mock_time_nsec(monotime_now += BILLION); + update_approx_time(++wallclock_now); + or_conn.tracked_for_dos_mitigation = 0; dos_new_client_conn(&or_conn, NULL); } } @@ -91,12 +101,14 @@ test_dos_conn_creation(void *arg) dos_conn_addr_get_defense_type(addr)); /* Register another conn and check that new conns are not allowed anymore */ + or_conn.tracked_for_dos_mitigation = 0; dos_new_client_conn(&or_conn, NULL); tt_int_op(DOS_CONN_DEFENSE_CLOSE, OP_EQ, dos_conn_addr_get_defense_type(addr)); /* Close a client conn and see that a new conn will be permitted again */ dos_close_client_conn(&or_conn); + or_conn.tracked_for_dos_mitigation = 0; tt_int_op(DOS_CONN_DEFENSE_NONE, OP_EQ, dos_conn_addr_get_defense_type(addr)); @@ -107,6 +119,7 @@ test_dos_conn_creation(void *arg) done: dos_free_all(); + monotime_disable_test_mocking(); } /** Helper mock: Place a fake IP addr for this channel in <b>addr_out</b> */ @@ -141,6 +154,7 @@ test_dos_circuit_creation(void *arg) /* Initialize test data */ or_connection_t or_conn; + memset(&or_conn, 0, sizeof or_conn); time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */ tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&TO_CONN(&or_conn)->addr, "18.0.0.1")); @@ -156,6 +170,7 @@ test_dos_circuit_creation(void *arg) * circuit counting subsystem */ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, addr, NULL, now); for (i = 0; i < min_conc_conns_for_cc ; i++) { + or_conn.tracked_for_dos_mitigation = 0; dos_new_client_conn(&or_conn, NULL); } @@ -205,6 +220,7 @@ test_dos_bucket_refill(void *arg) channel_init(chan); chan->is_client = 1; or_connection_t or_conn; + memset(&or_conn, 0, sizeof or_conn); tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&TO_CONN(&or_conn)->addr, "18.0.0.1")); tor_addr_t *addr = &TO_CONN(&or_conn)->addr; @@ -421,12 +437,12 @@ test_dos_bucket_refill(void *arg) dos_free_all(); } -/* Test if we avoid counting a known relay. */ +/* Test if we avoid counting a known relay. (We no longer do) */ static void test_known_relay(void *arg) { clientmap_entry_t *entry = NULL; - routerstatus_t *rs = NULL; microdesc_t *md = NULL; routerinfo_t *ri = NULL; + routerstatus_t *rs = NULL; (void) arg; @@ -446,6 +462,7 @@ test_known_relay(void *arg) /* Setup an OR conn so we can pass it to the DoS subsystem. */ or_connection_t or_conn; + memset(&or_conn, 0, sizeof or_conn); tor_addr_parse(&TO_CONN(&or_conn)->addr, "42.42.42.42"); rs = tor_malloc_zero(sizeof(*rs)); @@ -462,34 +479,24 @@ test_known_relay(void *arg) * client connection. */ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &TO_CONN(&or_conn)->addr, NULL, 0); - /* Suppose we have 5 connections in rapid succession, the counter should - * always be 0 because we should ignore this. */ - dos_new_client_conn(&or_conn, NULL); - dos_new_client_conn(&or_conn, NULL); + /* Suppose we have 5 connections in rapid succession */ dos_new_client_conn(&or_conn, NULL); + or_conn.tracked_for_dos_mitigation = 0; dos_new_client_conn(&or_conn, NULL); + or_conn.tracked_for_dos_mitigation = 0; dos_new_client_conn(&or_conn, NULL); - entry = geoip_lookup_client(&TO_CONN(&or_conn)->addr, NULL, - GEOIP_CLIENT_CONNECT); - tt_assert(entry); - /* We should have a count of 0. */ - tt_uint_op(entry->dos_stats.conn_stats.concurrent_count, OP_EQ, 0); - - /* To make sure that his is working properly, make a unknown client - * connection and see if we do get it. */ - tor_addr_parse(&TO_CONN(&or_conn)->addr, "42.42.42.43"); - geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &TO_CONN(&or_conn)->addr, - NULL, 0); + or_conn.tracked_for_dos_mitigation = 0; dos_new_client_conn(&or_conn, NULL); + or_conn.tracked_for_dos_mitigation = 0; dos_new_client_conn(&or_conn, NULL); entry = geoip_lookup_client(&TO_CONN(&or_conn)->addr, NULL, GEOIP_CLIENT_CONNECT); tt_assert(entry); - /* We should have a count of 2. */ - tt_uint_op(entry->dos_stats.conn_stats.concurrent_count, OP_EQ, 2); + /* We should have a count of 5. */ + tt_uint_op(entry->dos_stats.conn_stats.concurrent_count, OP_EQ, 5); done: - routerstatus_free(rs); routerinfo_free(ri); microdesc_free(md); + routerstatus_free(rs); smartlist_clear(dummy_ns->routerstatus_list); networkstatus_vote_free(dummy_ns); dos_free_all(); @@ -511,6 +518,7 @@ test_dos_conn_rate(void *arg) /* Initialize test data */ or_connection_t or_conn; + memset(&or_conn, 0, sizeof or_conn); time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */ tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&TO_CONN(&or_conn)->addr, "18.0.0.1")); @@ -526,6 +534,7 @@ test_dos_conn_rate(void *arg) { /* Register many conns from this client but not enough to get it blocked */ unsigned int i; for (i = 0; i < burst_conn - 1; i++) { + or_conn.tracked_for_dos_mitigation = 0; dos_new_client_conn(&or_conn, NULL); } } @@ -536,6 +545,7 @@ test_dos_conn_rate(void *arg) /* Register another conn and check that new conns are not allowed anymore. * We should have reached our burst. */ + or_conn.tracked_for_dos_mitigation = 0; dos_new_client_conn(&or_conn, NULL); tt_int_op(DOS_CONN_DEFENSE_CLOSE, OP_EQ, dos_conn_addr_get_defense_type(addr)); diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c index 11a5589d21..a02dca1b60 100644 --- a/src/test/test_hs_client.c +++ b/src/test/test_hs_client.c @@ -15,6 +15,7 @@ #define CIRCUITLIST_PRIVATE #define CONNECTION_PRIVATE #define CRYPT_PATH_PRIVATE +#define TOR_CONGESTION_CONTROL_COMMON_PRIVATE #include "test/test.h" #include "test/test_helpers.h" @@ -54,7 +55,7 @@ #include "core/or/origin_circuit_st.h" #include "core/or/socks_request_st.h" -#define TOR_CONGESTION_CONTROL_PRIVATE +#include "core/or/congestion_control_st.h" #include "core/or/congestion_control_common.h" static int @@ -177,6 +178,7 @@ helper_get_circ_and_stream_for_test(origin_circuit_t **circ_out, /* prop224: Setup hs ident on the circuit */ or_circ->hs_ident = hs_ident_circuit_new(&service_pk); + or_circ->hs_ident->intro_auth_pk.pubkey[0] = 42; TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN; @@ -1186,6 +1188,7 @@ test_socks_hs_errors(void *arg) circ->purpose = CIRCUIT_PURPOSE_C_REND_READY; ocirc = TO_ORIGIN_CIRCUIT(circ); ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey); + ocirc->hs_ident->intro_auth_pk.pubkey[0] = 42; ocirc->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); /* Code path will log this exit so build it. */ ocirc->build_state->chosen_exit = extend_info_new("TestNickname", digest, diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c index 347a5b7174..ea7c589be9 100644 --- a/src/test/test_hs_common.c +++ b/src/test/test_hs_common.c @@ -645,6 +645,19 @@ test_disaster_srv(void *arg) tt_mem_op(cached_disaster_srv_one, OP_EQ, srv_one, DIGEST256_LEN); tt_mem_op(cached_disaster_srv_two, OP_EQ, srv_two, DIGEST256_LEN); + /* For at least one SRV, check that its result was as expected. */ + { + uint8_t srv1_expected[32]; + crypto_digest256( + (char*)srv1_expected, + "shared-random-disaster\0\0\0\0\0\0\x05\xA0\0\0\0\0\0\0\0\1", + strlen("shared-random-disaster")+16, + DIGEST_SHA3_256); + tt_mem_op(srv_one, OP_EQ, srv1_expected, DIGEST256_LEN); + tt_str_op(hex_str((char*)srv_one, DIGEST256_LEN), OP_EQ, + "F8A4948707653837FA44ABB5BBC75A12F6F101E7F8FAF699B9715F4965D3507D"); + } + /* Ask for an SRV that has already been computed */ get_disaster_srv(2, srv_two); /* and check that the cache entries have not changed */ @@ -1509,6 +1522,67 @@ test_reachability(void *arg) } } +static void +test_blinding_basics(void *arg) +{ + (void)arg; + char *mem_op_hex_tmp = NULL; + const uint64_t time_period = 1234; + ed25519_keypair_t keypair; + + time_t instant; + tt_int_op(0, OP_EQ, parse_iso_time("1973-05-20 01:50:33", &instant)); + tt_int_op(1440, OP_EQ, get_time_period_length()); // in minutes, remember. + tt_int_op(time_period, OP_EQ, hs_get_time_period_num(instant)); + + const char pubkey_hex[] = + "833990B085C1A688C1D4C8B1F6B56AFAF5A2ECA674449E1D704F83765CCB7BC6"; + const char seckey_hex[] = + "D8C7FF0E31295B66540D789AF3E3DF992038A9592EEA01D8B7CBA06D6E66D159" + "4D6167696320576F7264733A20737065697373636F62616C742062697669756D"; + base16_decode((char*)keypair.pubkey.pubkey, sizeof(keypair.pubkey.pubkey), + pubkey_hex, strlen(pubkey_hex)); + base16_decode((char*)keypair.seckey.seckey, sizeof(keypair.seckey.seckey), + seckey_hex, strlen(seckey_hex)); + + uint64_t period_len = get_time_period_length(); + tt_u64_op(period_len, OP_EQ, 1440); + uint8_t params[32]; + build_blinded_key_param(&keypair.pubkey, NULL, 0, + time_period, 1440, + params); + test_memeq_hex(params, + "379E50DB31FEE6775ABD0AF6FB7C371E" + "060308F4F847DB09FE4CFE13AF602287"); + + ed25519_public_key_t blinded_public; + hs_build_blinded_pubkey(&keypair.pubkey, NULL, 0, time_period, + &blinded_public); + hs_subcredential_t subcred; + hs_get_subcredential(&keypair.pubkey, &blinded_public, &subcred); + + test_memeq_hex(blinded_public.pubkey, + "3A50BF210E8F9EE955AE0014F7A6917F" + "B65EBF098A86305ABB508D1A7291B6D5"); + test_memeq_hex(subcred.subcred, + "635D55907816E8D76398A675A50B1C2F" + "3E36B42A5CA77BA3A0441285161AE07D"); + + ed25519_keypair_t blinded_keypair; + hs_build_blinded_keypair(&keypair, NULL, 0, time_period, + &blinded_keypair); + tt_mem_op(blinded_public.pubkey, OP_EQ, blinded_keypair.pubkey.pubkey, + ED25519_PUBKEY_LEN); + test_memeq_hex(blinded_keypair.seckey.seckey, + "A958DC83AC885F6814C67035DE817A2C" + "604D5D2F715282079448F789B656350B" + "4540FE1F80AA3F7E91306B7BF7A8E367" + "293352B14A29FDCC8C19F3558075524B"); + + done: + tor_free(mem_op_hex_tmp); +} + /** Pick an HSDir for service with <b>onion_identity_pk</b> as a client. Put * its identity digest in <b>hsdir_digest_out</b>. */ static void @@ -1843,6 +1917,7 @@ test_client_service_hsdir_set_sync(void *arg) } struct testcase_t hs_common_tests[] = { + { "blinding_basics", test_blinding_basics, TT_FORK, NULL, NULL }, { "build_address", test_build_address, TT_FORK, NULL, NULL }, { "validate_address", test_validate_address, TT_FORK, diff --git a/src/test/test_hs_control.c b/src/test/test_hs_control.c index c32803b380..eaeba47b0c 100644 --- a/src/test/test_hs_control.c +++ b/src/test/test_hs_control.c @@ -628,7 +628,7 @@ test_hs_control_store_permanent_creds(void *arg) tor_free(args); tor_free(cp1); - /* Overwrite the credentials and check that they got overwrited. */ + /* Overwrite the credentials and check that they got overwritten. */ args = tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd " "x25519:UDRvZLvcJo0QRLvDfkpgbtsqbkhIUQZyeo2FNBrgS18= " "Flags=Permanent"); diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c index 469e3c39f9..89815f4858 100644 --- a/src/test/test_hs_descriptor.c +++ b/src/test/test_hs_descriptor.c @@ -7,6 +7,7 @@ */ #define HS_DESCRIPTOR_PRIVATE +#define TOR_CONGESTION_CONTROL_COMMON_PRIVATE #include "lib/crypt_ops/crypto_ed25519.h" #include "lib/crypt_ops/crypto_format.h" @@ -25,6 +26,7 @@ #include "test/rng_test_helpers.h" #define TOR_CONGESTION_CONTROL_PRIVATE +#include "core/or/congestion_control_st.h" #include "core/or/congestion_control_common.h" #ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS @@ -364,6 +366,75 @@ test_decode_descriptor(void *arg) hs_helper_desc_equal(desc, decoded); } + /* Decode a descriptor without auth clients, and with PoW data added via + * test_extra_plaintext to test both the normal case of PoW decoding and the + * extra plaintext mechanism itself. */ + { + tor_assert(!desc->encrypted_data.pow_params); + + char pow_seed_base64[HS_POW_SEED_LEN*2]; + uint8_t pow_seed[HS_POW_SEED_LEN]; + crypto_strongest_rand(pow_seed, sizeof pow_seed); + tt_int_op(base64_encode_nopad(pow_seed_base64, sizeof pow_seed_base64, + pow_seed, sizeof pow_seed), OP_GT, 0); + + time_t expiration_time = time(NULL); + char time_buf[ISO_TIME_LEN + 1]; + format_iso_time_nospace(time_buf, expiration_time); + + const unsigned suggested_effort = 123456; + char *extra_plaintext = NULL; + tor_asprintf(&extra_plaintext, + "pow-params v1 %s %u %s\n", + pow_seed_base64, suggested_effort, time_buf); + + tor_free(encoded); + desc->encrypted_data.test_extra_plaintext = extra_plaintext; + ret = hs_desc_encode_descriptor(desc, &signing_kp, NULL, &encoded); + tor_free(extra_plaintext); + desc->encrypted_data.test_extra_plaintext = extra_plaintext; + + tt_int_op(ret, OP_EQ, 0); + tt_assert(encoded); + + desc->encrypted_data.pow_params = + tor_malloc_zero(sizeof(hs_pow_desc_params_t)); + desc->encrypted_data.pow_params->type = HS_POW_DESC_V1; + memcpy(desc->encrypted_data.pow_params->seed, pow_seed, HS_POW_SEED_LEN); + desc->encrypted_data.pow_params->suggested_effort = suggested_effort; + desc->encrypted_data.pow_params->expiration_time = expiration_time; + + hs_descriptor_free(decoded); + ret = hs_desc_decode_descriptor(encoded, &subcredential, NULL, &decoded); + tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK); + tt_assert(decoded); + + hs_helper_desc_equal(desc, decoded); + + tor_free(desc->encrypted_data.pow_params); + } + + /* Now a version of the above that's expected to fail. This reproduces the + * issue from ticket tor#40793, in which pow_params gets too few parameters + * but this would cause an assert instead of an early validation fail. + * Make sure it fails to parse. Prior to the fix for #40793 this fails + * an assertion instead. */ + { + tor_free(encoded); + tor_assert(!desc->encrypted_data.pow_params); + desc->encrypted_data.test_extra_plaintext = "pow-params v1 a a\n"; + ret = hs_desc_encode_descriptor(desc, &signing_kp, NULL, &encoded); + desc->encrypted_data.test_extra_plaintext = NULL; + + tt_int_op(ret, OP_EQ, 0); + tt_assert(encoded); + + hs_descriptor_free(decoded); + ret = hs_desc_decode_descriptor(encoded, &subcredential, NULL, &decoded); + tt_int_op(ret, OP_EQ, HS_DESC_DECODE_ENCRYPTED_ERROR); + tt_assert(!decoded); + } + done: hs_descriptor_free(desc); hs_descriptor_free(desc_no_ip); @@ -845,30 +916,21 @@ test_validate_sendme(void *arg) { (void)arg; - /* Test basic operation: factors of 2X in either direction are OK */ + /* Test basic operation: +/- 1 in either direction are OK */ cc_sendme_inc = 31; - tt_assert(congestion_control_validate_sendme_increment(15)); - tt_assert(congestion_control_validate_sendme_increment(62)); + tt_assert(congestion_control_validate_sendme_increment(30)); + tt_assert(congestion_control_validate_sendme_increment(32)); - /* Test basic operation: Exceeding 2X fails */ + /* Test basic operation: Exceeding +/- 1 fails */ cc_sendme_inc = 31; - tt_assert(!congestion_control_validate_sendme_increment(14)); - tt_assert(!congestion_control_validate_sendme_increment(63)); + tt_assert(!congestion_control_validate_sendme_increment(29)); + tt_assert(!congestion_control_validate_sendme_increment(33)); /* Test potential overflow conditions */ - cc_sendme_inc = 129; - tt_assert(congestion_control_validate_sendme_increment(255)); - tt_assert(congestion_control_validate_sendme_increment(64)); - tt_assert(!congestion_control_validate_sendme_increment(63)); - - cc_sendme_inc = 127; - tt_assert(!congestion_control_validate_sendme_increment(255)); - tt_assert(congestion_control_validate_sendme_increment(254)); - - cc_sendme_inc = 255; + cc_sendme_inc = 254; tt_assert(congestion_control_validate_sendme_increment(255)); - tt_assert(congestion_control_validate_sendme_increment(127)); - tt_assert(!congestion_control_validate_sendme_increment(126)); + tt_assert(congestion_control_validate_sendme_increment(253)); + tt_assert(!congestion_control_validate_sendme_increment(252)); /* Test 0 case */ cc_sendme_inc = 1; diff --git a/src/test/test_hs_dos.c b/src/test/test_hs_dos.c index 70f2ef412f..81410f7b9b 100644 --- a/src/test/test_hs_dos.c +++ b/src/test/test_hs_dos.c @@ -16,6 +16,7 @@ #include "test/log_test_helpers.h" #include "app/config/config.h" +#include "lib/time/compat_time.h" #include "core/or/circuitlist.h" #include "core/or/circuituse.h" @@ -45,7 +46,8 @@ free_mock_consensus(void) static void test_can_send_intro2(void *arg) { - uint32_t now = (uint32_t) approx_time(); + static const uint64_t BILLION = 1000000000; + uint64_t now = 12345; or_circuit_t *or_circ = NULL; (void) arg; @@ -55,6 +57,8 @@ test_can_send_intro2(void *arg) get_options_mutable()->ORPort_set = 1; setup_mock_consensus(); + monotime_enable_test_mocking(); + monotime_coarse_set_mock_time_nsec(now); or_circ = or_circuit_new(1, NULL); @@ -68,7 +72,7 @@ test_can_send_intro2(void *arg) /* Simulate that 10 cells have arrived in 1 second. There should be no * refill since the bucket is already at maximum on the first cell. */ - update_approx_time(++now); + monotime_coarse_set_mock_time_nsec(now += BILLION); for (int i = 0; i < 10; i++) { tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ)); } @@ -76,7 +80,7 @@ test_can_send_intro2(void *arg) get_intro2_burst_consensus_param(NULL) - 10); /* Fully refill the bucket minus 1 cell. */ - update_approx_time(++now); + monotime_coarse_set_mock_time_nsec(now += BILLION); tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ)); tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, get_intro2_burst_consensus_param(NULL) - 1); @@ -84,7 +88,7 @@ test_can_send_intro2(void *arg) /* Receive an INTRODUCE2 at each second. We should have the bucket full * since at every second it gets refilled. */ for (int i = 0; i < 10; i++) { - update_approx_time(++now); + monotime_coarse_set_mock_time_nsec(now += BILLION); tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ)); } /* Last check if we can send the cell decrements the bucket so minus 1. */ @@ -92,7 +96,8 @@ test_can_send_intro2(void *arg) get_intro2_burst_consensus_param(NULL) - 1); /* Manually reset bucket for next test. */ - token_bucket_ctr_reset(&or_circ->introduce2_bucket, now); + token_bucket_ctr_reset(&or_circ->introduce2_bucket, + (uint32_t) monotime_coarse_absolute_sec()); tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, get_intro2_burst_consensus_param(NULL)); @@ -115,7 +120,7 @@ test_can_send_intro2(void *arg) } /* One second has passed, we should have the rate minus 1 cell added. */ - update_approx_time(++now); + monotime_coarse_set_mock_time_nsec(now += BILLION); tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ)); tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, get_intro2_rate_consensus_param(NULL) - 1); @@ -125,6 +130,7 @@ test_can_send_intro2(void *arg) hs_free_all(); free_mock_consensus(); + monotime_disable_test_mocking(); } static void diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c index cbcdeade92..82b7ec029d 100644 --- a/src/test/test_hs_intropoint.c +++ b/src/test/test_hs_intropoint.c @@ -14,6 +14,7 @@ #include "test/test.h" #include "test/log_test_helpers.h" #include "lib/crypt_ops/crypto_rand.h" +#include "lib/time/compat_time.h" #include "core/or/or.h" #include "core/or/channel.h" @@ -127,7 +128,7 @@ helper_create_intro_circuit(void) tt_assert(circ); circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); token_bucket_ctr_init(&circ->introduce2_bucket, 100, 100, - (uint32_t) approx_time()); + (uint32_t) monotime_coarse_absolute_sec()); done: return circ; } diff --git a/src/test/test_hs_metrics.c b/src/test/test_hs_metrics.c index 8625933df7..acb0649434 100644 --- a/src/test/test_hs_metrics.c +++ b/src/test/test_hs_metrics.c @@ -40,7 +40,8 @@ test_metrics(void *arg) /* Update entry by identifier. */ hs_metrics_update_by_ident(HS_METRICS_NUM_INTRODUCTIONS, - &service->keys.identity_pk, 0, 42); + &service->keys.identity_pk, 0, NULL, 42, + 0, false); /* Confirm the entry value. */ const smartlist_t *entries = metrics_store_get_all(service->metrics.store, @@ -53,9 +54,42 @@ test_metrics(void *arg) /* Update entry by service now. */ hs_metrics_update_by_service(HS_METRICS_NUM_INTRODUCTIONS, - service, 0, 42); + service, 0, NULL, 42, 0, false); tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 84); + const char *reason = HS_METRICS_ERR_INTRO_REQ_BAD_AUTH_KEY; + + /* Update tor_hs_intro_rejected_intro_req_count */ + hs_metrics_update_by_ident(HS_METRICS_NUM_REJECTED_INTRO_REQ, + &service->keys.identity_pk, 0, + reason, 112, 0, false); + + entries = metrics_store_get_all(service->metrics.store, + "tor_hs_intro_rejected_intro_req_count"); + tt_assert(entries); + tt_int_op(smartlist_len(entries), OP_EQ, + hs_metrics_intro_req_error_reasons_size); + + entry = metrics_store_find_entry_with_label( + entries, "reason=\"bad_auth_key\""); + tt_assert(entry); + tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 112); + + /* Update tor_hs_intro_rejected_intro_req_count entry by service now. */ + hs_metrics_update_by_service(HS_METRICS_NUM_REJECTED_INTRO_REQ, service, 0, + reason, 10, 0, false); + tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 122); + + /* So far these have been relative updates. Test updates with reset */ + hs_metrics_update_by_service(HS_METRICS_NUM_REJECTED_INTRO_REQ, + service, 0, reason, 10, 0, true); + tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 10); + + hs_metrics_update_by_ident(HS_METRICS_NUM_REJECTED_INTRO_REQ, + &service->keys.identity_pk, 0, reason, + 345, 0, true); + tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 345); + done: hs_free_all(); } diff --git a/src/test/test_hs_pow.c b/src/test/test_hs_pow.c new file mode 100644 index 0000000000..072fbacff4 --- /dev/null +++ b/src/test/test_hs_pow.c @@ -0,0 +1,500 @@ +/* Copyright (c) 2020-2023, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_pow.c + * \brief Tests for service proof-of-work verification and wire protocol. + */ + +#define HS_SERVICE_PRIVATE +#define HS_CIRCUIT_PRIVATE + +#include "lib/cc/compat_compiler.h" +#include "lib/cc/torint.h" + +#include "test/hs_test_helpers.h" +#include "test/log_test_helpers.h" +#include "test/test_helpers.h" +#include "test/test.h" + +#include "app/config/config.h" +#include "core/or/circuitbuild.h" +#include "core/or/circuitlist.h" +#include "core/or/relay.h" +#include "feature/hs/hs_cell.h" +#include "feature/hs/hs_circuit.h" +#include "feature/hs/hs_metrics.h" +#include "feature/hs/hs_pow.h" +#include "feature/hs/hs_service.h" +#include "feature/nodelist/nodelist.h" + +#include "core/or/crypt_path_st.h" +#include "core/or/origin_circuit_st.h" +#include "feature/nodelist/node_st.h" +#include "feature/nodelist/routerinfo_st.h" + +#include "trunnel/hs/cell_introduce1.h" + +static int test_rend_launch_count; +static uint32_t test_rend_launch_expect_effort; + +static void +mock_launch_rendezvous_point_circuit(const hs_service_t *service, + const ed25519_public_key_t *ip_auth_pubkey, + const curve25519_keypair_t *ip_enc_key_kp, + const hs_cell_intro_rdv_data_t *rdv_data, + time_t now) +{ + (void) service; + (void) ip_auth_pubkey; + (void) ip_enc_key_kp; + (void) rdv_data; + (void) now; + + tt_int_op(test_rend_launch_expect_effort, OP_EQ, rdv_data->pow_effort); + test_rend_launch_count++; + +done: + ; +} + +static node_t *fake_node = NULL; + +static const node_t * +mock_build_state_get_exit_node(cpath_build_state_t *state) +{ + (void) state; + + if (!fake_node) { + curve25519_secret_key_t seckey; + curve25519_secret_key_generate(&seckey, 0); + + fake_node = tor_malloc_zero(sizeof(node_t)); + fake_node->ri = tor_malloc_zero(sizeof(routerinfo_t)); + fake_node->ri->onion_curve25519_pkey = + tor_malloc_zero(sizeof(curve25519_public_key_t)); + curve25519_public_key_generate(fake_node->ri->onion_curve25519_pkey, + &seckey); + } + + return fake_node; +} + +static smartlist_t * +mock_node_get_link_specifier_smartlist(const node_t *node, bool direct_conn) +{ + (void) node; + (void) direct_conn; + + smartlist_t *lspecs = smartlist_new(); + link_specifier_t *ls_legacy = link_specifier_new(); + smartlist_add(lspecs, ls_legacy); + + return lspecs; +} + +static size_t relay_payload_len; +static uint8_t relay_payload[RELAY_PAYLOAD_SIZE]; + +static int +mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ, + uint8_t relay_command, const char *payload, + size_t payload_len, + crypt_path_t *cpath_layer, + const char *filename, int lineno) +{ + (void) stream_id; + (void) circ; + (void) relay_command; + (void) payload; + (void) payload_len; + (void) cpath_layer; + (void) filename; + (void) lineno; + + memcpy(relay_payload, payload, payload_len); + relay_payload_len = payload_len; + + return 0; +} + +typedef struct testing_hs_pow_service_t { + hs_service_t service; + hs_subcredential_t subcred; + hs_service_intro_point_t *service_ip; + hs_desc_intro_point_t *desc_ip; + hs_ntor_intro_cell_keys_t intro_keys; + origin_circuit_t *intro_circ; + origin_circuit_t *rend_circ; +} testing_hs_pow_service_t; + +/* Common test setup */ +static testing_hs_pow_service_t * +testing_hs_pow_service_new(void) +{ + MOCK(build_state_get_exit_node, mock_build_state_get_exit_node); + MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge); + MOCK(launch_rendezvous_point_circuit, mock_launch_rendezvous_point_circuit); + MOCK(node_get_link_specifier_smartlist, + mock_node_get_link_specifier_smartlist); + + testing_hs_pow_service_t *tsvc = tor_malloc_zero(sizeof *tsvc); + hs_metrics_service_init(&tsvc->service); + + ed25519_keypair_t identity_keypair; + ed25519_keypair_generate(&identity_keypair, 0); + hs_helper_get_subcred_from_identity_keypair(&identity_keypair, + &tsvc->subcred); + + curve25519_secret_key_t seckey; + curve25519_public_key_t pkey; + curve25519_secret_key_generate(&seckey, 0); + curve25519_public_key_generate(&pkey, &seckey); + + node_t intro_node; + memset(&intro_node, 0, sizeof(intro_node)); + routerinfo_t ri; + memset(&ri, 0, sizeof(routerinfo_t)); + ri.onion_curve25519_pkey = &pkey; + intro_node.ri = &ri; + + hs_service_intro_point_t *svc_ip = service_intro_point_new(&intro_node); + const ed25519_public_key_t *ip_auth_pubkey = &svc_ip->auth_key_kp.pubkey; + const curve25519_public_key_t *ip_enc_pubkey = &svc_ip->enc_key_kp.pubkey; + tsvc->service_ip = svc_ip; + + ed25519_keypair_t signing_kp; + ed25519_keypair_generate(&signing_kp, 0); + tsvc->desc_ip = hs_helper_build_intro_point(&signing_kp, 0, "1.2.3.4", 0, + &svc_ip->auth_key_kp, + &svc_ip->enc_key_kp); + + tsvc->intro_circ = origin_circuit_new(); + tsvc->rend_circ = origin_circuit_new(); + + tsvc->intro_circ->cpath = tor_malloc_zero(sizeof(crypt_path_t)); + + struct hs_ident_circuit_t *hs_ident = tor_malloc_zero(sizeof *hs_ident); + tsvc->rend_circ->hs_ident = hs_ident; + tsvc->intro_circ->hs_ident = hs_ident; + curve25519_keypair_generate(&hs_ident->rendezvous_client_kp, 0); + tt_int_op(0, OP_EQ, + hs_ntor_client_get_introduce1_keys(ip_auth_pubkey, + ip_enc_pubkey, + &hs_ident->rendezvous_client_kp, + &tsvc->subcred, + &tsvc->intro_keys)); + done: + return tsvc; +} + +static void +testing_hs_pow_service_free(testing_hs_pow_service_t *tsvc) +{ + hs_metrics_service_free(&tsvc->service); + service_intro_point_free(tsvc->service_ip); + hs_desc_intro_point_free(tsvc->desc_ip); + hs_pow_free_service_state(tsvc->service.state.pow_state); + tor_free(tsvc); + + if (fake_node) { + tor_free(fake_node->ri->onion_curve25519_pkey); + tor_free(fake_node->ri); + tor_free(fake_node); + } + + UNMOCK(build_state_get_exit_node); + UNMOCK(relay_send_command_from_edge_); + UNMOCK(launch_rendezvous_point_circuit); + UNMOCK(node_get_link_specifier_smartlist); +} + +/* Make sure we can send a PoW extension to a service without PoW enabled */ +static void +test_hs_pow_unsolicited(void *arg) +{ + (void)arg; + + testing_hs_pow_service_t *tsvc = testing_hs_pow_service_new(); + + /* Try this twice, changing only the presence or lack of PoW solution */ + for (int test_variant = 0; test_variant < 2; test_variant++) { + + relay_payload_len = 0; + test_rend_launch_count = 0; + test_rend_launch_expect_effort = 0; + memset(relay_payload, 0, sizeof relay_payload); + + hs_pow_solution_t solution = { 0 }; + int retval; + + retval = hs_circ_send_introduce1(tsvc->intro_circ, tsvc->rend_circ, + tsvc->desc_ip, &tsvc->subcred, + test_variant == 0 ? &solution : NULL); + + tt_int_op(retval, OP_EQ, 0); + tt_assert(!fast_mem_is_zero((const char*)relay_payload, + sizeof relay_payload)); + tt_int_op(relay_payload_len, OP_NE, 0); + + retval = hs_circ_handle_introduce2(&tsvc->service, tsvc->intro_circ, + tsvc->service_ip, &tsvc->subcred, + relay_payload, + relay_payload_len); + + tt_int_op(retval, OP_EQ, test_variant == 0 ? -1 : 0); + tt_int_op(test_rend_launch_count, OP_EQ, test_variant == 0 ? 0 : 1); + } + + done: + testing_hs_pow_service_free(tsvc); +} + +static void +test_hs_pow_vectors(void *arg) +{ + (void)arg; + + /* This covers encoding, wire protocol, and verification for PoW-extended + * introduction cells. The solutions here can be generated using the + * setup in test_hs_pow_slow. + */ + static const struct { + uint32_t claimed_effort; + uint32_t validated_effort; + int expected_retval; + const char *seed_hex; + const char *service_blinded_id_hex; + const char *nonce_hex; + const char *sol_hex; + const char *encoded_hex; + } vectors[] = { + { + /* All zero, expect invalid */ + 1, 0, -1, + "0000000000000000000000000000000000000000000000000000000000000000", + "1111111111111111111111111111111111111111111111111111111111111111", + "00000000000000000000000000000000", "00000000000000000000000000000000", + "01" + "00000000000000000000000000000000" + "00000001" "00000000" + "00000000000000000000000000000000" + }, + { + /* Valid zero-effort solution */ + 0, 0, 0, + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "1111111111111111111111111111111111111111111111111111111111111111", + "55555555555555555555555555555555", "4312f87ceab844c78e1c793a913812d7", + "01" + "55555555555555555555555555555555" + "00000000" "aaaaaaaa" + "4312f87ceab844c78e1c793a913812d7" + }, + { + /* Valid high-effort solution */ + 1000000, 1000000, 0, + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "1111111111111111111111111111111111111111111111111111111111111111", + "59217255555555555555555555555555", "0f3db97b9cac20c1771680a1a34848d3", + "01" + "59217255555555555555555555555555" + "000f4240" "aaaaaaaa" + "0f3db97b9cac20c1771680a1a34848d3" + }, + { + /* Reject replays */ + 1000000, 0, -1, + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "1111111111111111111111111111111111111111111111111111111111111111", + "59217255555555555555555555555555", "0f3db97b9cac20c1771680a1a34848d3", + "01" + "59217255555555555555555555555555" + "000f4240" "aaaaaaaa" + "0f3db97b9cac20c1771680a1a34848d3" + }, + { + /* The claimed effort must exactly match what's in the challenge */ + 99999, 0, -1, + "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f", + "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed", + "2eff9fdbc34326d9d2f18ed277469c63", "400cb091139f86b352119f6e131802d6", + "01" + "2eff9fdbc34326d9d2f18ed277469c63" + "0001869f" "86fb0acf" + "400cb091139f86b352119f6e131802d6" + }, + { + /* Otherwise good solution but with a corrupted nonce */ + 100000, 0, -1, + "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f", + "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed", + "2eff9fdbc34326d9a2f18ed277469c63", "400cb091139f86b352119f6e131802d6", + "01" + "2eff9fdbc34326d9a2f18ed277469c63" + "000186a0" "86fb0acf" + "400cb091139f86b352119f6e131802d6" + }, + { + /* Corrected version of above */ + 100000, 100000, 0, + "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f", + "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed", + "2eff9fdbc34326d9d2f18ed277469c63", "400cb091139f86b352119f6e131802d6", + "01" + "2eff9fdbc34326d9d2f18ed277469c63" + "000186a0" "86fb0acf" + "400cb091139f86b352119f6e131802d6" + } + }; + + testing_hs_pow_service_t *tsvc = testing_hs_pow_service_new(); + hs_pow_service_state_t *pow_state = tor_malloc_zero(sizeof *pow_state); + tsvc->service.state.pow_state = pow_state; + tsvc->service.desc_current = service_descriptor_new(); + pow_state->rend_request_pqueue = smartlist_new(); + + char *mem_op_hex_tmp = NULL; + uint8_t *decrypted = NULL; + trn_cell_introduce_encrypted_t *enc_cell = NULL; + trn_cell_introduce1_t *cell = NULL; + + const unsigned num_vectors = sizeof vectors / sizeof vectors[0]; + for (unsigned vec_i = 0; vec_i < num_vectors; vec_i++) { + const int expected_retval = vectors[vec_i].expected_retval; + const char *service_blinded_id_hex = vectors[vec_i].service_blinded_id_hex; + const char *seed_hex = vectors[vec_i].seed_hex; + const char *nonce_hex = vectors[vec_i].nonce_hex; + const char *sol_hex = vectors[vec_i].sol_hex; + const char *encoded_hex = vectors[vec_i].encoded_hex; + + relay_payload_len = 0; + test_rend_launch_count = 0; + test_rend_launch_expect_effort = vectors[vec_i].validated_effort; + memset(relay_payload, 0, sizeof relay_payload); + + hs_pow_solution_t solution = { + .effort = vectors[vec_i].claimed_effort, + }; + int retval; + + tt_int_op(strlen(service_blinded_id_hex), OP_EQ, 2 * HS_POW_ID_LEN); + tt_int_op(strlen(seed_hex), OP_EQ, 2 * HS_POW_SEED_LEN); + tt_int_op(strlen(nonce_hex), OP_EQ, 2 * sizeof solution.nonce); + tt_int_op(strlen(sol_hex), OP_EQ, 2 * sizeof solution.equix_solution); + + tt_assert(tsvc->service.desc_current); + ed25519_public_key_t *desc_blinded_pubkey = + &tsvc->service.desc_current->desc->plaintext_data.blinded_pubkey; + + tt_int_op(base16_decode((char*)desc_blinded_pubkey->pubkey, + HS_POW_ID_LEN, service_blinded_id_hex, + 2 * HS_POW_ID_LEN), + OP_EQ, HS_POW_ID_LEN); + tt_int_op(base16_decode((char*)pow_state->seed_previous, HS_POW_SEED_LEN, + seed_hex, 2 * HS_POW_SEED_LEN), + OP_EQ, HS_POW_SEED_LEN); + tt_int_op(base16_decode((char*)solution.nonce, HS_POW_NONCE_LEN, + nonce_hex, 2 * HS_POW_NONCE_LEN), + OP_EQ, HS_POW_NONCE_LEN); + tt_int_op(base16_decode((char*)solution.equix_solution, HS_POW_EQX_SOL_LEN, + sol_hex, 2 * HS_POW_EQX_SOL_LEN), + OP_EQ, HS_POW_EQX_SOL_LEN); + + ed25519_pubkey_copy(&tsvc->service_ip->blinded_id, desc_blinded_pubkey); + memcpy(solution.seed_head, pow_state->seed_previous, HS_POW_SEED_HEAD_LEN); + + /* Try to encode 'solution' into a relay cell */ + + retval = hs_circ_send_introduce1(tsvc->intro_circ, tsvc->rend_circ, + tsvc->desc_ip, &tsvc->subcred, + &solution); + + tt_int_op(retval, OP_EQ, 0); + tt_assert(!fast_mem_is_zero((const char*)relay_payload, + sizeof relay_payload)); + tt_int_op(relay_payload_len, OP_NE, 0); + + /* Check the service's response to this introduction */ + + retval = hs_circ_handle_introduce2(&tsvc->service, tsvc->intro_circ, + tsvc->service_ip, &tsvc->subcred, + relay_payload, + relay_payload_len); + tt_int_op(retval, OP_EQ, expected_retval); + tt_int_op(test_rend_launch_count, OP_EQ, expected_retval == 0 ? 1 : 0); + + /* Start unpacking the cell ourselves so we can check the PoW data */ + + trn_cell_introduce1_free(cell); + cell = NULL; + tt_int_op(trn_cell_introduce1_parse(&cell, relay_payload, + relay_payload_len), OP_GT, 0); + + size_t encrypted_section_len; + const uint8_t *encrypted_section; + encrypted_section = trn_cell_introduce1_getconstarray_encrypted(cell); + encrypted_section_len = trn_cell_introduce1_getlen_encrypted(cell); + tt_int_op(encrypted_section_len, OP_GT, + DIGEST256_LEN + CURVE25519_PUBKEY_LEN); + + /* Decrypt the encrypted portion of the INTRODUCE1 */ + + crypto_cipher_t *cipher = NULL; + cipher = crypto_cipher_new_with_bits((char *) tsvc->intro_keys.enc_key, + CURVE25519_PUBKEY_LEN * 8); + tt_ptr_op(cipher, OP_NE, NULL); + + size_t decrypted_len = encrypted_section_len + - DIGEST256_LEN - CURVE25519_PUBKEY_LEN; + tor_free(decrypted); + decrypted = tor_malloc_zero(decrypted_len); + retval = crypto_cipher_decrypt(cipher, (char *) decrypted, + (const char *) encrypted_section + + CURVE25519_PUBKEY_LEN, + decrypted_len); + crypto_cipher_free(cipher); + tt_int_op(retval, OP_EQ, 0); + + /* Parse the outer layer of the encrypted payload */ + + trn_cell_introduce_encrypted_free(enc_cell); + enc_cell = NULL; + tt_int_op(trn_cell_introduce_encrypted_parse(&enc_cell, decrypted, + decrypted_len), OP_GT, 0); + + /* Check for the expected single extension */ + + const trn_extension_t *extensions = + trn_cell_introduce_encrypted_get_extensions(enc_cell); + tt_int_op(trn_extension_get_num(extensions), OP_EQ, 1); + + const trn_extension_field_t *field = + trn_extension_getconst_fields(extensions, 0); + tt_int_op(trn_extension_field_get_field_type(field), + OP_EQ, TRUNNEL_EXT_TYPE_POW); + + const uint8_t *field_data = trn_extension_field_getconstarray_field(field); + size_t field_len = trn_extension_field_getlen_field(field); + + /* Our test vectors cover the packed data in the single extension */ + + tt_int_op(field_len * 2, OP_EQ, strlen(encoded_hex)); + test_memeq_hex(field_data, encoded_hex); + } + + done: + tor_free(mem_op_hex_tmp); + tor_free(decrypted); + trn_cell_introduce1_free(cell); + trn_cell_introduce_encrypted_free(enc_cell); + service_descriptor_free(tsvc->service.desc_current); + testing_hs_pow_service_free(tsvc); + hs_pow_remove_seed_from_cache(NULL); +} + +struct testcase_t hs_pow_tests[] = { + { "unsolicited", test_hs_pow_unsolicited, TT_FORK, NULL, NULL }, + { "vectors", test_hs_pow_vectors, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; diff --git a/src/test/test_hs_pow_slow.c b/src/test/test_hs_pow_slow.c new file mode 100644 index 0000000000..ff715cf53e --- /dev/null +++ b/src/test/test_hs_pow_slow.c @@ -0,0 +1,273 @@ +/* Copyright (c) 2020-2023, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_pow_slow.c + * \brief Slower (solve + verify) tests for service proof-of-work defenses. + */ + +#define HS_SERVICE_PRIVATE + +#include "lib/cc/compat_compiler.h" +#include "lib/cc/torint.h" + +#include "test/test.h" +#include "test/test_helpers.h" +#include "test/log_test_helpers.h" +#include "test/rng_test_helpers.h" + +#include "app/config/config.h" +#include "feature/hs/hs_pow.h" + +static int +testing_one_hs_pow_solution(const hs_pow_solution_t *ref_solution, + const ed25519_public_key_t *service_blinded_id, + const uint8_t *seed) +{ + int retval = -1; + hs_pow_solution_t sol_buffer; + hs_pow_service_state_t *s = tor_malloc_zero(sizeof(hs_pow_service_state_t)); + s->rend_request_pqueue = smartlist_new(); + + memcpy(s->seed_previous, seed, HS_POW_SEED_LEN); + + const unsigned num_variants = 10; + const unsigned num_attempts = 3; + + for (unsigned variant = 0; variant < num_variants; variant++) { + hs_pow_remove_seed_from_cache(seed); + + for (unsigned attempt = 0; attempt < num_attempts; attempt++) { + int expected = -1; + memcpy(&sol_buffer, ref_solution, sizeof sol_buffer); + + /* One positive test, and a few negative tests of corrupted solutions */ + if (variant == 0) { + if (attempt == 0) { + /* Only the first attempt should succeed (nonce replay) */ + expected = 0; + } + } else if (variant & 1) { + sol_buffer.nonce[variant / 2 % HS_POW_NONCE_LEN]++; + } else { + sol_buffer.equix_solution[variant / 2 % HS_POW_EQX_SOL_LEN]++; + } + + tt_int_op(expected, OP_EQ, + hs_pow_verify(service_blinded_id, s, &sol_buffer)); + } + } + + retval = 0; +done: + hs_pow_free_service_state(s); + return retval; +} + +static void +test_hs_pow_vectors(void *arg) +{ + (void)arg; + + /* All test vectors include a solve, verify, and fail-verify phase + * as well as a test of the nonce replay cache. The initial nonce for the + * solution search is set via the solver's RNG data. The amount of solve + * time during test execution can be tuned based on how far away from the + * winning nonce our solve_rng value is set. + */ + static const struct { + uint32_t effort; + const char *solve_rng_hex; + const char *seed_hex; + const char *service_blinded_id_hex; + const char *nonce_hex; + const char *sol_hex; + } vectors[] = { + { + 0, "55555555555555555555555555555555", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "1111111111111111111111111111111111111111111111111111111111111111", + "55555555555555555555555555555555", "4312f87ceab844c78e1c793a913812d7" + }, + { + 1, "55555555555555555555555555555555", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "1111111111111111111111111111111111111111111111111111111111111111", + "55555555555555555555555555555555", "84355542ab2b3f79532ef055144ac5ab" + }, + { + 1, "55555555555555555555555555555555", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "1111111111111111111111111111111111111111111111111111111111111110", + "55555555555555555555555555555555", "115e4b70da858792fc205030b8c83af9" + }, + { + 2, "55555555555555555555555555555555", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "1111111111111111111111111111111111111111111111111111111111111111", + "55555555555555555555555555555555", "4600a93a535ed76dc746c99942ab7de2" + }, + { + 10, "55555555555555555555555555555555", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "1111111111111111111111111111111111111111111111111111111111111111", + "56555555555555555555555555555555", "128bbda5df2929c3be086de2aad34aed" + }, + { + 10, "ffffffffffffffffffffffffffffffff", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "1111111111111111111111111111111111111111111111111111111111111111", + "01000000000000000000000000000000", "203af985537fadb23f3ed5873b4c81ce" + }, + { + 1337, "7fffffffffffffffffffffffffffffff", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "4111111111111111111111111111111111111111111111111111111111111111", + "01000000000000000000000000000000", "31c377cb72796ed80ae77df6ac1d6bfd" + }, + { + 31337, "34a20000000000000000000000000000", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "1111111111111111111111111111111111111111111111111111111111111111", + "36a20000000000000000000000000000", "ca6899b91113aaf7536f28db42526bff" + }, + { + 100, "55555555555555555555555555555555", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "1111111111111111111111111111111111111111111111111111111111111111", + "56555555555555555555555555555555", "3a4122a240bd7abfc922ab3cbb9479ed" + }, + { + 1000, "d3555555555555555555555555555555", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "1111111111111111111111111111111111111111111111111111111111111111", + "d4555555555555555555555555555555", "338cc08f57697ce8ac2e4b453057d6e9" + }, + { + 10000, "c5715555555555555555555555555555", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "1111111111111111111111111111111111111111111111111111111111111111", + "c8715555555555555555555555555555", "9f2d3d4ed831ac96ad34c25fb59ff3e2" + }, + { + 100000, "418d5655555555555555555555555555", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "1111111111111111111111111111111111111111111111111111111111111111", + "428d5655555555555555555555555555", "9863f3acd2d15adfd244a7ca61d4c6ff" + }, + { + 1000000, "58217255555555555555555555555555", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "1111111111111111111111111111111111111111111111111111111111111111", + "59217255555555555555555555555555", "0f3db97b9cac20c1771680a1a34848d3" + }, + { + 1, "d0aec1669384bfe5ed39cd724d6c7954", + "c52be1f8a5e6cc3b8fb71cfdbe272cbc91d4d035400f2f94fb0d0074794e0a07", + "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed", + "d1aec1669384bfe5ed39cd724d6c7954", "462606e5f8c2f3f844127b8bfdd6b4ff" + }, + { + 1, "b4d0e611e6935750fcf9406aae131f62", + "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f", + "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed", + "b4d0e611e6935750fcf9406aae131f62", "9f3fbd50b1a83fb63284bde44318c0fd" + }, + { + 1, "b4d0e611e6935750fcf9406aae131f62", + "9dfbd06d86fed8e12de3ab214e1a63ea61f46253fe08346a20378da70c4a327d", + "bec632eb76123956f99a06d394fcbee8f135b8ed01f2e90aabe404cb0346744a", + "b4d0e611e6935750fcf9406aae131f62", "161baa7490356292d020065fdbe55ffc" + }, + { + 1, "40559fdbc34326d9d2f18ed277469c63", + "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f", + "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed", + "40559fdbc34326d9d2f18ed277469c63", "fa649c6a2c5c0bb6a3511b9ea4b448d1" + }, + { + 10000, "34569fdbc34326d9d2f18ed277469c63", + "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f", + "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed", + "36569fdbc34326d9d2f18ed277469c63", "2802951e623c74adc443ab93e99633ee" + }, + { + 100000, "2cff9fdbc34326d9d2f18ed277469c63", + "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f", + "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed", + "2eff9fdbc34326d9d2f18ed277469c63", "400cb091139f86b352119f6e131802d6" + }, + { + 1000000, "5243b3dbc34326d9d2f18ed277469c63", + "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f", + "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed", + "5543b3dbc34326d9d2f18ed277469c63", "b47c718b56315e9697173a6bac1feaa4" + }, + }; + + const unsigned num_vectors = sizeof vectors / sizeof vectors[0]; + for (unsigned vec_i = 0; vec_i < num_vectors; vec_i++) { + const char *seed_hex = vectors[vec_i].seed_hex; + const char *service_blinded_id_hex = vectors[vec_i].service_blinded_id_hex; + const char *solve_rng_hex = vectors[vec_i].solve_rng_hex; + const char *nonce_hex = vectors[vec_i].nonce_hex; + const char *sol_hex = vectors[vec_i].sol_hex; + + uint8_t rng_bytes[HS_POW_NONCE_LEN]; + hs_pow_solution_t output; + hs_pow_solution_t solution = { 0 }; + hs_pow_solver_inputs_t input = { + .effort = vectors[vec_i].effort, + .CompiledProofOfWorkHash = -1 + }; + + tt_int_op(strlen(service_blinded_id_hex), OP_EQ, 2 * HS_POW_ID_LEN); + tt_int_op(strlen(seed_hex), OP_EQ, 2 * sizeof input.seed); + tt_int_op(strlen(solve_rng_hex), OP_EQ, 2 * sizeof rng_bytes); + tt_int_op(strlen(nonce_hex), OP_EQ, 2 * sizeof solution.nonce); + tt_int_op(strlen(sol_hex), OP_EQ, 2 * sizeof solution.equix_solution); + + tt_int_op(base16_decode((char*)input.service_blinded_id.pubkey, + HS_POW_ID_LEN, service_blinded_id_hex, + 2 * HS_POW_ID_LEN), + OP_EQ, HS_POW_ID_LEN); + tt_int_op(base16_decode((char*)input.seed, HS_POW_SEED_LEN, + seed_hex, 2 * HS_POW_SEED_LEN), + OP_EQ, HS_POW_SEED_LEN); + tt_int_op(base16_decode((char*)rng_bytes, sizeof rng_bytes, + solve_rng_hex, 2 * sizeof rng_bytes), + OP_EQ, HS_POW_NONCE_LEN); + tt_int_op(base16_decode((char*)&solution.nonce, sizeof solution.nonce, + nonce_hex, 2 * sizeof solution.nonce), + OP_EQ, HS_POW_NONCE_LEN); + tt_int_op(base16_decode((char*)&solution.equix_solution, + sizeof solution.equix_solution, + sol_hex, 2 * sizeof solution.equix_solution), + OP_EQ, HS_POW_EQX_SOL_LEN); + memcpy(solution.seed_head, input.seed, HS_POW_SEED_HEAD_LEN); + + memset(&output, 0xaa, sizeof output); + testing_enable_prefilled_rng(rng_bytes, HS_POW_NONCE_LEN); + tt_int_op(0, OP_EQ, hs_pow_solve(&input, &output)); + testing_disable_prefilled_rng(); + + tt_mem_op(solution.seed_head, OP_EQ, output.seed_head, + sizeof output.seed_head); + tt_mem_op(solution.nonce, OP_EQ, output.nonce, + sizeof output.nonce); + tt_mem_op(&solution.equix_solution, OP_EQ, &output.equix_solution, + sizeof output.equix_solution); + + tt_int_op(testing_one_hs_pow_solution(&output, &input.service_blinded_id, + input.seed), OP_EQ, 0); + } + + done: + testing_disable_prefilled_rng(); + hs_pow_remove_seed_from_cache(NULL); +} + +struct testcase_t slow_hs_pow_tests[] = { + { "vectors", test_hs_pow_vectors, 0, NULL, NULL }, + END_OF_TESTCASES +}; diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 482ee1a014..dc60c7ca29 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -1183,6 +1183,8 @@ test_bad_introduce2(void *arg) origin_circuit_t *circ = NULL; hs_service_t *service = NULL; hs_service_intro_point_t *ip = NULL; + const smartlist_t *entries = NULL; + const metrics_store_entry_t *entry = NULL; (void) arg; @@ -1227,6 +1229,37 @@ test_bad_introduce2(void *arg) "an INTRODUCE2 cell on circuit"); teardown_capture_of_logs(); + entries = metrics_store_get_all(service->metrics.store, + "tor_hs_intro_rejected_intro_req_count"); + + tt_assert(entries); + /* There are `hs_metrics_intro_req_size` entries (one for each + * possible `reason` label value). */ + tt_int_op(smartlist_len(entries), OP_EQ, + hs_metrics_intro_req_error_reasons_size); + + /* Make sure the tor_hs_intro_rejected_intro_req_count metric was + * only incremented for reason HS_METRICS_ERR_INTRO_REQ_BAD_AUTH_KEY. */ + for (size_t i = 0; i < hs_metrics_intro_req_error_reasons_size; ++i) { + const char *reason = hs_metrics_intro_req_error_reasons[i]; + + if (!strcmp(reason, HS_METRICS_ERR_INTRO_REQ_BAD_AUTH_KEY)) { + continue; + } + + entry = metrics_store_find_entry_with_label( + entries, + metrics_format_label("reason", reason)); + tt_assert(entry); + tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 0); + } + + entry = metrics_store_find_entry_with_label( + entries, + metrics_format_label("reason", HS_METRICS_ERR_INTRO_REQ_BAD_AUTH_KEY)); + tt_assert(entry); + tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 1); + /* Set an IP object now for this circuit. */ { ip = helper_create_service_ip(); @@ -1243,6 +1276,33 @@ test_bad_introduce2(void *arg) tt_int_op(ret, OP_EQ, -1); tt_u64_op(ip->introduce2_count, OP_EQ, 0); + /* Make sure the tor_hs_intro_rejected_intro_req_count metric was incremented + * a second time, this time, with reason="invalid_introduce2_cell". */ + entry = metrics_store_find_entry_with_label( + entries, + metrics_format_label("reason", HS_METRICS_ERR_INTRO_REQ_INTRODUCE2)); + tt_assert(entry); + tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 1); + + /* The metric entries with other reason labels are unaffected */ + entry = metrics_store_find_entry_with_label( + entries, + metrics_format_label("reason", HS_METRICS_ERR_INTRO_REQ_SUBCREDENTIAL)); + tt_assert(entry); + tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 0); + + entry = metrics_store_find_entry_with_label( + entries, metrics_format_label( + "reason", HS_METRICS_ERR_INTRO_REQ_INTRODUCE2_REPLAY)); + tt_assert(entry); + tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 0); + + entry = metrics_store_find_entry_with_label( + entries, + metrics_format_label("reason", HS_METRICS_ERR_INTRO_REQ_BAD_AUTH_KEY)); + tt_assert(entry); + tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 1); + done: or_state_free(dummy_state); dummy_state = NULL; @@ -2219,12 +2279,16 @@ mock_build_state_get_exit_node(cpath_build_state_t *state) static void mock_launch_rendezvous_point_circuit(const hs_service_t *service, - const hs_service_intro_point_t *ip, - const hs_cell_introduce2_data_t *data) + const ed25519_public_key_t *ip_auth_pubkey, + const curve25519_keypair_t *ip_enc_key_kp, + const hs_cell_intro_rdv_data_t *rdv_data, + time_t now) { (void) service; - (void) ip; - (void) data; + (void) ip_auth_pubkey; + (void) ip_enc_key_kp; + (void) rdv_data; + (void) now; return; } @@ -2276,6 +2340,8 @@ test_intro2_handling(void *arg) /* Disable onionbalance */ x_service.config.ob_master_pubkeys = NULL; x_service.state.replay_cache_rend_cookie = replaycache_new(0,0); + /* Initialize the metrics store */ + hs_metrics_service_init(&x_service); /* Create subcredential for x: */ ed25519_keypair_t x_identity_keypair; @@ -2344,7 +2410,7 @@ test_intro2_handling(void *arg) /* Create INTRODUCE1 */ tt_assert(fast_mem_is_zero(relay_payload, sizeof(relay_payload))); retval = hs_circ_send_introduce1(intro_circ, &rend_circ, - alice_ip, &x_subcred); + alice_ip, &x_subcred, NULL); /* Check that the payload was written successfully */ tt_int_op(retval, OP_EQ, 0); @@ -2385,7 +2451,7 @@ test_intro2_handling(void *arg) /* Create INTRODUCE1 from Alice to X through Z */ memset(relay_payload, 0, sizeof(relay_payload)); retval = hs_circ_send_introduce1(intro_circ, &rend_circ, - alice_ip, &z_subcred); + alice_ip, &z_subcred, NULL); /* Check that the payload was written successfully */ tt_int_op(retval, OP_EQ, 0); @@ -2422,7 +2488,7 @@ test_intro2_handling(void *arg) /* Create INTRODUCE1 from Alice to X using X's subcred. */ memset(relay_payload, 0, sizeof(relay_payload)); retval = hs_circ_send_introduce1(intro_circ, &rend_circ, - alice_ip, &x_subcred); + alice_ip, &x_subcred, NULL); /* Check that the payload was written successfully */ tt_int_op(retval, OP_EQ, 0); @@ -2439,6 +2505,20 @@ test_intro2_handling(void *arg) (uint8_t*)relay_payload, relay_payload_len); tt_int_op(retval, OP_EQ, 0); + /* We haven't encountered any errors yet, so all the introduction request + * error metrics should be 0 */ + const smartlist_t *entries = metrics_store_get_all( + x_service.metrics.store, "tor_hs_intro_rejected_intro_req_count"); + const metrics_store_entry_t *entry = NULL; + + for (size_t i = 0; i < hs_metrics_intro_req_error_reasons_size; ++i) { + entry = metrics_store_find_entry_with_label( + entries, + metrics_format_label("reason", hs_metrics_intro_req_error_reasons[i])); + tt_assert(entry); + tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 0); + } + /* ************************************************************ */ /* Act IV: @@ -2456,6 +2536,11 @@ test_intro2_handling(void *arg) tt_int_op(retval, OP_EQ, -1); expect_log_msg_containing("with the same ENCRYPTED section"); teardown_capture_of_logs(); + entry = metrics_store_find_entry_with_label( + entries, + metrics_format_label("reason", HS_METRICS_ERR_INTRO_REQ_INTRODUCE2)); + tt_assert(entry); + tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 1); /* Now cleanup the intro point replay cache but not the service replay cache and see that this one triggers this time. */ @@ -2470,6 +2555,12 @@ test_intro2_handling(void *arg) expect_log_msg_containing("with same REND_COOKIE"); teardown_capture_of_logs(); + entry = metrics_store_find_entry_with_label( + entries, metrics_format_label( + "reason", HS_METRICS_ERR_INTRO_REQ_INTRODUCE2_REPLAY)); + tt_assert(entry); + tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 1); + /* Now just to make sure cleanup both replay caches and make sure that the cell gets through */ replaycache_free(x_ip->replay_cache); @@ -2482,12 +2573,15 @@ test_intro2_handling(void *arg) (uint8_t*)relay_payload, relay_payload_len); tt_int_op(retval, OP_EQ, 0); + /* This time, the error metric was *not* incremented */ + tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 1); + /* As a final thing, create an INTRODUCE1 cell from Alice to X using Y's * subcred (should fail since Y is just another instance and not the frontend * service!) */ memset(relay_payload, 0, sizeof(relay_payload)); retval = hs_circ_send_introduce1(intro_circ, &rend_circ, - alice_ip, &y_subcred); + alice_ip, &y_subcred, NULL); tt_int_op(retval, OP_EQ, 0); /* Check that the payload was written successfully */ @@ -2498,7 +2592,13 @@ test_intro2_handling(void *arg) intro_circ, x_ip, &y_subcred, (uint8_t*)relay_payload, relay_payload_len); + tt_int_op(retval, OP_EQ, -1); + entry = metrics_store_find_entry_with_label( + entries, + metrics_format_label("reason", HS_METRICS_ERR_INTRO_REQ_INTRODUCE2)); + tt_assert(entry); + tt_int_op(metrics_store_entry_get_value(entry), OP_EQ, 2); done: /* Start cleaning up X */ @@ -2507,6 +2607,7 @@ test_intro2_handling(void *arg) tor_free(x_service.state.ob_subcreds); service_descriptor_free(x_service.desc_current); service_descriptor_free(x_service.desc_next); + hs_metrics_service_free(&x_service); service_intro_point_free(x_ip); /* Clean up Alice */ diff --git a/src/test/test_metrics.c b/src/test/test_metrics.c index ba1a763f0c..0bf072dbfc 100644 --- a/src/test/test_metrics.c +++ b/src/test/test_metrics.c @@ -28,11 +28,16 @@ #include "lib/encoding/confline.h" #include "lib/metrics/metrics_store.h" +#include <limits.h> + #define TEST_METRICS_ENTRY_NAME "entryA" #define TEST_METRICS_ENTRY_HELP "Description of entryA" #define TEST_METRICS_ENTRY_LABEL_1 "label=\"farfadet\"" #define TEST_METRICS_ENTRY_LABEL_2 "label=\"ponki\"" +#define TEST_METRICS_HIST_ENTRY_NAME "test_hist_entry" +#define TEST_METRICS_HIST_ENTRY_HELP "Description of test_hist_entry" + static void set_metrics_port(or_options_t *options) { @@ -189,7 +194,8 @@ test_prometheus(void *arg) /* Add entry and validate its content. */ entry = metrics_store_add(store, METRICS_TYPE_COUNTER, TEST_METRICS_ENTRY_NAME, - TEST_METRICS_ENTRY_HELP); + TEST_METRICS_ENTRY_HELP, + 0, NULL); tt_assert(entry); metrics_store_entry_add_label(entry, TEST_METRICS_ENTRY_LABEL_1); @@ -209,10 +215,60 @@ test_prometheus(void *arg) } static void +test_prometheus_histogram(void *arg) +{ + metrics_store_t *store = NULL; + metrics_store_entry_t *entry = NULL; + buf_t *buf = buf_new(); + char *output = NULL; + const int64_t buckets[] = { 10, 20, 3000 }; + + (void) arg; + + /* Fresh new store. No entries. */ + store = metrics_store_new(); + tt_assert(store); + + /* Add a histogram entry and validate its content. */ + entry = metrics_store_add(store, METRICS_TYPE_HISTOGRAM, + TEST_METRICS_HIST_ENTRY_NAME, + TEST_METRICS_HIST_ENTRY_HELP, + ARRAY_LENGTH(buckets), buckets); + tt_assert(entry); + metrics_store_entry_add_label(entry, TEST_METRICS_ENTRY_LABEL_1); + + static const char *expected = + "# HELP " TEST_METRICS_HIST_ENTRY_NAME " " + TEST_METRICS_HIST_ENTRY_HELP "\n" + "# TYPE " TEST_METRICS_HIST_ENTRY_NAME " histogram\n" + TEST_METRICS_HIST_ENTRY_NAME "_bucket{" + TEST_METRICS_ENTRY_LABEL_1 ",le=\"10.00\"} 0\n" + TEST_METRICS_HIST_ENTRY_NAME "_bucket{" + TEST_METRICS_ENTRY_LABEL_1 ",le=\"20.00\"} 0\n" + TEST_METRICS_HIST_ENTRY_NAME "_bucket{" + TEST_METRICS_ENTRY_LABEL_1 ",le=\"3000.00\"} 0\n" + TEST_METRICS_HIST_ENTRY_NAME "_bucket{" + TEST_METRICS_ENTRY_LABEL_1 ",le=\"+Inf\"} 0\n" + TEST_METRICS_HIST_ENTRY_NAME "_sum{" TEST_METRICS_ENTRY_LABEL_1 "} 0\n" + TEST_METRICS_HIST_ENTRY_NAME "_count{" TEST_METRICS_ENTRY_LABEL_1 "} 0\n"; + + metrics_store_get_output(METRICS_FORMAT_PROMETHEUS, store, buf); + output = buf_extract(buf, NULL); + tt_str_op(expected, OP_EQ, output); + + done: + buf_free(buf); + tor_free(output); + metrics_store_free(store); +} + +static void test_store(void *arg) { metrics_store_t *store = NULL; metrics_store_entry_t *entry = NULL; + const int64_t buckets[] = { 10, 20, 3000 }; + const size_t bucket_count = ARRAY_LENGTH(buckets); (void) arg; @@ -224,7 +280,7 @@ test_store(void *arg) /* Add entry and validate its content. */ entry = metrics_store_add(store, METRICS_TYPE_COUNTER, TEST_METRICS_ENTRY_NAME, - TEST_METRICS_ENTRY_HELP); + TEST_METRICS_ENTRY_HELP, 0, NULL); tt_assert(entry); tt_int_op(entry->type, OP_EQ, METRICS_TYPE_COUNTER); tt_str_op(entry->name, OP_EQ, TEST_METRICS_ENTRY_NAME); @@ -251,7 +307,7 @@ test_store(void *arg) /* Add entry and validate its content. */ entry = metrics_store_add(store, METRICS_TYPE_COUNTER, TEST_METRICS_ENTRY_NAME, - TEST_METRICS_ENTRY_HELP); + TEST_METRICS_ENTRY_HELP, 0, NULL); tt_assert(entry); metrics_store_entry_add_label(entry, TEST_METRICS_ENTRY_LABEL_2); @@ -261,6 +317,89 @@ test_store(void *arg) tt_assert(entries); tt_int_op(smartlist_len(entries), OP_EQ, 2); + /* Add a histogram entry and validate its content. */ + entry = metrics_store_add(store, METRICS_TYPE_HISTOGRAM, + TEST_METRICS_HIST_ENTRY_NAME, + TEST_METRICS_HIST_ENTRY_HELP, + bucket_count, buckets); + + tt_assert(entry); + tt_int_op(entry->type, OP_EQ, METRICS_TYPE_HISTOGRAM); + tt_str_op(entry->name, OP_EQ, TEST_METRICS_HIST_ENTRY_NAME); + tt_str_op(entry->help, OP_EQ, TEST_METRICS_HIST_ENTRY_HELP); + tt_uint_op(entry->u.histogram.bucket_count, OP_EQ, bucket_count); + + for (size_t i = 0; i < bucket_count; ++i) { + tt_uint_op(entry->u.histogram.buckets[i].bucket, OP_EQ, buckets[i]); + tt_uint_op(entry->u.histogram.buckets[i].value, OP_EQ, 0); + } + + /* Access the entry. */ + tt_assert(metrics_store_get_all(store, TEST_METRICS_HIST_ENTRY_NAME)); + + /* Record various observations. */ + metrics_store_hist_entry_update(entry, 3, 11); + tt_int_op(metrics_store_hist_entry_get_value(entry, 10), OP_EQ, 0); + tt_int_op(metrics_store_hist_entry_get_value(entry, 20), OP_EQ, 3); + tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 3); + tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 3); + tt_int_op(metrics_store_hist_entry_get_count(entry), OP_EQ, 3); + tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, 11); + + metrics_store_hist_entry_update(entry, 1, 42); + tt_int_op(metrics_store_hist_entry_get_value(entry, 10), OP_EQ, 0); + tt_int_op(metrics_store_hist_entry_get_value(entry, 20), OP_EQ, 3); + tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 4); + tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 4); + tt_int_op(metrics_store_hist_entry_get_count(entry), OP_EQ, 4); + tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, 53); + + /* Ensure this resets all buckets back to 0. */ + metrics_store_entry_reset(entry); + for (size_t i = 0; i < bucket_count; ++i) { + tt_uint_op(entry->u.histogram.buckets[i].bucket, OP_EQ, buckets[i]); + tt_uint_op(entry->u.histogram.buckets[i].value, OP_EQ, 0); + } + + /* tt_int_op assigns the third argument to a variable of type long, which + * overflows on some platforms (e.g. on some 32-bit systems). We disable + * these checks for those platforms. */ +#if LONG_MAX >= INT64_MAX + metrics_store_hist_entry_update(entry, 1, INT64_MAX - 13); + tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, INT64_MAX - 13); + metrics_store_hist_entry_update(entry, 1, 13); + tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, INT64_MAX); + /* Uh-oh, the sum of all observations is now greater than INT64_MAX. Make + * sure we reset the entry instead of overflowing the sum. */ + metrics_store_hist_entry_update(entry, 1, 1); + tt_int_op(metrics_store_hist_entry_get_value(entry, 10), OP_EQ, 1); + tt_int_op(metrics_store_hist_entry_get_value(entry, 20), OP_EQ, 1); + tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 1); + tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 1); + tt_int_op(metrics_store_hist_entry_get_count(entry), OP_EQ, 1); + tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, 1); +#endif + +#if LONG_MIN <= INT64_MIN + metrics_store_entry_reset(entry); + /* In practice, we're not going to have negative observations (as we only use + * histograms for timings, which are always positive), but technically + * prometheus _does_ support negative observations. */ + metrics_store_hist_entry_update(entry, 1, INT64_MIN + 13); + tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, INT64_MIN + 13); + metrics_store_hist_entry_update(entry, 1, -13); + tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, INT64_MIN); + /* Uh-oh, the sum of all observations is now less than INT64_MIN. Make + * sure we reset the entry instead of underflowing the sum. */ + metrics_store_hist_entry_update(entry, 1, -1); + tt_int_op(metrics_store_hist_entry_get_value(entry, 10), OP_EQ, 1); + tt_int_op(metrics_store_hist_entry_get_value(entry, 20), OP_EQ, 1); + tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 1); + tt_int_op(metrics_store_hist_entry_get_value(entry, 3000), OP_EQ, 1); + tt_int_op(metrics_store_hist_entry_get_count(entry), OP_EQ, 1); + tt_int_op(metrics_store_hist_entry_get_sum(entry), OP_EQ, -1); +#endif + done: metrics_store_free(store); } @@ -270,6 +409,7 @@ struct testcase_t metrics_tests[] = { { "config", test_config, TT_FORK, NULL, NULL }, { "connection", test_connection, TT_FORK, NULL, NULL }, { "prometheus", test_prometheus, TT_FORK, NULL, NULL }, + { "prometheus_histogram", test_prometheus_histogram, TT_FORK, NULL, NULL }, { "store", test_store, TT_FORK, NULL, NULL }, END_OF_TESTCASES diff --git a/src/test/test_nodelist.c b/src/test/test_nodelist.c index 250db9a964..ecd29f5464 100644 --- a/src/test/test_nodelist.c +++ b/src/test/test_nodelist.c @@ -1273,7 +1273,6 @@ test_nodelist_routerstatus_has_visibly_changed(void *arg) memcpy(rs_orig.descriptor_digest, "abcdefghijklmnopqrst", 20); tor_addr_from_ipv4h(&rs_orig.ipv4_addr, 0x7f000001); rs_orig.ipv4_orport = 3; - rs_orig.published_on = time(NULL); rs_orig.has_bandwidth = 1; rs_orig.bandwidth_kb = 20; @@ -1284,9 +1283,9 @@ test_nodelist_routerstatus_has_visibly_changed(void *arg) tor_free(fmt); \ fmt_orig = routerstatus_format_entry(&rs_orig, NULL, NULL, \ NS_CONTROL_PORT, \ - NULL); \ + NULL, -1); \ fmt = routerstatus_format_entry(&rs, NULL, NULL, NS_CONTROL_PORT, \ - NULL); \ + NULL, -1); \ tt_assert(fmt_orig); \ tt_assert(fmt); \ STMT_END @@ -1322,9 +1321,6 @@ test_nodelist_routerstatus_has_visibly_changed(void *arg) strlcpy(rs.nickname, "fr1end1y", sizeof(rs.nickname)); ASSERT_CHANGED(); - rs.published_on += 3600; - ASSERT_CHANGED(); - rs.ipv4_orport = 55; ASSERT_CHANGED(); diff --git a/src/test/test_ntor_v3.c b/src/test/test_ntor_v3.c index 1d06403076..0d51c684a0 100644 --- a/src/test/test_ntor_v3.c +++ b/src/test/test_ntor_v3.c @@ -14,6 +14,8 @@ #include "core/or/extend_info_st.h" #include "core/or/crypt_path_st.h" #define TOR_CONGESTION_CONTROL_PRIVATE +#define TOR_CONGESTION_CONTROL_COMMON_PRIVATE +#include "core/or/congestion_control_st.h" #include "core/or/congestion_control_common.h" #include "app/config/config.h" @@ -262,6 +264,7 @@ test_ntor3_handshake(void *arg) tt_int_op(serv_params.cc_enabled, OP_EQ, 0); /* client off, serv on -> off */ + congestion_control_set_cc_disabled(); serv_ns_params.cc_enabled = 1; run_full_handshake(&serv_ns_params, &client_params, &serv_params); tt_int_op(client_params.cc_enabled, OP_EQ, 0); diff --git a/src/test/test_options.c b/src/test/test_options.c index 182e6dd572..6610e317a7 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -2006,43 +2006,6 @@ test_options_validate__testing(void *ignored) } static void -test_options_validate__hidserv(void *ignored) -{ - (void)ignored; - int ret; - char *msg; - setup_capture_of_logs(LOG_WARN); - - options_test_data_t *tdata = NULL; - - free_options_test_data(tdata); - tdata = get_options_test_data("RendPostPeriod 1\n" ); - mock_clean_saved_logs(); - ret = options_validate(NULL, tdata->opt, &msg); - tt_int_op(ret, OP_EQ, 0); - expect_log_msg("RendPostPeriod option is too short;" - " raising to 600 seconds.\n"); - tt_int_op(tdata->opt->RendPostPeriod, OP_EQ, 600); - tor_free(msg); - - free_options_test_data(tdata); - tdata = get_options_test_data("RendPostPeriod 302401\n" ); - mock_clean_saved_logs(); - ret = options_validate(NULL, tdata->opt, &msg); - tt_int_op(ret, OP_EQ, 0); - expect_log_msg("RendPostPeriod is too large; " - "clipping to 302400s.\n"); - tt_int_op(tdata->opt->RendPostPeriod, OP_EQ, 302400); - tor_free(msg); - - done: - teardown_capture_of_logs(); - policies_free_all(); - free_options_test_data(tdata); - tor_free(msg); -} - -static void test_options_validate__path_bias(void *ignored) { (void)ignored; @@ -4270,7 +4233,6 @@ struct testcase_t options_tests[] = { LOCAL_VALIDATE_TEST(safe_logging), LOCAL_VALIDATE_TEST(publish_server_descriptor), LOCAL_VALIDATE_TEST(testing), - LOCAL_VALIDATE_TEST(hidserv), LOCAL_VALIDATE_TEST(path_bias), LOCAL_VALIDATE_TEST(bandwidth), LOCAL_VALIDATE_TEST(circuits), diff --git a/src/test/test_parsecommon.c b/src/test/test_parsecommon.c index b32840264e..866f89a4e1 100644 --- a/src/test/test_parsecommon.c +++ b/src/test/test_parsecommon.c @@ -254,6 +254,50 @@ test_parsecommon_get_next_token_success(void *arg) } static void +test_parsecommon_get_next_token_carriage_return(void *arg) +{ + memarea_t *area = memarea_new(); + smartlist_t *tokens = smartlist_new(); + + (void)arg; + + token_rule_t table[] = { + T01("uptime", K_UPTIME, GE(1), NO_OBJ), + T01("hibernating", K_HIBERNATING, GE(1), NO_OBJ), + END_OF_TABLE, + }; + + char *str = tor_strdup( + "hibernating 0\r\nuptime 1024\n" + "hibernating 0\ruptime 1024\n"); + + int retval = + tokenize_string(area, str, NULL, + tokens, table, 0); + + tt_int_op(smartlist_len(tokens), OP_EQ, 3); + directory_token_t *token = smartlist_get(tokens, 0); + + tt_int_op(token->tp, OP_EQ, K_HIBERNATING); + + token = smartlist_get(tokens, 1); + + tt_int_op(token->tp, OP_EQ, K_UPTIME); + + token = smartlist_get(tokens, 2); + + tt_int_op(token->tp, OP_EQ, K_HIBERNATING); + + tt_int_op(retval, OP_EQ, -1); + + done: + tor_free(str); + memarea_drop_all(area); + smartlist_free(tokens); + return; +} + +static void test_parsecommon_get_next_token_concat_args(void *arg) { memarea_t *area = memarea_new(); @@ -571,6 +615,7 @@ test_parsecommon_get_next_token_err_bad_base64(void *arg) struct testcase_t parsecommon_tests[] = { PARSECOMMON_TEST(tokenize_string_null), + PARSECOMMON_TEST(get_next_token_carriage_return), PARSECOMMON_TEST(tokenize_string_multiple_lines), PARSECOMMON_TEST(tokenize_string_min_cnt), PARSECOMMON_TEST(tokenize_string_max_cnt), diff --git a/src/test/test_parseconf.sh b/src/test/test_parseconf.sh index c02b8b23c0..85a8cbbf0c 100755 --- a/src/test/test_parseconf.sh +++ b/src/test/test_parseconf.sh @@ -98,6 +98,9 @@ # want to encode that knowledge in this test script, so we supply a # separate result file for every combination of disabled modules that # has a different result.) +# +# This logic ignores modules that are not listed by --list-modules +# (dircache) and some that do not currently affect config parsing (pow). umask 077 set -e @@ -197,6 +200,8 @@ echo "This pattern should not match any log messages" \ "$NON_EMPTY" STANDARD_LIBS="libevent\\|openssl\\|zlib" +MODULES_WITHOUT_CONFIG_TESTS="dircache\\|pow" + # Lib names are restricted to [a-z0-9]* at the moment # We don't actually want to support foreign accents here # shellcheck disable=SC2018,SC2019 @@ -229,6 +234,7 @@ TOR_LIBS_ENABLED_SEARCH="$(echo "$TOR_LIBS_ENABLED_SEARCH" | tr ' ' '\n' \ | grep -v '^_*$' | tr '\n' ' ')" TOR_MODULES_DISABLED="$("$TOR_BINARY" --list-modules | grep ': no' \ + | grep -v "$MODULES_WITHOUT_CONFIG_TESTS" \ | cut -d ':' -f1 | sort | tr '\n' '_')" # Remove the last underscore, if there is one TOR_MODULES_DISABLED=${TOR_MODULES_DISABLED%_} diff --git a/src/test/test_periodic_event.c b/src/test/test_periodic_event.c index 6a9569ae89..58565f6af1 100644 --- a/src/test/test_periodic_event.c +++ b/src/test/test_periodic_event.c @@ -50,7 +50,7 @@ test_pe_initialize(void *arg) /* Initialize the events but the callback won't get called since we would * need to run the main loop and then wait for a second delaying the unit - * tests. Instead, we'll test the callback work indepedently elsewhere. */ + * tests. Instead, we'll test the callback work independently elsewhere. */ initialize_periodic_events(); periodic_events_connect_all(); set_network_participation(false); diff --git a/src/test/test_process_descs.c b/src/test/test_process_descs.c index abcb6ae2fe..c13e8b58c4 100644 --- a/src/test/test_process_descs.c +++ b/src/test/test_process_descs.c @@ -47,17 +47,15 @@ test_process_descs_versions(void *arg) { "Tor 0.4.3.0-alpha-dev", true }, { "Tor 0.4.3.8", true }, { "Tor 0.4.4.9", true }, - - /* The 0.4.5.x series stable is supported. */ { "Tor 0.4.5.5-rc", true }, - { "Tor 0.4.5.6", false }, - { "Tor 0.4.5.15", false }, - + { "Tor 0.4.5.6", true }, + { "Tor 0.4.5.15", true }, { "Tor 0.4.6.0-alpha-dev", true }, { "Tor 0.4.6.1-alpha", true }, { "Tor 0.4.6.5", true }, { "Tor 0.4.6.50", true }, /* Non existing one in the 0.4.6 series */ + /* The 0.4.7.x series is supported. */ { "Tor 0.4.7.0-alpha-dev", false }, { "Tor 0.4.7.3-alpha", false }, { "Tor 0.4.7.12", false }, diff --git a/src/test/test_rebind.sh b/src/test/test_rebind.sh index aae2a9a6a0..d87e008f9a 100755 --- a/src/test/test_rebind.sh +++ b/src/test/test_rebind.sh @@ -51,6 +51,7 @@ tmpdir= # shellcheck disable=SC2317 clean () { if [ -n "$tmpdir" ] && [ -d "$tmpdir" ]; then + ls -l "$tmpdir" rm -rf "$tmpdir" fi } diff --git a/src/test/test_router.c b/src/test/test_router.c index 15cc93fbfc..47084bba01 100644 --- a/src/test/test_router.c +++ b/src/test/test_router.c @@ -282,7 +282,6 @@ test_router_mark_if_too_old(void *arg) 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; @@ -302,25 +301,14 @@ test_router_mark_if_too_old(void *arg) 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. + // Version in consensus marked as stale? We'll mark it. 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); diff --git a/src/test/test_sandbox.c b/src/test/test_sandbox.c index 7ec08a3546..64182ecc91 100644 --- a/src/test/test_sandbox.c +++ b/src/test/test_sandbox.c @@ -12,6 +12,8 @@ #include "orconfig.h" #include "lib/sandbox/sandbox.h" +#include "lib/crypt_ops/crypto_rand.h" +#include "ext/equix/include/equix.h" #ifdef USE_LIBSECCOMP @@ -292,6 +294,58 @@ test_sandbox_stat_filename(void *arg) (void)0; } +/** This is a simplified subset of test_crypto_equix(), running one solve + * and one verify from inside the sandbox. The sandbox restricts mprotect, and + * hashx will experience a failure at runtime which this test case exercises. + * The result of the solve and verify should both still be correct, since we + * expect it to cleanly fall back on an interpreted implementation which has + * no operating system dependencies. */ +static void +test_sandbox_crypto_equix(void *arg) +{ + (void)arg; + + const char *challenge_literal = "abce"; + const size_t challenge_len = strlen(challenge_literal); + const size_t num_sols = 4; + static const equix_solution sols_expected[EQUIX_MAX_SOLS] = { + {{ 0x4fca, 0x72eb, 0x101f, 0xafab, 0x1add, 0x2d71, 0x75a3, 0xc978 }}, + {{ 0x17f1, 0x7aa6, 0x23e3, 0xab00, 0x7e2f, 0x917e, 0x16da, 0xda9e }}, + {{ 0x70ee, 0x7757, 0x8a54, 0xbd2b, 0x90e4, 0xe31e, 0x2085, 0xe47e }}, + {{ 0x62c5, 0x86d1, 0x5752, 0xe1f0, 0x12da, 0x8f33, 0x7336, 0xf161 }}, + }; + + equix_solutions_buffer output; + equix_ctx *solve_ctx = NULL, *verify_ctx = NULL; + + solve_ctx = equix_alloc(EQUIX_CTX_SOLVE | EQUIX_CTX_TRY_COMPILE); + tt_ptr_op(solve_ctx, OP_NE, NULL); + + equix_result result; + memset(&output, 0xEE, sizeof output); + result = equix_solve(solve_ctx, challenge_literal, challenge_len, &output); + tt_int_op(result, OP_EQ, EQUIX_OK); + tt_int_op(output.count, OP_EQ, num_sols); + tt_int_op(output.flags, OP_EQ, 0); /* EQUIX_SOLVER_DID_USE_COMPILER unset */ + tt_mem_op(output.sols, OP_EQ, sols_expected, + num_sols * sizeof(equix_solution)); + + verify_ctx = equix_alloc(EQUIX_CTX_VERIFY | EQUIX_CTX_TRY_COMPILE); + tt_ptr_op(verify_ctx, OP_NE, NULL); + + /* Test one of the solutions randomly */ + const unsigned sol_i = crypto_rand_int(num_sols); + equix_solution *sol = &output.sols[sol_i]; + + result = equix_verify(verify_ctx, challenge_literal, + challenge_len, sol); + tt_int_op(EQUIX_OK, OP_EQ, result); + + done: + equix_free(solve_ctx); + equix_free(verify_ctx); +} + #define SANDBOX_TEST_SKIPPED(name) \ { #name, test_sandbox_ ## name, TT_SKIP, NULL, NULL } @@ -343,6 +397,8 @@ struct testcase_t sandbox_tests[] = { #else SANDBOX_TEST_SKIPPED(stat_filename), #endif + + SANDBOX_TEST_IN_SANDBOX(crypto_equix), END_OF_TESTCASES }; diff --git a/src/test/test_scheduler.c b/src/test/test_scheduler.c index 7e41a4be25..06b3dafce8 100644 --- a/src/test/test_scheduler.c +++ b/src/test/test_scheduler.c @@ -81,8 +81,7 @@ mock_vanilla_networkstatus_get_param( (void)default_val; (void)min_val; (void)max_val; - // only support KISTSchedRunInterval right now - tor_assert(strcmp(param_name, "KISTSchedRunInterval")==0); + (void)param_name; return 0; } @@ -95,8 +94,7 @@ mock_kist_networkstatus_get_param( (void)default_val; (void)min_val; (void)max_val; - // only support KISTSchedRunInterval right now - tor_assert(strcmp(param_name, "KISTSchedRunInterval")==0); + (void)param_name; return 12; } @@ -863,7 +861,7 @@ test_scheduler_initfree(void *arg) /* We have specified nothing in the torrc and there's no consensus so the * KIST scheduler is what should be in use */ tt_ptr_op(the_scheduler, OP_EQ, get_kist_scheduler()); - tt_int_op(sched_run_interval, OP_EQ, 10); + tt_int_op(sched_run_interval, OP_EQ, KIST_SCHED_RUN_INTERVAL_DEFAULT); scheduler_free_all(); @@ -906,7 +904,7 @@ test_scheduler_can_use_kist(void *arg) #else /* HAVE_KIST_SUPPORT */ tt_int_op(res_should, OP_EQ, 0); #endif /* HAVE_KIST_SUPPORT */ - tt_int_op(res_freq, OP_EQ, 10); + tt_int_op(res_freq, OP_EQ, KIST_SCHED_RUN_INTERVAL_DEFAULT); /* Test defer to consensus, and kist consensus available */ MOCK(networkstatus_get_param, mock_kist_networkstatus_get_param); diff --git a/src/test/test_slow.c b/src/test/test_slow.c index 5f42b43103..08366416ca 100644 --- a/src/test/test_slow.c +++ b/src/test/test_slow.c @@ -21,6 +21,7 @@ struct testgroup_t testgroups[] = { { "slow/crypto/", slow_crypto_tests }, { "slow/process/", slow_process_tests }, + { "slow/hs_pow/", slow_hs_pow_tests }, { "slow/prob_distr/", slow_stochastic_prob_distr_tests }, { "slow/ptr/", slow_ptr_tests }, END_OF_GROUPS diff --git a/src/test/test_util.c b/src/test/test_util.c index 1dae2c617e..391c3d07c1 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -2945,7 +2945,7 @@ test_util_gzip_compression_bomb(void *arg) tt_int_op(-1, OP_EQ, tor_compress(&result, &result_len, one_mb, one_million, ZLIB_METHOD)); - expect_single_log_msg_containing( + expect_log_msg_containing( "We compressed something and got an insanely high " "compression factor; other Tors would think this " "was a compression bomb."); diff --git a/src/test/test_voting_flags.c b/src/test/test_voting_flags.c index 457b0fa796..a5b1248cc1 100644 --- a/src/test/test_voting_flags.c +++ b/src/test/test_voting_flags.c @@ -40,7 +40,6 @@ setup_cfg(flag_vote_test_cfg_t *c) memset(c->ri.cache_info.signed_descriptor_digest, 0xee, DIGEST_LEN); c->ri.cache_info.published_on = c->now - 100; - c->expected.published_on = c->now - 100; tor_addr_from_ipv4h(&c->ri.ipv4_addr, 0x7f010105); tor_addr_from_ipv4h(&c->expected.ipv4_addr, 0x7f010105); @@ -65,7 +64,6 @@ check_result(flag_vote_test_cfg_t *c) dirauth_set_routerstatus_from_routerinfo(&rs, &c->node, &c->ri, c->now, 0, 0); - tt_i64_op(rs.published_on, OP_EQ, c->expected.published_on); tt_str_op(rs.nickname, OP_EQ, c->expected.nickname); // identity_digest and descriptor_digest are not set here. @@ -144,13 +142,11 @@ test_voting_flags_staledesc(void *arg) time_t now = cfg->now; cfg->ri.cache_info.published_on = now - DESC_IS_STALE_INTERVAL + 10; - cfg->expected.published_on = now - DESC_IS_STALE_INTERVAL + 10; // no change in expectations for is_staledesc if (!check_result(cfg)) goto done; cfg->ri.cache_info.published_on = now - DESC_IS_STALE_INTERVAL - 10; - cfg->expected.published_on = now - DESC_IS_STALE_INTERVAL - 10; cfg->expected.is_staledesc = 1; if (!check_result(cfg)) goto done; diff --git a/src/test/testing_common.c b/src/test/testing_common.c index 2fd424c07e..88d04e6082 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -244,14 +244,18 @@ void tinytest_postfork(void); void tinytest_prefork(void) { +#ifdef ENABLE_NSS free_pregenerated_keys(); +#endif subsystems_prefork(); } void tinytest_postfork(void) { subsystems_postfork(); +#ifdef ENABLE_NSS init_pregenerated_keys(); +#endif } static void |