diff options
-rw-r--r-- | changes/bug23499 | 6 | ||||
-rw-r--r-- | changes/bug23524 | 4 | ||||
-rw-r--r-- | changes/bug23525 | 6 | ||||
-rw-r--r-- | changes/prop224 | 36 | ||||
-rw-r--r-- | doc/HACKING/GettingStarted.md | 5 | ||||
-rw-r--r-- | src/common/util.c | 6 | ||||
-rw-r--r-- | src/or/bridges.c | 9 | ||||
-rw-r--r-- | src/or/control.c | 2 | ||||
-rw-r--r-- | src/or/directory.c | 112 | ||||
-rw-r--r-- | src/or/hs_client.c | 11 | ||||
-rw-r--r-- | src/or/hs_client.h | 8 | ||||
-rw-r--r-- | src/or/main.c | 1 | ||||
-rw-r--r-- | src/test/hs_indexes.py | 70 | ||||
-rw-r--r-- | src/test/hs_test_helpers.c | 31 | ||||
-rw-r--r-- | src/test/include.am | 2 | ||||
-rw-r--r-- | src/test/test_hs_client.c | 180 | ||||
-rw-r--r-- | src/test/test_hs_common.c | 166 | ||||
-rwxr-xr-x | src/test/test_key_expiration.sh | 2 |
18 files changed, 552 insertions, 105 deletions
diff --git a/changes/bug23499 b/changes/bug23499 new file mode 100644 index 0000000000..e53b03c34e --- /dev/null +++ b/changes/bug23499 @@ -0,0 +1,6 @@ + o Minor bugfixes: + - Directory servers now include a "Date:" http header for response + codes other than 200. Clients starting with a skewed clock and a + recent consensus were getting "304 Not modified" responses from + directory authorities, so without a Date header the client would + never hear about a wrong clock. Fixes bug 23499; bugfix on 0.0.8rc1. diff --git a/changes/bug23524 b/changes/bug23524 new file mode 100644 index 0000000000..c8ece52930 --- /dev/null +++ b/changes/bug23524 @@ -0,0 +1,4 @@ + o Minor bugfixes (DoS-resistance): + - If future code asks if there are any running bridges, without checking + if bridges are enabled, log a BUG warning rather than crashing. + Fixes 23524 on 0.3.0.1-alpha. diff --git a/changes/bug23525 b/changes/bug23525 new file mode 100644 index 0000000000..3a9c766c3a --- /dev/null +++ b/changes/bug23525 @@ -0,0 +1,6 @@ + o Minor bugfixes (control port): + - Make download status next attempts reported over the control port + consistent with the time used by tor. This issue only occurs if a + download status has not been reset before it is queried over the + control port. + Fixes 23525, not in any released version of tor. diff --git a/changes/prop224 b/changes/prop224 new file mode 100644 index 0000000000..9401ff7835 --- /dev/null +++ b/changes/prop224 @@ -0,0 +1,36 @@ + o Major features (next-generation onion services): + - Tor now supports the next-generation onion services protocol for clients + and services! As part of this release, the core of proposal 224 has been + implemented and is available for experimentation and testing by our + users. This newer version of onion services (v3) features various + improvements over the legacy system: + a) Better crypto (replaced SHA1/DH/RSA1024 with SHA3/ed25519/curve25519) + b) Improved directory protocol leaking less to directory servers. + c) Improved directory protocol with smaller surface for targeted attacks. + d) Better onion address security against impersonation. + e) More extensible introduction/rendezvous protocol. + f) A cleaner and more modular codebase. + + Furthermore, as part of this update, onion addresses increase in length + and are now 56 characters long: + 4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad.onion + + In the future, we will be releasing more options and features but we + first need a testing period, so that the current codebase matures and + becomes more robust. Here are some of the features we have planned: + 1) Offline keys for onion services + 2) Advanced client authorization for onion services + 3) Improved guard algorithm for onion services + 4) Next-gen onion service statistics + + Please see our proposal for more details: + https://gitweb.torproject.org/torspec.git/tree/proposals/224-rend-spec-ng.txt + + The default version for onion services remains v2 (the legacy system) + until this new codebase gets tested and hardened. + + Service operators who want to experiment with the new system can use the + 'HiddenServiceVersion 3' torrc directive along with the regular onion + service configuration options. + + We will publish a blog post about this new feature soon! Enjoy! diff --git a/doc/HACKING/GettingStarted.md b/doc/HACKING/GettingStarted.md index 0295adc1ff..0c42404634 100644 --- a/doc/HACKING/GettingStarted.md +++ b/doc/HACKING/GettingStarted.md @@ -11,8 +11,9 @@ whole Tor ecosystem.) If you are looking for a more bare-bones, less user-friendly information -dump of important information, you might like reading doc/HACKING -instead. You should probably read it before you write your first patch. +dump of important information, you might like reading the "torguts" +documents linked to below. You should probably read it before you write +your first patch. Required background diff --git a/src/common/util.c b/src/common/util.c index 36d0f4d068..ac2fd6700f 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -477,7 +477,7 @@ round_to_power_of_2(uint64_t u64) /** Return the lowest x such that x is at least <b>number</b>, and x modulo * <b>divisor</b> == 0. If no such x can be expressed as an unsigned, return - * UINT_MAX */ + * UINT_MAX. Asserts if divisor is zero. */ unsigned round_to_next_multiple_of(unsigned number, unsigned divisor) { @@ -491,7 +491,7 @@ round_to_next_multiple_of(unsigned number, unsigned divisor) /** Return the lowest x such that x is at least <b>number</b>, and x modulo * <b>divisor</b> == 0. If no such x can be expressed as a uint32_t, return - * UINT32_MAX */ + * UINT32_MAX. Asserts if divisor is zero. */ uint32_t round_uint32_to_next_multiple_of(uint32_t number, uint32_t divisor) { @@ -506,7 +506,7 @@ round_uint32_to_next_multiple_of(uint32_t number, uint32_t divisor) /** Return the lowest x such that x is at least <b>number</b>, and x modulo * <b>divisor</b> == 0. If no such x can be expressed as a uint64_t, return - * UINT64_MAX */ + * UINT64_MAX. Asserts if divisor is zero. */ uint64_t round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor) { diff --git a/src/or/bridges.c b/src/or/bridges.c index 0d4549dd16..1eec4e39ec 100644 --- a/src/or/bridges.c +++ b/src/or/bridges.c @@ -455,8 +455,8 @@ bridge_add_from_config(bridge_line_t *bridge_line) b->fetch_status.schedule = DL_SCHED_BRIDGE; b->fetch_status.backoff = DL_SCHED_RANDOM_EXPONENTIAL; b->fetch_status.increment_on = DL_SCHED_INCREMENT_ATTEMPT; - /* This will fail if UseBridges is not set -- and it does. */ - // download_status_reset(&b->fetch_status); + /* We can't reset the bridge's download status here, because UseBridges + * might be 0 now, and it might be changed to 1 much later. */ b->socks_args = bridge_line->socks_args; if (!bridge_list) bridge_list = smartlist_new(); @@ -625,6 +625,7 @@ fetch_bridge_descriptors(const or_options_t *options, time_t now) SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) { + /* This resets the download status on first use */ if (!download_status_is_ready(&bridge->fetch_status, now, IMPOSSIBLE_TO_DOWNLOAD)) continue; /* don't bother, no need to retry yet */ @@ -835,7 +836,9 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache) MOCK_IMPL(int, any_bridge_descriptors_known, (void)) { - tor_assert(get_options()->UseBridges); + if (BUG(!get_options()->UseBridges)) { + return 0; + } if (!bridge_list) return 0; diff --git a/src/or/control.c b/src/or/control.c index ce2a4c8cfd..b5dc87b835 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -2158,7 +2158,7 @@ download_status_to_string(const download_status_t *dl) if (dl) { /* Get some substrings of the eventual output ready */ - format_iso_time(tbuf, dl->next_attempt_at); + format_iso_time(tbuf, download_status_get_next_attempt_at(dl)); switch (dl->schedule) { case DL_SCHED_GENERIC: diff --git a/src/or/directory.c b/src/or/directory.c index 89e2735e61..630524db67 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -3476,17 +3476,31 @@ connection_dir_about_to_close(dir_connection_t *dir_conn) * <b>status</b> and <b>reason_phrase</b>. Write it to <b>conn</b>. */ static void -write_http_status_line(dir_connection_t *conn, int status, +write_short_http_response(dir_connection_t *conn, int status, const char *reason_phrase) { - char buf[256]; - if (tor_snprintf(buf, sizeof(buf), "HTTP/1.0 %d %s\r\n\r\n", - status, reason_phrase ? reason_phrase : "OK") < 0) { - log_warn(LD_BUG,"status line too long."); - return; + char *buf = NULL; + char *datestring = NULL; + + IF_BUG_ONCE(!reason_phrase) { /* bullet-proofing */ + reason_phrase = "unspecified"; + } + + if (server_mode(get_options())) { + /* include the Date: header, but only if we're a relay or bridge */ + char datebuf[RFC1123_TIME_LEN+1]; + format_rfc1123_time(datebuf, time(NULL)); + tor_asprintf(&datestring, "Date: %s\r\n", datebuf); } + + tor_asprintf(&buf, "HTTP/1.0 %d %s\r\n%s\r\n", + status, reason_phrase, datestring?datestring:""); + log_debug(LD_DIRSERV,"Wrote status 'HTTP/1.0 %d %s'", status, reason_phrase); connection_buf_add(buf, strlen(buf), TO_CONN(conn)); + + tor_free(datestring); + tor_free(buf); } /** Write the header for an HTTP/1.0 response onto <b>conn</b>-\>outbuf, @@ -3835,7 +3849,7 @@ directory_handle_command_get,(dir_connection_t *conn, const char *headers, conn->base_.state = DIR_CONN_STATE_SERVER_WRITING; if (parse_http_url(headers, &url) < 0) { - write_http_status_line(conn, 400, "Bad request"); + write_short_http_response(conn, 400, "Bad request"); return 0; } if ((header = http_get_header(headers, "If-Modified-Since: "))) { @@ -3896,7 +3910,7 @@ directory_handle_command_get,(dir_connection_t *conn, const char *headers, } /* we didn't recognize the url */ - write_http_status_line(conn, 404, "Not found"); + write_short_http_response(conn, 404, "Not found"); result = 0; done: @@ -3924,7 +3938,7 @@ handle_get_frontpage(dir_connection_t *conn, const get_handler_args_t *args) NULL, DIRPORTFRONTPAGE_CACHE_LIFETIME); connection_buf_add(frontpage, dlen, TO_CONN(conn)); } else { - write_http_status_line(conn, 404, "Not found"); + write_short_http_response(conn, 404, "Not found"); } return 0; } @@ -4306,13 +4320,13 @@ handle_get_current_consensus(dir_connection_t *conn, parsed_consensus_request_t req; if (parse_consensus_request(&req, args) < 0) { - write_http_status_line(conn, 404, "Couldn't parse request"); + write_short_http_response(conn, 404, "Couldn't parse request"); goto done; } if (digest_list_contains_best_consensus(req.flav, req.diff_from_digests)) { - write_http_status_line(conn, 304, "Not modified"); + write_short_http_response(conn, 304, "Not modified"); geoip_note_ns_response(GEOIP_REJECT_NOT_MODIFIED); goto done; } @@ -4327,7 +4341,7 @@ handle_get_current_consensus(dir_connection_t *conn, } if (req.diff_only && !cached_consensus) { - write_http_status_line(conn, 404, "No such diff available"); + write_short_http_response(conn, 404, "No such diff available"); // XXXX warn_consensus_is_too_old(v, req.flavor, now); geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND); goto done; @@ -4350,7 +4364,7 @@ handle_get_current_consensus(dir_connection_t *conn, if (cached_consensus && have_valid_until && !networkstatus_valid_until_is_reasonably_live(valid_until, now)) { - write_http_status_line(conn, 404, "Consensus is too old"); + write_short_http_response(conn, 404, "Consensus is too old"); warn_consensus_is_too_old(cached_consensus, req.flavor, now); geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND); goto done; @@ -4358,7 +4372,7 @@ handle_get_current_consensus(dir_connection_t *conn, if (cached_consensus && req.want_fps && !client_likes_consensus(cached_consensus, req.want_fps)) { - write_http_status_line(conn, 404, "Consensus not signed by sufficient " + write_short_http_response(conn, 404, "Consensus not signed by sufficient " "number of requested authorities"); geoip_note_ns_response(GEOIP_REJECT_NOT_ENOUGH_SIGS); goto done; @@ -4384,11 +4398,11 @@ handle_get_current_consensus(dir_connection_t *conn, &n_expired); if (!smartlist_len(conn->spool) && !n_expired) { - write_http_status_line(conn, 404, "Not found"); + write_short_http_response(conn, 404, "Not found"); geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND); goto done; } else if (!smartlist_len(conn->spool)) { - write_http_status_line(conn, 304, "Not modified"); + write_short_http_response(conn, 304, "Not modified"); geoip_note_ns_response(GEOIP_REJECT_NOT_MODIFIED); goto done; } @@ -4397,7 +4411,7 @@ handle_get_current_consensus(dir_connection_t *conn, log_debug(LD_DIRSERV, "Client asked for network status lists, but we've been " "writing too many bytes lately. Sending 503 Dir busy."); - write_http_status_line(conn, 503, "Directory busy, try again later"); + write_short_http_response(conn, 503, "Directory busy, try again later"); geoip_note_ns_response(GEOIP_REJECT_BUSY); goto done; } @@ -4510,7 +4524,7 @@ handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args) smartlist_free(fps); } if (!smartlist_len(dir_items) && !smartlist_len(items)) { - write_http_status_line(conn, 404, "Not found"); + write_short_http_response(conn, 404, "Not found"); goto vote_done; } @@ -4547,7 +4561,7 @@ handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args) }); if (global_write_bucket_low(TO_CONN(conn), estimated_len, 2)) { - write_http_status_line(conn, 503, "Directory busy, try again later"); + write_short_http_response(conn, 503, "Directory busy, try again later"); goto vote_done; } write_http_response_header(conn, body_len ? body_len : -1, @@ -4604,14 +4618,14 @@ handle_get_microdesc(dir_connection_t *conn, const get_handler_args_t *args) compress_method != NO_METHOD, &size_guess, NULL); if (smartlist_len(conn->spool) == 0) { - write_http_status_line(conn, 404, "Not found"); + write_short_http_response(conn, 404, "Not found"); goto done; } if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) { log_info(LD_DIRSERV, "Client asked for server descriptors, but we've been " "writing too many bytes lately. Sending 503 Dir busy."); - write_http_status_line(conn, 503, "Directory busy, try again later"); + write_short_http_response(conn, 503, "Directory busy, try again later"); goto done; } @@ -4703,13 +4717,14 @@ handle_get_descriptor(dir_connection_t *conn, const get_handler_args_t *args) if (res < 0 || size_guess == 0 || smartlist_len(conn->spool) == 0) { if (msg == NULL) msg = "Not found"; - write_http_status_line(conn, 404, msg); + write_short_http_response(conn, 404, msg); } else { if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) { log_info(LD_DIRSERV, "Client asked for server descriptors, but we've been " "writing too many bytes lately. Sending 503 Dir busy."); - write_http_status_line(conn, 503, "Directory busy, try again later"); + write_short_http_response(conn, 503, + "Directory busy, try again later"); dir_conn_clear_spool(conn); goto done; } @@ -4782,18 +4797,18 @@ handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args) }); smartlist_free(fp_sks); } else { - write_http_status_line(conn, 400, "Bad request"); + write_short_http_response(conn, 400, "Bad request"); goto keys_done; } if (!smartlist_len(certs)) { - write_http_status_line(conn, 404, "Not found"); + write_short_http_response(conn, 404, "Not found"); goto keys_done; } SMARTLIST_FOREACH(certs, authority_cert_t *, c, if (c->cache_info.published_on < if_modified_since) SMARTLIST_DEL_CURRENT(certs, c)); if (!smartlist_len(certs)) { - write_http_status_line(conn, 304, "Not modified"); + write_short_http_response(conn, 304, "Not modified"); goto keys_done; } len = 0; @@ -4803,7 +4818,7 @@ handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args) if (global_write_bucket_low(TO_CONN(conn), compress_method != NO_METHOD ? len/2 : len, 2)) { - write_http_status_line(conn, 503, "Directory busy, try again later"); + write_short_http_response(conn, 503, "Directory busy, try again later"); goto keys_done; } @@ -4854,19 +4869,19 @@ handle_get_hs_descriptor_v2(dir_connection_t *conn, connection_buf_add(descp, strlen(descp), TO_CONN(conn)); break; case 0: /* well-formed but not present */ - write_http_status_line(conn, 404, "Not found"); + write_short_http_response(conn, 404, "Not found"); break; case -1: /* not well-formed */ - write_http_status_line(conn, 400, "Bad request"); + write_short_http_response(conn, 400, "Bad request"); break; } } else { /* not well-formed */ - write_http_status_line(conn, 400, "Bad request"); + write_short_http_response(conn, 400, "Bad request"); } goto done; } else { /* Not encrypted! */ - write_http_status_line(conn, 404, "Not found"); + write_short_http_response(conn, 404, "Not found"); } done: return 0; @@ -4885,7 +4900,7 @@ handle_get_hs_descriptor_v3(dir_connection_t *conn, /* Reject unencrypted dir connections */ if (!connection_dir_is_encrypted(conn)) { - write_http_status_line(conn, 404, "Not found"); + write_short_http_response(conn, 404, "Not found"); goto done; } @@ -4897,7 +4912,7 @@ handle_get_hs_descriptor_v3(dir_connection_t *conn, retval = hs_cache_lookup_as_dir(HS_VERSION_THREE, pubkey_str, &desc_str); if (retval <= 0 || desc_str == NULL) { - write_http_status_line(conn, 404, "Not found"); + write_short_http_response(conn, 404, "Not found"); goto done; } @@ -4932,7 +4947,7 @@ handle_get_networkstatus_bridges(dir_connection_t *conn, if (!header || tor_memneq(digest, options->BridgePassword_AuthDigest_, DIGEST256_LEN)) { - write_http_status_line(conn, 404, "Not found"); + write_short_http_response(conn, 404, "Not found"); tor_free(header); goto done; } @@ -5067,12 +5082,12 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers, if (!public_server_mode(options)) { log_info(LD_DIR, "Rejected dir post request from %s " "since we're not a public relay.", conn->base_.address); - write_http_status_line(conn, 503, "Not acting as a public relay"); + write_short_http_response(conn, 503, "Not acting as a public relay"); goto done; } if (parse_http_url(headers, &url) < 0) { - write_http_status_line(conn, 400, "Bad request"); + write_short_http_response(conn, 400, "Bad request"); return 0; } log_debug(LD_DIRSERV,"rewritten url as '%s'.", escaped(url)); @@ -5083,10 +5098,10 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers, if (rend_cache_store_v2_desc_as_dir(body) < 0) { log_warn(LD_REND, "Rejected v2 rend descriptor (body size %d) from %s.", (int)body_len, conn->base_.address); - write_http_status_line(conn, 400, + write_short_http_response(conn, 400, "Invalid v2 service descriptor rejected"); } else { - write_http_status_line(conn, 200, "Service descriptor (v2) stored"); + write_short_http_response(conn, 200, "Service descriptor (v2) stored"); log_info(LD_REND, "Handled v2 rendezvous descriptor post: accepted"); } goto done; @@ -5103,14 +5118,14 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers, if (code != 200) { msg = "Invalid HS descriptor. Rejected."; } - write_http_status_line(conn, code, msg); + write_short_http_response(conn, code, msg); goto done; } if (!authdir_mode(options)) { /* we just provide cached directories; we don't want to * receive anything. */ - write_http_status_line(conn, 400, "Nonauthoritative directory does not " + write_short_http_response(conn, 400, "Nonauthoritative directory does not " "accept posted server descriptors"); goto done; } @@ -5125,7 +5140,7 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers, tor_assert(msg); if (r == ROUTER_ADDED_SUCCESSFULLY) { - write_http_status_line(conn, 200, msg); + write_short_http_response(conn, 200, msg); } else if (WRA_WAS_OUTDATED(r)) { write_http_response_header_impl(conn, -1, NULL, NULL, "X-Descriptor-Not-New: Yes\r\n", -1); @@ -5134,7 +5149,7 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers, "Rejected router descriptor or extra-info from %s " "(\"%s\").", conn->base_.address, msg); - write_http_status_line(conn, 400, msg); + write_short_http_response(conn, 400, msg); } goto done; } @@ -5144,12 +5159,12 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers, const char *msg = "OK"; int status; if (dirvote_add_vote(body, &msg, &status)) { - write_http_status_line(conn, status, "Vote stored"); + write_short_http_response(conn, status, "Vote stored"); } else { tor_assert(msg); log_warn(LD_DIRSERV, "Rejected vote from %s (\"%s\").", conn->base_.address, msg); - write_http_status_line(conn, status, msg); + write_short_http_response(conn, status, msg); } goto done; } @@ -5158,17 +5173,18 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers, !strcmp(url,"/tor/post/consensus-signature")) { /* sigs on consensus. */ const char *msg = NULL; if (dirvote_add_signatures(body, conn->base_.address, &msg)>=0) { - write_http_status_line(conn, 200, msg?msg:"Signatures stored"); + write_short_http_response(conn, 200, msg?msg:"Signatures stored"); } else { log_warn(LD_DIR, "Unable to store signatures posted by %s: %s", conn->base_.address, msg?msg:"???"); - write_http_status_line(conn, 400, msg?msg:"Unable to store signatures"); + write_short_http_response(conn, 400, + msg?msg:"Unable to store signatures"); } goto done; } /* we didn't recognize the url */ - write_http_status_line(conn, 404, "Not found"); + write_short_http_response(conn, 404, "Not found"); done: tor_free(url); @@ -5363,7 +5379,7 @@ find_dl_schedule(const download_status_t *dls, const or_options_t *options) } case DL_SCHED_BRIDGE: /* A bridge client downloading bridge descriptors */ - if (any_bridge_descriptors_known()) { + if (options->UseBridges && any_bridge_descriptors_known()) { /* A bridge client with one or more running bridges */ return options->TestingBridgeDownloadSchedule; } else { diff --git a/src/or/hs_client.c b/src/or/hs_client.c index be5ece068c..77fbf548ed 100644 --- a/src/or/hs_client.c +++ b/src/or/hs_client.c @@ -521,7 +521,7 @@ client_rendezvous_circ_has_opened(origin_circuit_t *circ) * to a newly allocated extend_info_t object fully initialized. Return NULL if * we can't convert it for which chances are that we are missing or malformed * link specifiers. */ -static extend_info_t * +STATIC extend_info_t * desc_intro_point_to_extend_info(const hs_desc_intro_point_t *ip) { extend_info_t *ei; @@ -594,7 +594,7 @@ intro_point_is_usable(const ed25519_public_key_t *service_pk, /* Using a descriptor desc, return a newly allocated extend_info_t object of a * randomly picked introduction point from its list. Return NULL if none are * usable. */ -static extend_info_t * +STATIC extend_info_t * client_get_random_intro(const ed25519_public_key_t *service_pk) { extend_info_t *ei = NULL, *ei_excluded = NULL; @@ -646,6 +646,12 @@ client_get_random_intro(const ed25519_public_key_t *service_pk) /* If this pick is in the ExcludeNodes list, we keep its reference so if * we ever end up not being able to pick anything else and StrictNodes is * unset, we'll use it. */ + if (ei_excluded) { + /* If something was already here free it. After the loop is gone we + * will examine the last excluded intro point, and that's fine since + * that's random anyway */ + extend_info_free(ei_excluded); + } ei_excluded = ei; continue; } @@ -662,6 +668,7 @@ client_get_random_intro(const ed25519_public_key_t *service_pk) if (options->StrictNodes) { log_warn(LD_REND, "Every introduction points are in the ExcludeNodes set " "and StrictNodes is set. We can't connect."); + extend_info_free(ei); ei = NULL; } diff --git a/src/or/hs_client.h b/src/or/hs_client.h index 3ea2b8cdf9..d50d346217 100644 --- a/src/or/hs_client.h +++ b/src/or/hs_client.h @@ -71,7 +71,13 @@ void hs_client_free_all(void); STATIC routerstatus_t * pick_hsdir_v3(const ed25519_public_key_t *onion_identity_pk); -#endif +STATIC extend_info_t * +client_get_random_intro(const ed25519_public_key_t *service_pk); + +STATIC extend_info_t * +desc_intro_point_to_extend_info(const hs_desc_intro_point_t *ip); + +#endif /* HS_CLIENT_PRIVATE */ #endif /* TOR_HS_CLIENT_H */ diff --git a/src/or/main.c b/src/or/main.c index c987ddc61b..117857e543 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -3785,7 +3785,6 @@ tor_main(int argc, char *argv[]) case CMD_KEY_EXPIRATION: init_keys(); result = log_cert_expiration(); - result = 0; break; case CMD_LIST_FINGERPRINT: result = do_list_fingerprint(); diff --git a/src/test/hs_indexes.py b/src/test/hs_indexes.py new file mode 100644 index 0000000000..af0b81f8de --- /dev/null +++ b/src/test/hs_indexes.py @@ -0,0 +1,70 @@ +# +# The hidden service subsystem has two type of index. The first type is a +# value that each node in the network gets assigned to using their identity +# key which is their position in the hashring. (hs_build_hsdir_index()). +# +# The second type is a value that both the client and service computes to +# store/fetch the descriptor on the hashring. (hs_build_hs_index()). +# + +import sys +import hashlib +import struct +import base64 + +# Python 3.6+, the SHA3 is available in hashlib natively. Else this requires +# the pysha3 package (pip install pysha3). +if sys.version_info < (3, 6): + import sha3 + # Test vector to make sure the right sha3 version will be used. pysha3 < 1.0 + # used the old Keccak implementation. During the finalization of SHA3, NIST + # changed the delimiter suffix from 0x01 to 0x06. The Keccak sponge function + # stayed the same. pysha3 1.0 provides the previous Keccak hash, too. + TEST_VALUE = "e167f68d6563d75bb25f3aa49c29ef612d41352dc00606de7cbd630bb2665f51" + if TEST_VALUE != sha3.sha3_256(b"Hello World").hexdigest(): + print("pysha3 version is < 1.0. Please install from:") + print("https://github.com/tiran/pysha3https://github.com/tiran/pysha3") + sys.exit(1) + +# The first index we'll build is the position index in the hashring that is +# constructed by the hs_build_hsdir_index() function. Construction is: +# SHA3-256("node-idx" | node_identity | +# shared_random_value | INT_8(period_length) | INT_8(period_num) ) + +PREFIX = "node-idx".encode() +# 32 bytes ed25519 pubkey. +IDENTITY = ("\x42" * 32).encode() +# SRV is 32 bytes. +SRV = ("\x43" * 32).encode() +# Time period length is a 8 bytes value. +PERIOD_LEN = 1440 +# Period number is a 8 bytes value. +PERIOD_NUM = 42 + +data = struct.pack('!8s32s32sQQ', PREFIX, IDENTITY, SRV, PERIOD_NUM, + PERIOD_LEN) +hsdir_index = hashlib.sha3_256(data).hexdigest() + +print("[hs_build_hsdir_index] %s" % (hsdir_index)) + +# The second index we'll build is where the HS stores and the client fetches +# the descriptor on the hashring. It is constructed by the hs_build_hs_index() +# function and the construction is: +# SHA3-256("store-at-idx" | blinded_public_key | +# INT_8(replicanum) | INT_8(period_num) | INT_8(period_length) ) + +PREFIX = "store-at-idx".encode() +# 32 bytes ed25519 pubkey. +PUBKEY = ("\x42" * 32).encode() +# Replica number is a 8 bytes value. +REPLICA_NUM = 1 +# Time period length is a 8 bytes value. +PERIOD_LEN = 1440 +# Period number is a 8 bytes value. +PERIOD_NUM = 42 + +data = struct.pack('!12s32sQQQ', PREFIX, PUBKEY, REPLICA_NUM, PERIOD_LEN, + PERIOD_NUM) +hs_index = hashlib.sha3_256(data).hexdigest() + +print("[hs_build_hs_index] %s" % (hs_index)) diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c index cab73196b9..9355971267 100644 --- a/src/test/hs_test_helpers.c +++ b/src/test/hs_test_helpers.c @@ -18,28 +18,29 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now, hs_desc_intro_point_t *intro_point = NULL; hs_desc_intro_point_t *ip = hs_desc_intro_point_new(); + /* For a usable intro point we need at least two link specifiers: One legacy + * keyid and one ipv4 */ { - hs_desc_link_specifier_t *ls = tor_malloc_zero(sizeof(*ls)); - if (legacy) { - ls->type = LS_LEGACY_ID; - memcpy(ls->u.legacy_id, "0299F268FCA9D55CD157976D39AE92B4B455B3A8", - DIGEST_LEN); - } else { - ls->u.ap.port = 9001; - int family = tor_addr_parse(&ls->u.ap.addr, addr); - switch (family) { - case AF_INET: - ls->type = LS_IPV4; + hs_desc_link_specifier_t *ls_legacy = tor_malloc_zero(sizeof(*ls_legacy)); + hs_desc_link_specifier_t *ls_v4 = tor_malloc_zero(sizeof(*ls_v4)); + ls_legacy->type = LS_LEGACY_ID; + memcpy(ls_legacy->u.legacy_id, "0299F268FCA9D55CD157976D39AE92B4B455B3A8", + DIGEST_LEN); + ls_v4->u.ap.port = 9001; + int family = tor_addr_parse(&ls_v4->u.ap.addr, addr); + switch (family) { + case AF_INET: + ls_v4->type = LS_IPV4; break; case AF_INET6: - ls->type = LS_IPV6; + ls_v4->type = LS_IPV6; break; default: /* Stop the test, not suppose to have an error. */ tt_int_op(family, OP_EQ, AF_INET); - } } - smartlist_add(ip->link_specifiers, ls); + smartlist_add(ip->link_specifiers, ls_legacy); + smartlist_add(ip->link_specifiers, ls_v4); } ret = ed25519_keypair_generate(&auth_kp, 0); @@ -137,7 +138,7 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip, smartlist_add(desc->encrypted_data.intro_points, hs_helper_build_intro_point(signing_kp, now, "3.2.1.4", 1)); smartlist_add(desc->encrypted_data.intro_points, - hs_helper_build_intro_point(signing_kp, now, "", 1)); + hs_helper_build_intro_point(signing_kp, now, "5.6.7.8", 1)); } descp = desc; diff --git a/src/test/include.am b/src/test/include.am index ced16c0a8d..8e8c9ca0da 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -332,6 +332,8 @@ EXTRA_DIST += \ src/test/bt_test.py \ src/test/ntor_ref.py \ src/test/hs_ntor_ref.py \ + src/test/hs_build_address.py \ + src/test/hs_indexes.py \ src/test/fuzz_static_testcases.sh \ src/test/slownacl_curve25519.py \ src/test/zero_length_keys.sh \ diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c index af5f5cb576..16b2d604fd 100644 --- a/src/test/test_hs_client.c +++ b/src/test/test_hs_client.c @@ -8,6 +8,7 @@ #define CRYPTO_PRIVATE #define MAIN_PRIVATE +#define HS_CLIENT_PRIVATE #define TOR_CHANNEL_INTERNAL_ #define CIRCUITBUILD_PRIVATE #define CIRCUITLIST_PRIVATE @@ -17,17 +18,22 @@ #include "test_helpers.h" #include "log_test_helpers.h" #include "rend_test_helpers.h" +#include "hs_test_helpers.h" #include "config.h" #include "crypto.h" #include "channeltls.h" +#include "routerset.h" #include "hs_circuit.h" +#include "hs_client.h" #include "hs_ident.h" +#include "hs_cache.h" #include "circuitlist.h" #include "circuitbuild.h" #include "connection.h" #include "connection_edge.h" +#include "networkstatus.h" static int mock_connection_ap_handshake_send_begin(entry_connection_t *ap_conn) @@ -36,6 +42,15 @@ mock_connection_ap_handshake_send_begin(entry_connection_t *ap_conn) return 0; } +static networkstatus_t mock_ns; + +static networkstatus_t * +mock_networkstatus_get_live_consensus(time_t now) +{ + (void) now; + return &mock_ns; +} + /* Test helper function: Setup a circuit and a stream with the same hidden * service destination, and put them in <b>circ_out</b> and * <b>conn_out</b>. Make the stream wait for circuits to be established to the @@ -276,11 +291,176 @@ test_e2e_rend_circuit_setup(void *arg) circuit_free(TO_CIRCUIT(or_circ)); } +/** Test client logic for picking intro points from a descriptor. Also test how + * ExcludeNodes and intro point failures affect picking intro points. */ +static void +test_client_pick_intro(void *arg) +{ + int ret; + ed25519_keypair_t service_kp; + hs_descriptor_t *desc = NULL; + + MOCK(networkstatus_get_live_consensus, + mock_networkstatus_get_live_consensus); + + (void) arg; + + hs_init(); + + /* Generate service keypair */ + tt_int_op(0, OP_EQ, ed25519_keypair_generate(&service_kp, 0)); + + /* Set time */ + ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC", + &mock_ns.valid_after); + tt_int_op(ret, OP_EQ, 0); + ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC", + &mock_ns.fresh_until); + tt_int_op(ret, OP_EQ, 0); + + update_approx_time(mock_ns.fresh_until-10); + time_t now = approx_time(); + + /* Test logic: + * + * 1) Add our desc with intro points to the HS cache. + * + * 2) Mark all descriptor intro points except _the chosen one_ as + * failed. Then query the desc to get a random intro: check that we got + * _the chosen one_. Then fail the chosen one as well, and see that no + * intros are returned. + * + * 3) Then clean the intro state cache and get an intro point. + * + * 4) Try fetching an intro with the wrong service key: shouldn't work + * + * 5) Set StrictNodes and put all our intro points in ExcludeNodes: see that + * nothing is returned. + */ + + /* 1) Add desc to HS cache */ + { + char *encoded = NULL; + desc = hs_helper_build_hs_desc_with_ip(&service_kp); + ret = hs_desc_encode_descriptor(desc, &service_kp, &encoded); + tt_int_op(ret, OP_EQ, 0); + tt_assert(encoded); + + /* store it */ + hs_cache_store_as_client(encoded, &service_kp.pubkey); + + /* fetch it to make sure it works */ + const hs_descriptor_t *fetched_desc = + hs_cache_lookup_as_client(&service_kp.pubkey); + tt_assert(fetched_desc); + tt_mem_op(fetched_desc->subcredential, OP_EQ, desc->subcredential, + DIGEST256_LEN); + tt_assert(!tor_mem_is_zero((char*)fetched_desc->subcredential, + DIGEST256_LEN)); + tor_free(encoded); + } + + /* 2) Mark all intro points except _the chosen one_ as failed. Then query the + * desc and get a random intro: check that we got _the chosen one_. */ + { + /* Pick the chosen intro point and get its ei */ + hs_desc_intro_point_t *chosen_intro_point = + smartlist_get(desc->encrypted_data.intro_points, 0); + extend_info_t *chosen_intro_ei = + desc_intro_point_to_extend_info(chosen_intro_point); + tt_assert(chosen_intro_point); + tt_assert(chosen_intro_ei); + + /* Now mark all other intro points as failed */ + SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points, + hs_desc_intro_point_t *, ip) { + /* Skip the chosen intro point */ + if (ip == chosen_intro_point) { + continue; + } + ed25519_public_key_t *intro_auth_key = &ip->auth_key_cert->signed_key; + hs_cache_client_intro_state_note(&service_kp.pubkey, + intro_auth_key, + INTRO_POINT_FAILURE_GENERIC); + } SMARTLIST_FOREACH_END(ip); + + /* Try to get a random intro: Should return the chosen one! */ + extend_info_t *ip = client_get_random_intro(&service_kp.pubkey); + tor_assert(ip); + tt_assert(!tor_mem_is_zero((char*)ip->identity_digest, DIGEST_LEN)); + tt_mem_op(ip->identity_digest, OP_EQ, chosen_intro_ei->identity_digest, + DIGEST_LEN); + + extend_info_free(chosen_intro_ei); + extend_info_free(ip); + + /* Now also mark the chosen one as failed: See that we can't get any intro + points anymore. */ + hs_cache_client_intro_state_note(&service_kp.pubkey, + &chosen_intro_point->auth_key_cert->signed_key, + INTRO_POINT_FAILURE_TIMEOUT); + ip = client_get_random_intro(&service_kp.pubkey); + tor_assert(!ip); + } + + /* 3) Clean the intro state cache and get an intro point */ + { + /* Pretend we are 5 mins in the future and order a cleanup of the intro + * state. This should clean up the intro point failures and allow us to get + * an intro. */ + hs_cache_client_intro_state_clean(now + 5*60); + + /* Get an intro. It should work! */ + extend_info_t *ip = client_get_random_intro(&service_kp.pubkey); + tor_assert(ip); + extend_info_free(ip); + } + + /* 4) Try fetching an intro with the wrong service key: shouldn't work */ + { + ed25519_keypair_t dummy_kp; + tt_int_op(0, OP_EQ, ed25519_keypair_generate(&dummy_kp, 0)); + extend_info_t *ip = client_get_random_intro(&dummy_kp.pubkey); + tor_assert(!ip); + } + + /* 5) Set StrictNodes and put all our intro points in ExcludeNodes: see that + * nothing is returned. */ + { + get_options_mutable()->ExcludeNodes = routerset_new(); + get_options_mutable()->StrictNodes = 1; + SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points, + hs_desc_intro_point_t *, ip) { + extend_info_t *intro_ei = desc_intro_point_to_extend_info(ip); + if (intro_ei) { + const char *ptr; + char ip_addr[TOR_ADDR_BUF_LEN]; + /* We need to decorate in case it is an IPv6 else routerset_parse() + * doesn't like it. */ + ptr = tor_addr_to_str(ip_addr, &intro_ei->addr, sizeof(ip_addr), 1); + tt_assert(ptr == ip_addr); + ret = routerset_parse(get_options_mutable()->ExcludeNodes, + ip_addr, ""); + tt_int_op(ret, OP_EQ, 0); + extend_info_free(intro_ei); + } + } SMARTLIST_FOREACH_END(ip); + + extend_info_t *ip = client_get_random_intro(&service_kp.pubkey); + tt_assert(!ip); + } + + done: + hs_descriptor_free(desc); +} + struct testcase_t hs_client_tests[] = { { "e2e_rend_circuit_setup_legacy", test_e2e_rend_circuit_setup_legacy, TT_FORK, NULL, NULL }, { "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, TT_FORK, NULL, NULL }, + { "client_pick_intro", test_client_pick_intro, + TT_FORK, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c index dc146326b8..2d63dff250 100644 --- a/src/test/test_hs_common.c +++ b/src/test/test_hs_common.c @@ -1453,31 +1453,102 @@ helper_client_pick_hsdir(const ed25519_public_key_t *onion_identity_pk, ; } -/** Set the consensus and system time based on <b>between_srv_and_tp</b>. If - * <b>between_srv_and_tp</b> is set, then set the time to be inside the time - * segment between SRV#N and TP#N. */ +static void +test_hs_indexes(void *arg) +{ + int ret; + uint64_t period_num = 42; + ed25519_public_key_t pubkey; + + (void) arg; + + /* Build the hs_index */ + { + uint8_t hs_index[DIGEST256_LEN]; + const char *b32_test_vector = + "37e5cbbd56a22823714f18f1623ece5983a0d64c78495a8cfab854245e5f9a8a"; + char test_vector[DIGEST256_LEN]; + ret = base16_decode(test_vector, sizeof(test_vector), b32_test_vector, + strlen(b32_test_vector)); + tt_int_op(ret, OP_EQ, sizeof(test_vector)); + /* Our test vector uses a public key set to 32 bytes of \x42. */ + memset(&pubkey, '\x42', sizeof(pubkey)); + hs_build_hs_index(1, &pubkey, period_num, hs_index); + tt_mem_op(hs_index, OP_EQ, test_vector, sizeof(hs_index)); + } + + /* Build the hsdir_index */ + { + uint8_t srv[DIGEST256_LEN]; + uint8_t hsdir_index[DIGEST256_LEN]; + const char *b32_test_vector = + "db475361014a09965e7e5e4d4a25b8f8d4b8f16cb1d8a7e95eed50249cc1a2d5"; + char test_vector[DIGEST256_LEN]; + ret = base16_decode(test_vector, sizeof(test_vector), b32_test_vector, + strlen(b32_test_vector)); + tt_int_op(ret, OP_EQ, sizeof(test_vector)); + /* Our test vector uses a public key set to 32 bytes of \x42. */ + memset(&pubkey, '\x42', sizeof(pubkey)); + memset(srv, '\x43', sizeof(srv)); + hs_build_hsdir_index(&pubkey, srv, period_num, hsdir_index); + tt_mem_op(hsdir_index, OP_EQ, test_vector, sizeof(hsdir_index)); + } + + done: + ; +} + +#define EARLY_IN_SRV_TO_TP 0 +#define LATE_IN_SRV_TO_TP 1 +#define EARLY_IN_TP_TO_SRV 2 +#define LATE_IN_TP_TO_SRV 3 + +/** Set the consensus and system time based on <b>position</b>. See the + * following diagram for details: + * + * +------------------------------------------------------------------+ + * | | + * | 00:00 12:00 00:00 12:00 00:00 12:00 | + * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 | + * | | + * | $==========|-----------$===========|----------$===========| | + * | | + * | | + * +------------------------------------------------------------------+ + */ static time_t -helper_set_consensus_and_system_time(networkstatus_t *ns, - int between_srv_and_tp) +helper_set_consensus_and_system_time(networkstatus_t *ns, int position) { - time_t real_time; + time_t real_time = 0; /* The period between SRV#N and TP#N is from 00:00 to 12:00 UTC. Consensus * valid_after is what matters here, the rest is just to specify the voting * period correctly. */ - if (between_srv_and_tp) { + if (position == LATE_IN_SRV_TO_TP) { parse_rfc1123_time("Wed, 13 Apr 2016 11:00:00 UTC", &ns->valid_after); parse_rfc1123_time("Wed, 13 Apr 2016 12:00:00 UTC", &ns->fresh_until); parse_rfc1123_time("Wed, 13 Apr 2016 14:00:00 UTC", &ns->valid_until); - } else { + } else if (position == EARLY_IN_TP_TO_SRV) { parse_rfc1123_time("Wed, 13 Apr 2016 13:00:00 UTC", &ns->valid_after); parse_rfc1123_time("Wed, 13 Apr 2016 14:00:00 UTC", &ns->fresh_until); parse_rfc1123_time("Wed, 13 Apr 2016 16:00:00 UTC", &ns->valid_until); + } else if (position == LATE_IN_TP_TO_SRV) { + parse_rfc1123_time("Wed, 13 Apr 2016 23:00:00 UTC", &ns->valid_after); + parse_rfc1123_time("Wed, 14 Apr 2016 00:00:00 UTC", &ns->fresh_until); + parse_rfc1123_time("Wed, 14 Apr 2016 02:00:00 UTC", &ns->valid_until); + } else if (position == EARLY_IN_SRV_TO_TP) { + parse_rfc1123_time("Wed, 14 Apr 2016 01:00:00 UTC", &ns->valid_after); + parse_rfc1123_time("Wed, 14 Apr 2016 02:00:00 UTC", &ns->fresh_until); + parse_rfc1123_time("Wed, 14 Apr 2016 04:00:00 UTC", &ns->valid_until); + } else { + tt_assert(0); } /* Set system time: pretend to be just 2 minutes before consensus expiry */ real_time = ns->valid_until - 120; update_approx_time(real_time); + + done: return real_time; } @@ -1485,8 +1556,7 @@ helper_set_consensus_and_system_time(networkstatus_t *ns, * test_client_service_sync() */ static void helper_test_hsdir_sync(networkstatus_t *ns, - int service_between_srv_and_tp, - int client_between_srv_and_tp, + int service_position, int client_position, int client_fetches_next_desc) { hs_service_descriptor_t *desc; @@ -1503,8 +1573,7 @@ helper_test_hsdir_sync(networkstatus_t *ns, */ /* 1) Initialize service time: consensus and real time */ - time_t now = helper_set_consensus_and_system_time(ns, - service_between_srv_and_tp); + time_t now = helper_set_consensus_and_system_time(ns, service_position); helper_initialize_big_hash_ring(ns); /* 2) Initialize service */ @@ -1519,7 +1588,7 @@ helper_test_hsdir_sync(networkstatus_t *ns, tt_int_op(smartlist_len(desc->previous_hsdirs), OP_EQ, 6); /* 3) Initialize client time */ - now = helper_set_consensus_and_system_time(ns, client_between_srv_and_tp); + now = helper_set_consensus_and_system_time(ns, client_position); cleanup_nodelist(); SMARTLIST_FOREACH(ns->routerstatus_list, @@ -1527,22 +1596,28 @@ helper_test_hsdir_sync(networkstatus_t *ns, smartlist_clear(ns->routerstatus_list); helper_initialize_big_hash_ring(ns); - /* 4) Fetch desc as client */ - char client_hsdir_b64_digest[BASE64_DIGEST_LEN+1] = {0}; - helper_client_pick_hsdir(&service->keys.identity_pk, - client_hsdir_b64_digest); - /* Cleanup right now so we don't memleak on error. */ - cleanup_nodelist(); + /* 4) Pick 6 HSDirs as a client and check that they were also chosen by the + service. */ + for (int y = 0 ; y < 6 ; y++) { + char client_hsdir_b64_digest[BASE64_DIGEST_LEN+1] = {0}; + helper_client_pick_hsdir(&service->keys.identity_pk, + client_hsdir_b64_digest); + + /* CHECK: Go through the hsdirs chosen by the service and make sure that it + * contains the one picked by the client! */ + retval = smartlist_contains_string(desc->previous_hsdirs, + client_hsdir_b64_digest); + tt_int_op(retval, OP_EQ, 1); + } - /* CHECK: Go through the hsdirs chosen by the service and make sure that it - * contains the one picked by the client! */ - retval = smartlist_contains_string(desc->previous_hsdirs, - client_hsdir_b64_digest); - tt_int_op(retval, OP_EQ, 1); + /* Finally, try to pick a 7th hsdir and see that NULL is returned since we + * exhausted all of them: */ + tt_assert(!pick_hsdir_v3(&service->keys.identity_pk)); done: /* At the end: free all services and initialize the subsystem again, we will * need it for next scenario. */ + cleanup_nodelist(); hs_service_free_all(); hs_service_init(); SMARTLIST_FOREACH(ns->routerstatus_list, @@ -1606,7 +1681,7 @@ test_client_service_hsdir_set_sync(void *arg) * | S C | * +------------------------------------------------------------------+ */ - helper_test_hsdir_sync(ns, 1, 1, 0); + helper_test_hsdir_sync(ns, LATE_IN_SRV_TO_TP, LATE_IN_SRV_TO_TP, 0); /* b) Scenario where both client and service are in the time segment between * TP#N and SRV#N+1. At this time the client fetches the second HS @@ -1622,7 +1697,7 @@ test_client_service_hsdir_set_sync(void *arg) * | S C | * +------------------------------------------------------------------+ */ - helper_test_hsdir_sync(ns, 0, 0, 1); + helper_test_hsdir_sync(ns, LATE_IN_TP_TO_SRV, LATE_IN_TP_TO_SRV, 1); /* c) Scenario where service is between SRV#N and TP#N, but client is * between TP#N and SRV#N+1. Client is forward in time so it fetches the @@ -1638,7 +1713,7 @@ test_client_service_hsdir_set_sync(void *arg) * | S C | * +------------------------------------------------------------------+ */ - helper_test_hsdir_sync(ns, 1, 0, 1); + helper_test_hsdir_sync(ns, LATE_IN_SRV_TO_TP, EARLY_IN_TP_TO_SRV, 1); /* d) Scenario where service is between TP#N and SRV#N+1, but client is * between SRV#N and TP#N. Client is backwards in time so it fetches the @@ -1654,7 +1729,39 @@ test_client_service_hsdir_set_sync(void *arg) * | C S | * +------------------------------------------------------------------+ */ - helper_test_hsdir_sync(ns, 0, 1, 0); + helper_test_hsdir_sync(ns, EARLY_IN_TP_TO_SRV, LATE_IN_SRV_TO_TP, 0); + + /* e) Scenario where service is between SRV#N and TP#N, but client is + * between TP#N-1 and SRV#3. Client is backwards in time so it fetches + * the first HS desc. + * + * +------------------------------------------------------------------+ + * | | + * | 00:00 12:00 00:00 12:00 00:00 12:00 | + * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 | + * | | + * | $==========|-----------$===========|-----------$===========| | + * | ^ ^ | + * | C S | + * +------------------------------------------------------------------+ + */ + helper_test_hsdir_sync(ns, EARLY_IN_SRV_TO_TP, LATE_IN_TP_TO_SRV, 0); + + /* f) Scenario where service is between TP#N and SRV#N+1, but client is + * between SRV#N+1 and TP#N+1. Client is forward in time so it fetches + * the second HS desc. + * + * +------------------------------------------------------------------+ + * | | + * | 00:00 12:00 00:00 12:00 00:00 12:00 | + * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 | + * | | + * | $==========|-----------$===========|-----------$===========| | + * | ^ ^ | + * | S C | + * +------------------------------------------------------------------+ + */ + helper_test_hsdir_sync(ns, LATE_IN_TP_TO_SRV, EARLY_IN_SRV_TO_TP, 1); done: networkstatus_vote_free(ns); @@ -1687,6 +1794,9 @@ struct testcase_t hs_common_tests[] = { NULL, NULL }, { "client_service_hsdir_set_sync", test_client_service_hsdir_set_sync, TT_FORK, NULL, NULL }, + { "hs_indexes", test_hs_indexes, TT_FORK, + NULL, NULL }, + END_OF_TESTCASES }; diff --git a/src/test/test_key_expiration.sh b/src/test/test_key_expiration.sh index 95d7911f04..5511dbf18c 100755 --- a/src/test/test_key_expiration.sh +++ b/src/test/test_key_expiration.sh @@ -90,7 +90,7 @@ if [ "$CASE1" = 1 ]; then echo "==== Case 1: Test --key-expiration without argument and ensure usage" echo " instructions are printed." - ${TOR} ${QUIETLY} --key-expiration 2>"$FN" + ${TOR} ${QUIETLY} --key-expiration 2>"$FN" || true grep "No valid argument to --key-expiration found!" "$FN" >/dev/null || \ die "Tor didn't mention supported --key-expiration argmuents" |